Merge git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next

Johannes Berg <johannes@sipsolutions.net> says:

"This time I have Felix's no-status rate control work, which will allow
drivers to work better with rate control even if they don't have perfect
status reporting. In addition to this, a small hwsim fix from Patrik,
one of the regulatory patches from Arik, and a number of cleanups and
fixes I did myself.

Of note is a patch where I disable CFG80211_WEXT so that compatibility
is no longer selectable - this is intended as a wake-up call for anyone
who's still using it, and is still easily worked around (it's a one-line
patch) before we fully remove the code as well in the future."

Signed-off-by: John W. Linville <linville@tuxdriver.com>
diff --git a/Documentation/devicetree/bindings/btmrvl.txt b/Documentation/devicetree/bindings/btmrvl.txt
new file mode 100644
index 0000000..58f964b
--- /dev/null
+++ b/Documentation/devicetree/bindings/btmrvl.txt
@@ -0,0 +1,29 @@
+btmrvl
+------
+
+Required properties:
+
+  - compatible : must be "btmrvl,cfgdata"
+
+Optional properties:
+
+  - btmrvl,cal-data : Calibration data downloaded to the device during
+		      initialization. This is an array of 28 values(u8).
+
+  - btmrvl,gpio-gap : gpio and gap (in msecs) combination to be
+		      configured.
+
+Example:
+
+GPIO pin 13 is configured as a wakeup source and GAP is set to 100 msecs
+in below example.
+
+btmrvl {
+	compatible = "btmrvl,cfgdata";
+
+	btmrvl,cal-data = /bits/ 8 <
+		0x37 0x01 0x1c 0x00 0xff 0xff 0xff 0xff 0x01 0x7f 0x04 0x02
+		0x00 0x00 0xba 0xce 0xc0 0xc6 0x2d 0x00 0x00 0x00 0x00 0x00
+		0x00 0x00 0xf0 0x00>;
+	btmrvl,gpio-gap = <0x0d64>;
+};
diff --git a/MAINTAINERS b/MAINTAINERS
index b42eb50..7ec37a3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4699,6 +4699,7 @@
 F:	drivers/net/ieee802154/
 F:	include/linux/nl802154.h
 F:	include/linux/ieee802154.h
+F:	include/net/nl802154.h
 F:	include/net/mac802154.h
 F:	include/net/af_ieee802154.h
 F:	include/net/cfg802154.h
diff --git a/drivers/bcma/host_pci.c b/drivers/bcma/host_pci.c
index 1e5ac0a..cd9161a 100644
--- a/drivers/bcma/host_pci.c
+++ b/drivers/bcma/host_pci.c
@@ -275,7 +275,7 @@
 static const struct pci_device_id bcma_pci_bridge_tbl[] = {
 	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x0576) },
 	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4313) },
-	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 43224) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 43224) },	/* 0xa8d8 */
 	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4331) },
 	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4353) },
 	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4357) },
@@ -285,7 +285,8 @@
 	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43a9) },
 	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43aa) },
 	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4727) },
-	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 43227) },	/* 0xA8DB */
+	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 43227) },	/* 0xa8db, BCM43217 (sic!) */
+	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 43228) },	/* 0xa8dc */
 	{ 0, },
 };
 MODULE_DEVICE_TABLE(pci, bcma_pci_bridge_tbl);
diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c
index 122086e..534e133 100644
--- a/drivers/bcma/main.c
+++ b/drivers/bcma/main.c
@@ -133,7 +133,7 @@
 	return false;
 }
 
-#ifdef CONFIG_OF
+#if defined(CONFIG_OF) && defined(CONFIG_OF_ADDRESS)
 static struct device_node *bcma_of_find_child_device(struct platform_device *parent,
 						     struct bcma_device *core)
 {
diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c
index 25c874d..fce7588 100644
--- a/drivers/bluetooth/ath3k.c
+++ b/drivers/bluetooth/ath3k.c
@@ -106,6 +106,7 @@
 	{ USB_DEVICE(0x13d3, 0x3375) },
 	{ USB_DEVICE(0x13d3, 0x3393) },
 	{ USB_DEVICE(0x13d3, 0x3402) },
+	{ USB_DEVICE(0x13d3, 0x3408) },
 	{ USB_DEVICE(0x13d3, 0x3432) },
 
 	/* Atheros AR5BBU12 with sflash firmware */
@@ -158,6 +159,7 @@
 	{ USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 },
 	{ USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 },
 	{ USB_DEVICE(0x13d3, 0x3402), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x13d3, 0x3408), .driver_info = BTUSB_ATH3012 },
 	{ USB_DEVICE(0x13d3, 0x3432), .driver_info = BTUSB_ATH3012 },
 
 	/* Atheros AR5BBU22 with sflash firmware */
diff --git a/drivers/bluetooth/btmrvl_main.c b/drivers/bluetooth/btmrvl_main.c
index 1d7db20..bb0d2c2 100644
--- a/drivers/bluetooth/btmrvl_main.c
+++ b/drivers/bluetooth/btmrvl_main.c
@@ -41,6 +41,11 @@
 
 	priv->adapter->int_count++;
 
+	if (priv->adapter->hs_state == HS_ACTIVATED) {
+		BT_DBG("BT: HS DEACTIVATED in ISR!\n");
+		priv->adapter->hs_state = HS_DEACTIVATED;
+	}
+
 	wake_up_interruptible(&priv->main_thread.wait_q);
 }
 EXPORT_SYMBOL_GPL(btmrvl_interrupt);
@@ -323,6 +328,7 @@
 		} else {
 			ret = priv->hw_wakeup_firmware(priv);
 			priv->adapter->hs_state = HS_DEACTIVATED;
+			BT_DBG("BT: HS DEACTIVATED due to host activity!\n");
 		}
 	}
 
@@ -492,29 +498,31 @@
 	return 0;
 }
 
-static int btmrvl_cal_data_dt(struct btmrvl_private *priv)
+static int btmrvl_check_device_tree(struct btmrvl_private *priv)
 {
 	struct device_node *dt_node;
 	u8 cal_data[BT_CAL_HDR_LEN + BT_CAL_DATA_SIZE];
-	const char name[] = "btmrvl_caldata";
-	const char property[] = "btmrvl,caldata";
 	int ret;
+	u32 val;
 
-	dt_node = of_find_node_by_name(NULL, name);
-	if (!dt_node)
-		return -ENODEV;
+	for_each_compatible_node(dt_node, NULL, "btmrvl,cfgdata") {
+		ret = of_property_read_u32(dt_node, "btmrvl,gpio-gap", &val);
+		if (!ret)
+			priv->btmrvl_dev.gpio_gap = val;
 
-	ret = of_property_read_u8_array(dt_node, property,
-					cal_data + BT_CAL_HDR_LEN,
-					BT_CAL_DATA_SIZE);
-	if (ret)
-		return ret;
+		ret = of_property_read_u8_array(dt_node, "btmrvl,cal-data",
+						cal_data + BT_CAL_HDR_LEN,
+						BT_CAL_DATA_SIZE);
+		if (ret)
+			return ret;
 
-	BT_DBG("Use cal data from device tree");
-	ret = btmrvl_download_cal_data(priv, cal_data, BT_CAL_DATA_SIZE);
-	if (ret) {
-		BT_ERR("Fail to download calibrate data");
-		return ret;
+		BT_DBG("Use cal data from device tree");
+		ret = btmrvl_download_cal_data(priv, cal_data,
+					       BT_CAL_DATA_SIZE);
+		if (ret) {
+			BT_ERR("Fail to download calibrate data");
+			return ret;
+		}
 	}
 
 	return 0;
@@ -526,14 +534,15 @@
 
 	btmrvl_send_module_cfg_cmd(priv, MODULE_BRINGUP_REQ);
 
-	btmrvl_cal_data_dt(priv);
+	priv->btmrvl_dev.gpio_gap = 0xffff;
+
+	btmrvl_check_device_tree(priv);
 
 	btmrvl_pscan_window_reporting(priv, 0x01);
 
 	priv->btmrvl_dev.psmode = 1;
 	btmrvl_enable_ps(priv);
 
-	priv->btmrvl_dev.gpio_gap = 0xffff;
 	btmrvl_send_hscfg_cmd(priv);
 
 	return 0;
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index cd634f3..9efdace 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -106,6 +106,8 @@
 	{ USB_DEVICE(0x0b05, 0x17b5) },
 	{ USB_DEVICE(0x0b05, 0x17cb) },
 	{ USB_DEVICE(0x413c, 0x8197) },
+	{ USB_DEVICE(0x13d3, 0x3404),
+	  .driver_info = BTUSB_BCM_PATCHRAM },
 
 	/* Foxconn - Hon Hai */
 	{ USB_VENDOR_AND_INTERFACE_INFO(0x0489, 0xff, 0x01, 0x01) },
@@ -183,6 +185,7 @@
 	{ USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 },
 	{ USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 },
 	{ USB_DEVICE(0x13d3, 0x3402), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x13d3, 0x3408), .driver_info = BTUSB_ATH3012 },
 	{ USB_DEVICE(0x13d3, 0x3432), .driver_info = BTUSB_ATH3012 },
 
 	/* Atheros AR5BBU12 with sflash firmware */
diff --git a/drivers/bluetooth/hci_ath.c b/drivers/bluetooth/hci_ath.c
index 0bc8a6a..2ff6dfd 100644
--- a/drivers/bluetooth/hci_ath.c
+++ b/drivers/bluetooth/hci_ath.c
@@ -74,7 +74,7 @@
 
 	status = tty->driver->ops->tiocmget(tty);
 
-	/* Disable Automatic RTSCTS */
+	/* Enable Automatic RTSCTS */
 	ktermios.c_cflag |= CRTSCTS;
 	status = tty_set_termios(tty, &ktermios);
 
diff --git a/drivers/net/ieee802154/at86rf230.c b/drivers/net/ieee802154/at86rf230.c
index ebcbeb3a..1c01356 100644
--- a/drivers/net/ieee802154/at86rf230.c
+++ b/drivers/net/ieee802154/at86rf230.c
@@ -46,10 +46,6 @@
 	u16 t_off_to_tx_on;
 	u16 t_frame;
 	u16 t_p_ack;
-	/* short interframe spacing time */
-	u16 t_sifs;
-	/* long interframe spacing time */
-	u16 t_lifs;
 	/* completion timeout for tx in msecs */
 	u16 t_tx_timeout;
 	int rssi_base_val;
@@ -719,19 +715,10 @@
 
 	enable_irq(lp->spi->irq);
 
-	if (lp->max_frame_retries <= 0) {
-		/* Interfame spacing time, which is phy depend.
-		 * TODO
-		 * Move this handling in MAC 802.15.4 layer.
-		 * This is currently a workaround to avoid fragmenation issues.
-		 */
-		if (skb->len > 18)
-			udelay(lp->data->t_lifs);
-		else
-			udelay(lp->data->t_sifs);
-	}
-
-	ieee802154_xmit_complete(lp->hw, skb);
+	if (lp->max_frame_retries <= 0)
+		ieee802154_xmit_complete(lp->hw, skb, true);
+	else
+		ieee802154_xmit_complete(lp->hw, skb, false);
 }
 
 static void
@@ -1038,6 +1025,36 @@
 	if (rc < 0)
 		return rc;
 
+	/* This sets the symbol_duration according frequency on the 212.
+	 * TODO move this handling while set channel and page in cfg802154.
+	 * We can do that, this timings are according 802.15.4 standard.
+	 * If we do that in cfg802154, this is a more generic calculation.
+	 *
+	 * This should also protected from ifs_timer. Means cancel timer and
+	 * init with a new value. For now, this is okay.
+	 */
+	if (channel == 0) {
+		if (page == 0) {
+			/* SUB:0 and BPSK:0 -> BPSK-20 */
+			lp->hw->phy->symbol_duration = 50;
+		} else {
+			/* SUB:1 and BPSK:0 -> BPSK-40 */
+			lp->hw->phy->symbol_duration = 25;
+		}
+	} else {
+		if (page == 0)
+			/* SUB:0 and BPSK:1 -> OQPSK-100/200/400 */
+			lp->hw->phy->symbol_duration = 40;
+		else
+			/* SUB:1 and BPSK:1 -> OQPSK-250/500/1000 */
+			lp->hw->phy->symbol_duration = 16;
+	}
+
+	lp->hw->phy->lifs_period = IEEE802154_LIFS_PERIOD *
+				   lp->hw->phy->symbol_duration;
+	lp->hw->phy->sifs_period = IEEE802154_SIFS_PERIOD *
+				   lp->hw->phy->symbol_duration;
+
 	return at86rf230_write_subreg(lp, SR_CHANNEL, channel);
 }
 
@@ -1047,23 +1064,11 @@
 	struct at86rf230_local *lp = hw->priv;
 	int rc;
 
-	if (page > 31 ||
-	    !(lp->hw->phy->channels_supported[page] & BIT(channel))) {
-		WARN_ON(1);
-		return -EINVAL;
-	}
-
 	rc = lp->data->set_channel(lp, page, channel);
-	if (rc < 0)
-		return rc;
-
 	/* Wait for PLL */
 	usleep_range(lp->data->t_channel_switch,
 		     lp->data->t_channel_switch + 10);
-	hw->phy->current_channel = channel;
-	hw->phy->current_page = page;
-
-	return 0;
+	return rc;
 }
 
 static int
@@ -1179,9 +1184,6 @@
 	struct at86rf230_local *lp = hw->priv;
 	int rc;
 
-	if (min_be > max_be || max_be > 8 || retries > 5)
-		return -EINVAL;
-
 	rc = at86rf230_write_subreg(lp, SR_MIN_BE, min_be);
 	if (rc)
 		return rc;
@@ -1199,9 +1201,6 @@
 	struct at86rf230_local *lp = hw->priv;
 	int rc = 0;
 
-	if (retries < -1 || retries > 15)
-		return -EINVAL;
-
 	lp->tx_aret = retries >= 0;
 	lp->max_frame_retries = retries;
 
@@ -1263,8 +1262,6 @@
 	.t_off_to_tx_on = 80,
 	.t_frame = 4096,
 	.t_p_ack = 545,
-	.t_sifs = 192,
-	.t_lifs = 640,
 	.t_tx_timeout = 2000,
 	.rssi_base_val = -91,
 	.set_channel = at86rf23x_set_channel,
@@ -1279,8 +1276,6 @@
 	.t_off_to_tx_on = 110,
 	.t_frame = 4096,
 	.t_p_ack = 545,
-	.t_sifs = 192,
-	.t_lifs = 640,
 	.t_tx_timeout = 2000,
 	.rssi_base_val = -91,
 	.set_channel = at86rf23x_set_channel,
@@ -1295,8 +1290,6 @@
 	.t_off_to_tx_on = 200,
 	.t_frame = 4096,
 	.t_p_ack = 545,
-	.t_sifs = 192,
-	.t_lifs = 640,
 	.t_tx_timeout = 2000,
 	.rssi_base_val = -100,
 	.set_channel = at86rf212_set_channel,
@@ -1432,6 +1425,7 @@
 		lp->data = &at86rf231_data;
 		lp->hw->phy->channels_supported[0] = 0x7FFF800;
 		lp->hw->phy->current_channel = 11;
+		lp->hw->phy->symbol_duration = 16;
 		break;
 	case 7:
 		chip = "at86rf212";
@@ -1441,6 +1435,7 @@
 			lp->hw->phy->channels_supported[0] = 0x00007FF;
 			lp->hw->phy->channels_supported[2] = 0x00007FF;
 			lp->hw->phy->current_channel = 5;
+			lp->hw->phy->symbol_duration = 25;
 		} else {
 			rc = -ENOTSUPP;
 		}
@@ -1450,6 +1445,7 @@
 		lp->data = &at86rf233_data;
 		lp->hw->phy->channels_supported[0] = 0x7FFF800;
 		lp->hw->phy->current_channel = 13;
+		lp->hw->phy->symbol_duration = 16;
 		break;
 	default:
 		chip = "unkown";
diff --git a/drivers/net/ieee802154/fakelb.c b/drivers/net/ieee802154/fakelb.c
index 6e62286..96947d7 100644
--- a/drivers/net/ieee802154/fakelb.c
+++ b/drivers/net/ieee802154/fakelb.c
@@ -58,9 +58,6 @@
 {
 	pr_debug("set channel to %d\n", channel);
 
-	hw->phy->current_page = page;
-	hw->phy->current_channel = channel;
-
 	return 0;
 }
 
diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h
index 76668dc..ccba4fe 100644
--- a/drivers/net/wireless/ath/ath.h
+++ b/drivers/net/wireless/ath/ath.h
@@ -80,6 +80,7 @@
 
 struct ath_regulatory {
 	char alpha2[2];
+	enum nl80211_dfs_regions region;
 	u16 country_code;
 	u16 max_power_level;
 	u16 current_rd;
diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index f660553..7762061 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -799,6 +799,17 @@
 	mutex_unlock(&ar->conf_mutex);
 }
 
+static void ath10k_core_init_max_sta_count(struct ath10k *ar)
+{
+	if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
+		ar->max_num_peers = TARGET_10X_NUM_PEERS;
+		ar->max_num_stations = TARGET_10X_NUM_STATIONS;
+	} else {
+		ar->max_num_peers = TARGET_NUM_PEERS;
+		ar->max_num_stations = TARGET_NUM_STATIONS;
+	}
+}
+
 int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode)
 {
 	int status;
@@ -1035,6 +1046,8 @@
 		return ret;
 	}
 
+	ath10k_core_init_max_sta_count(ar);
+
 	mutex_lock(&ar->conf_mutex);
 
 	ret = ath10k_core_start(ar, ATH10K_FIRMWARE_MODE_NORMAL);
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 8f86bd3..514c219 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -79,10 +79,12 @@
 
 struct ath10k_skb_cb {
 	dma_addr_t paddr;
+	u8 eid;
 	u8 vdev_id;
 
 	struct {
 		u8 tid;
+		u16 freq;
 		bool is_offchan;
 		struct ath10k_htt_txbuf *txbuf;
 		u32 txbuf_paddr;
@@ -122,6 +124,7 @@
 	struct completion service_ready;
 	struct completion unified_ready;
 	wait_queue_head_t tx_credits_wq;
+	DECLARE_BITMAP(svc_map, WMI_SERVICE_MAX);
 	struct wmi_cmd_map *cmd;
 	struct wmi_vdev_param_map *vdev_param;
 	struct wmi_pdev_param_map *pdev_param;
@@ -218,6 +221,8 @@
 	int vdev_id;
 	u8 addr[ETH_ALEN];
 	DECLARE_BITMAP(peer_ids, ATH10K_MAX_NUM_PEER_IDS);
+
+	/* protected by ar->data_lock */
 	struct ieee80211_key_conf *keys[WMI_MAX_KEY_INDEX + 1];
 };
 
@@ -310,7 +315,6 @@
 	struct ath10k_fw_stats fw_stats;
 	struct completion fw_stats_complete;
 	bool fw_stats_done;
-	DECLARE_BITMAP(wmi_service_bitmap, WMI_SERVICE_MAX);
 
 	unsigned long htt_stats_mask;
 	struct delayed_work htt_stats_dwork;
@@ -320,6 +324,7 @@
 	/* protected by conf_mutex */
 	u32 fw_dbglog_mask;
 	u32 pktlog_filter;
+	u32 reg_addr;
 
 	u8 htt_max_amsdu;
 	u8 htt_max_ampdu;
@@ -560,8 +565,12 @@
 	struct list_head peers;
 	wait_queue_head_t peer_mapping_wq;
 
-	/* number of created peers; protected by data_lock */
+	/* protected by conf_mutex */
 	int num_peers;
+	int num_stations;
+
+	int max_num_peers;
+	int max_num_stations;
 
 	struct work_struct offchan_tx_work;
 	struct sk_buff_head offchan_tx_queue;
diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c
index a8f5a72..a716758 100644
--- a/drivers/net/wireless/ath/ath10k/debug.c
+++ b/drivers/net/wireless/ath/ath10k/debug.c
@@ -17,9 +17,8 @@
 
 #include <linux/module.h>
 #include <linux/debugfs.h>
-#include <linux/version.h>
-#include <linux/vermagic.h>
 #include <linux/vmalloc.h>
+#include <linux/utsname.h>
 
 #include "core.h"
 #include "debug.h"
@@ -124,7 +123,7 @@
 
 void ath10k_print_driver_info(struct ath10k *ar)
 {
-	ath10k_info(ar, "%s (0x%08x, 0x%08x) fw %s api %d htt %d.%d wmi %d.%d.%d.%d cal %s\n",
+	ath10k_info(ar, "%s (0x%08x, 0x%08x) fw %s api %d htt %d.%d wmi %d.%d.%d.%d cal %s max_sta %d\n",
 		    ar->hw_params.name,
 		    ar->target_version,
 		    ar->chip_id,
@@ -136,7 +135,8 @@
 		    ar->fw_version_minor,
 		    ar->fw_version_release,
 		    ar->fw_version_build,
-		    ath10k_cal_mode_str(ar->cal_mode));
+		    ath10k_cal_mode_str(ar->cal_mode),
+		    ar->max_num_stations);
 	ath10k_info(ar, "debug %d debugfs %d tracing %d dfs %d testmode %d\n",
 		    config_enabled(CONFIG_ATH10K_DEBUG),
 		    config_enabled(CONFIG_ATH10K_DEBUGFS),
@@ -179,13 +179,6 @@
 
 #ifdef CONFIG_ATH10K_DEBUGFS
 
-void ath10k_debug_read_service_map(struct ath10k *ar,
-				   const void *service_map,
-				   size_t map_size)
-{
-	memcpy(ar->debug.wmi_service_bitmap, service_map, map_size);
-}
-
 static ssize_t ath10k_read_wmi_services(struct file *file,
 					char __user *user_buf,
 					size_t count, loff_t *ppos)
@@ -207,8 +200,9 @@
 	if (len > buf_len)
 		len = buf_len;
 
+	spin_lock_bh(&ar->data_lock);
 	for (i = 0; i < WMI_SERVICE_MAX; i++) {
-		enabled = test_bit(i, ar->debug.wmi_service_bitmap);
+		enabled = test_bit(i, ar->wmi.svc_map);
 		name = wmi_service_name(i);
 
 		if (!name) {
@@ -224,6 +218,7 @@
 				 "%-40s %s\n",
 				 name, enabled ? "enabled" : "-");
 	}
+	spin_unlock_bh(&ar->data_lock);
 
 	ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
 
@@ -866,8 +861,8 @@
 	strlcpy(dump_data->fw_ver, ar->hw->wiphy->fw_version,
 		sizeof(dump_data->fw_ver));
 
-	dump_data->kernel_ver_code = cpu_to_le32(LINUX_VERSION_CODE);
-	strlcpy(dump_data->kernel_ver, VERMAGIC_STRING,
+	dump_data->kernel_ver_code = 0;
+	strlcpy(dump_data->kernel_ver, init_utsname()->release,
 		sizeof(dump_data->kernel_ver));
 
 	dump_data->tv_sec = cpu_to_le64(crash_data->timestamp.tv_sec);
@@ -929,6 +924,236 @@
 	.llseek = default_llseek,
 };
 
+static ssize_t ath10k_reg_addr_read(struct file *file,
+				    char __user *user_buf,
+				    size_t count, loff_t *ppos)
+{
+	struct ath10k *ar = file->private_data;
+	u8 buf[32];
+	unsigned int len = 0;
+	u32 reg_addr;
+
+	mutex_lock(&ar->conf_mutex);
+	reg_addr = ar->debug.reg_addr;
+	mutex_unlock(&ar->conf_mutex);
+
+	len += scnprintf(buf + len, sizeof(buf) - len, "0x%x\n", reg_addr);
+
+	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t ath10k_reg_addr_write(struct file *file,
+				     const char __user *user_buf,
+				     size_t count, loff_t *ppos)
+{
+	struct ath10k *ar = file->private_data;
+	u32 reg_addr;
+	int ret;
+
+	ret = kstrtou32_from_user(user_buf, count, 0, &reg_addr);
+	if (ret)
+		return ret;
+
+	if (!IS_ALIGNED(reg_addr, 4))
+		return -EFAULT;
+
+	mutex_lock(&ar->conf_mutex);
+	ar->debug.reg_addr = reg_addr;
+	mutex_unlock(&ar->conf_mutex);
+
+	return count;
+}
+
+static const struct file_operations fops_reg_addr = {
+	.read = ath10k_reg_addr_read,
+	.write = ath10k_reg_addr_write,
+	.open = simple_open,
+	.owner = THIS_MODULE,
+	.llseek = default_llseek,
+};
+
+static ssize_t ath10k_reg_value_read(struct file *file,
+				     char __user *user_buf,
+				     size_t count, loff_t *ppos)
+{
+	struct ath10k *ar = file->private_data;
+	u8 buf[48];
+	unsigned int len;
+	u32 reg_addr, reg_val;
+	int ret;
+
+	mutex_lock(&ar->conf_mutex);
+
+	if (ar->state != ATH10K_STATE_ON &&
+	    ar->state != ATH10K_STATE_UTF) {
+		ret = -ENETDOWN;
+		goto exit;
+	}
+
+	reg_addr = ar->debug.reg_addr;
+
+	reg_val = ath10k_hif_read32(ar, reg_addr);
+	len = scnprintf(buf, sizeof(buf), "0x%08x:0x%08x\n", reg_addr, reg_val);
+
+	ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+exit:
+	mutex_unlock(&ar->conf_mutex);
+
+	return ret;
+}
+
+static ssize_t ath10k_reg_value_write(struct file *file,
+				      const char __user *user_buf,
+				      size_t count, loff_t *ppos)
+{
+	struct ath10k *ar = file->private_data;
+	u32 reg_addr, reg_val;
+	int ret;
+
+	mutex_lock(&ar->conf_mutex);
+
+	if (ar->state != ATH10K_STATE_ON &&
+	    ar->state != ATH10K_STATE_UTF) {
+		ret = -ENETDOWN;
+		goto exit;
+	}
+
+	reg_addr = ar->debug.reg_addr;
+
+	ret = kstrtou32_from_user(user_buf, count, 0, &reg_val);
+	if (ret)
+		goto exit;
+
+	ath10k_hif_write32(ar, reg_addr, reg_val);
+
+	ret = count;
+
+exit:
+	mutex_unlock(&ar->conf_mutex);
+
+	return ret;
+}
+
+static const struct file_operations fops_reg_value = {
+	.read = ath10k_reg_value_read,
+	.write = ath10k_reg_value_write,
+	.open = simple_open,
+	.owner = THIS_MODULE,
+	.llseek = default_llseek,
+};
+
+static ssize_t ath10k_mem_value_read(struct file *file,
+				     char __user *user_buf,
+				     size_t count, loff_t *ppos)
+{
+	struct ath10k *ar = file->private_data;
+	u8 *buf;
+	int ret;
+
+	if (*ppos < 0)
+		return -EINVAL;
+
+	if (!count)
+		return 0;
+
+	mutex_lock(&ar->conf_mutex);
+
+	buf = vmalloc(count);
+	if (!buf) {
+		ret = -ENOMEM;
+		goto exit;
+	}
+
+	if (ar->state != ATH10K_STATE_ON &&
+	    ar->state != ATH10K_STATE_UTF) {
+		ret = -ENETDOWN;
+		goto exit;
+	}
+
+	ret = ath10k_hif_diag_read(ar, *ppos, buf, count);
+	if (ret) {
+		ath10k_warn(ar, "failed to read address 0x%08x via diagnose window fnrom debugfs: %d\n",
+			    (u32)(*ppos), ret);
+		goto exit;
+	}
+
+	ret = copy_to_user(user_buf, buf, count);
+	if (ret) {
+		ret = -EFAULT;
+		goto exit;
+	}
+
+	count -= ret;
+	*ppos += count;
+	ret = count;
+
+exit:
+	vfree(buf);
+	mutex_unlock(&ar->conf_mutex);
+
+	return ret;
+}
+
+static ssize_t ath10k_mem_value_write(struct file *file,
+				      const char __user *user_buf,
+				      size_t count, loff_t *ppos)
+{
+	struct ath10k *ar = file->private_data;
+	u8 *buf;
+	int ret;
+
+	if (*ppos < 0)
+		return -EINVAL;
+
+	if (!count)
+		return 0;
+
+	mutex_lock(&ar->conf_mutex);
+
+	buf = vmalloc(count);
+	if (!buf) {
+		ret = -ENOMEM;
+		goto exit;
+	}
+
+	if (ar->state != ATH10K_STATE_ON &&
+	    ar->state != ATH10K_STATE_UTF) {
+		ret = -ENETDOWN;
+		goto exit;
+	}
+
+	ret = copy_from_user(buf, user_buf, count);
+	if (ret) {
+		ret = -EFAULT;
+		goto exit;
+	}
+
+	ret = ath10k_hif_diag_write(ar, *ppos, buf, count);
+	if (ret) {
+		ath10k_warn(ar, "failed to write address 0x%08x via diagnose window from debugfs: %d\n",
+			    (u32)(*ppos), ret);
+		goto exit;
+	}
+
+	*ppos += count;
+	ret = count;
+
+exit:
+	vfree(buf);
+	mutex_unlock(&ar->conf_mutex);
+
+	return ret;
+}
+
+static const struct file_operations fops_mem_value = {
+	.read = ath10k_mem_value_read,
+	.write = ath10k_mem_value_write,
+	.open = simple_open,
+	.owner = THIS_MODULE,
+	.llseek = default_llseek,
+};
+
 static int ath10k_debug_htt_stats_req(struct ath10k *ar)
 {
 	u64 cookie;
@@ -1630,6 +1855,15 @@
 	debugfs_create_file("fw_crash_dump", S_IRUSR, ar->debug.debugfs_phy,
 			    ar, &fops_fw_crash_dump);
 
+	debugfs_create_file("reg_addr", S_IRUSR | S_IWUSR,
+			    ar->debug.debugfs_phy, ar, &fops_reg_addr);
+
+	debugfs_create_file("reg_value", S_IRUSR | S_IWUSR,
+			    ar->debug.debugfs_phy, ar, &fops_reg_value);
+
+	debugfs_create_file("mem_value", S_IRUSR | S_IWUSR,
+			    ar->debug.debugfs_phy, ar, &fops_mem_value);
+
 	debugfs_create_file("chip_id", S_IRUSR, ar->debug.debugfs_phy,
 			    ar, &fops_chip_id);
 
diff --git a/drivers/net/wireless/ath/ath10k/debug.h b/drivers/net/wireless/ath/ath10k/debug.h
index 0c934a8..1b87a5d 100644
--- a/drivers/net/wireless/ath/ath10k/debug.h
+++ b/drivers/net/wireless/ath/ath10k/debug.h
@@ -35,6 +35,7 @@
 	ATH10K_DBG_BMI		= 0x00000400,
 	ATH10K_DBG_REGULATORY	= 0x00000800,
 	ATH10K_DBG_TESTMODE	= 0x00001000,
+	ATH10K_DBG_WMI_PRINT	= 0x00002000,
 	ATH10K_DBG_ANY		= 0xffffffff,
 };
 
@@ -61,9 +62,6 @@
 void ath10k_debug_destroy(struct ath10k *ar);
 int ath10k_debug_register(struct ath10k *ar);
 void ath10k_debug_unregister(struct ath10k *ar);
-void ath10k_debug_read_service_map(struct ath10k *ar,
-				   const void *service_map,
-				   size_t map_size);
 void ath10k_debug_fw_stats_process(struct ath10k *ar, struct sk_buff *skb);
 struct ath10k_fw_crash_data *
 ath10k_debug_get_new_fw_crash_data(struct ath10k *ar);
@@ -108,12 +106,6 @@
 {
 }
 
-static inline void ath10k_debug_read_service_map(struct ath10k *ar,
-						 const void *service_map,
-						 size_t map_size)
-{
-}
-
 static inline void ath10k_debug_fw_stats_process(struct ath10k *ar,
 						 struct sk_buff *skb)
 {
diff --git a/drivers/net/wireless/ath/ath10k/hif.h b/drivers/net/wireless/ath/ath10k/hif.h
index 30301f5..0c92e02 100644
--- a/drivers/net/wireless/ath/ath10k/hif.h
+++ b/drivers/net/wireless/ath/ath10k/hif.h
@@ -20,6 +20,7 @@
 
 #include <linux/kernel.h>
 #include "core.h"
+#include "debug.h"
 
 struct ath10k_hif_sg_item {
 	u16 transfer_id;
@@ -31,11 +32,9 @@
 
 struct ath10k_hif_cb {
 	int (*tx_completion)(struct ath10k *ar,
-			     struct sk_buff *wbuf,
-			     unsigned transfer_id);
+			     struct sk_buff *wbuf);
 	int (*rx_completion)(struct ath10k *ar,
-			     struct sk_buff *wbuf,
-			     u8 pipe_id);
+			     struct sk_buff *wbuf);
 };
 
 struct ath10k_hif_ops {
@@ -47,6 +46,8 @@
 	int (*diag_read)(struct ath10k *ar, u32 address, void *buf,
 			 size_t buf_len);
 
+	int (*diag_write)(struct ath10k *ar, u32 address, const void *data,
+			  int nbytes);
 	/*
 	 * API to handle HIF-specific BMI message exchanges, this API is
 	 * synchronous and only allowed to be called from a context that
@@ -84,6 +85,10 @@
 
 	u16 (*get_free_queue_number)(struct ath10k *ar, u8 pipe_id);
 
+	u32 (*read32)(struct ath10k *ar, u32 address);
+
+	void (*write32)(struct ath10k *ar, u32 address, u32 value);
+
 	/* Power up the device and enter BMI transfer mode for FW download */
 	int (*power_up)(struct ath10k *ar);
 
@@ -108,6 +113,15 @@
 	return ar->hif.ops->diag_read(ar, address, buf, buf_len);
 }
 
+static inline int ath10k_hif_diag_write(struct ath10k *ar, u32 address,
+					const void *data, int nbytes)
+{
+	if (!ar->hif.ops->diag_write)
+		return -EOPNOTSUPP;
+
+	return ar->hif.ops->diag_write(ar, address, data, nbytes);
+}
+
 static inline int ath10k_hif_exchange_bmi_msg(struct ath10k *ar,
 					      void *request, u32 request_len,
 					      void *response, u32 *response_len)
@@ -187,4 +201,25 @@
 	return ar->hif.ops->resume(ar);
 }
 
+static inline u32 ath10k_hif_read32(struct ath10k *ar, u32 address)
+{
+	if (!ar->hif.ops->read32) {
+		ath10k_warn(ar, "hif read32 not supported\n");
+		return 0xdeaddead;
+	}
+
+	return ar->hif.ops->read32(ar, address);
+}
+
+static inline void ath10k_hif_write32(struct ath10k *ar,
+				      u32 address, u32 data)
+{
+	if (!ar->hif.ops->write32) {
+		ath10k_warn(ar, "hif write32 not supported\n");
+		return;
+	}
+
+	ar->hif.ops->write32(ar, address, data);
+}
+
 #endif /* _HIF_H_ */
diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c
index 676bd4e..f1946a6 100644
--- a/drivers/net/wireless/ath/ath10k/htc.c
+++ b/drivers/net/wireless/ath/ath10k/htc.c
@@ -160,6 +160,7 @@
 
 	ath10k_htc_prepare_tx_skb(ep, skb);
 
+	skb_cb->eid = eid;
 	skb_cb->paddr = dma_map_single(dev, skb->data, skb->len, DMA_TO_DEVICE);
 	ret = dma_mapping_error(dev, skb_cb->paddr);
 	if (ret)
@@ -197,15 +198,18 @@
 }
 
 static int ath10k_htc_tx_completion_handler(struct ath10k *ar,
-					    struct sk_buff *skb,
-					    unsigned int eid)
+					    struct sk_buff *skb)
 {
 	struct ath10k_htc *htc = &ar->htc;
-	struct ath10k_htc_ep *ep = &htc->endpoint[eid];
+	struct ath10k_skb_cb *skb_cb;
+	struct ath10k_htc_ep *ep;
 
 	if (WARN_ON_ONCE(!skb))
 		return 0;
 
+	skb_cb = ATH10K_SKB_CB(skb);
+	ep = &htc->endpoint[skb_cb->eid];
+
 	ath10k_htc_notify_tx_completion(ep, skb);
 	/* the skb now belongs to the completion handler */
 
@@ -317,8 +321,7 @@
 }
 
 static int ath10k_htc_rx_completion_handler(struct ath10k *ar,
-					    struct sk_buff *skb,
-					    u8 pipe_id)
+					    struct sk_buff *skb)
 {
 	int status = 0;
 	struct ath10k_htc *htc = &ar->htc;
diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h
index 15c58e8..1bd5545 100644
--- a/drivers/net/wireless/ath/ath10k/htt.h
+++ b/drivers/net/wireless/ath/ath10k/htt.h
@@ -126,6 +126,7 @@
  *                  (HL hosts manage queues on the host )
  *       more_in_batch: only for HL hosts. indicates if more packets are
  *                      pending. this allows target to wait and aggregate
+ *       freq: 0 means home channel of given vdev. intended for offchannel
  */
 struct htt_data_tx_desc {
 	u8 flags0; /* %HTT_DATA_TX_DESC_FLAGS0_ */
@@ -133,7 +134,8 @@
 	__le16 len;
 	__le16 id;
 	__le32 frags_paddr;
-	__le32 peerid;
+	__le16 peerid;
+	__le16 freq;
 	u8 prefetch[0]; /* start of frame, for FW classification engine */
 } __packed;
 
@@ -156,6 +158,9 @@
 	HTT_RX_RING_FLAGS_PHY_DATA_RX  = 1 << 15
 };
 
+#define HTT_RX_RING_SIZE_MIN 128
+#define HTT_RX_RING_SIZE_MAX 2048
+
 struct htt_rx_ring_setup_ring {
 	__le32 fw_idx_shadow_reg_paddr;
 	__le32 rx_ring_base_paddr;
diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c
index 52c6306..9c782a4 100644
--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
@@ -25,19 +25,8 @@
 
 #include <linux/log2.h>
 
-/* slightly larger than one large A-MPDU */
-#define HTT_RX_RING_SIZE_MIN 128
-
-/* roughly 20 ms @ 1 Gbps of 1500B MSDUs */
-#define HTT_RX_RING_SIZE_MAX 2048
-
-#define HTT_RX_AVG_FRM_BYTES 1000
-
-/* ms, very conservative */
-#define HTT_RX_HOST_LATENCY_MAX_MS 20
-
-/* ms, conservative */
-#define HTT_RX_HOST_LATENCY_WORST_LIKELY_MS 10
+#define HTT_RX_RING_SIZE 1024
+#define HTT_RX_RING_FILL_LEVEL 1000
 
 /* when under memory pressure rx ring refill may fail and needs a retry */
 #define HTT_RX_RING_REFILL_RETRY_MS 50
@@ -45,68 +34,6 @@
 static int ath10k_htt_rx_get_csum_state(struct sk_buff *skb);
 static void ath10k_htt_txrx_compl_task(unsigned long ptr);
 
-static int ath10k_htt_rx_ring_size(struct ath10k_htt *htt)
-{
-	int size;
-
-	/*
-	 * It is expected that the host CPU will typically be able to
-	 * service the rx indication from one A-MPDU before the rx
-	 * indication from the subsequent A-MPDU happens, roughly 1-2 ms
-	 * later. However, the rx ring should be sized very conservatively,
-	 * to accomodate the worst reasonable delay before the host CPU
-	 * services a rx indication interrupt.
-	 *
-	 * The rx ring need not be kept full of empty buffers. In theory,
-	 * the htt host SW can dynamically track the low-water mark in the
-	 * rx ring, and dynamically adjust the level to which the rx ring
-	 * is filled with empty buffers, to dynamically meet the desired
-	 * low-water mark.
-	 *
-	 * In contrast, it's difficult to resize the rx ring itself, once
-	 * it's in use. Thus, the ring itself should be sized very
-	 * conservatively, while the degree to which the ring is filled
-	 * with empty buffers should be sized moderately conservatively.
-	 */
-
-	/* 1e6 bps/mbps / 1e3 ms per sec = 1000 */
-	size =
-	    htt->max_throughput_mbps +
-	    1000  /
-	    (8 * HTT_RX_AVG_FRM_BYTES) * HTT_RX_HOST_LATENCY_MAX_MS;
-
-	if (size < HTT_RX_RING_SIZE_MIN)
-		size = HTT_RX_RING_SIZE_MIN;
-
-	if (size > HTT_RX_RING_SIZE_MAX)
-		size = HTT_RX_RING_SIZE_MAX;
-
-	size = roundup_pow_of_two(size);
-
-	return size;
-}
-
-static int ath10k_htt_rx_ring_fill_level(struct ath10k_htt *htt)
-{
-	int size;
-
-	/* 1e6 bps/mbps / 1e3 ms per sec = 1000 */
-	size =
-	    htt->max_throughput_mbps *
-	    1000  /
-	    (8 * HTT_RX_AVG_FRM_BYTES) * HTT_RX_HOST_LATENCY_WORST_LIKELY_MS;
-
-	/*
-	 * Make sure the fill level is at least 1 less than the ring size.
-	 * Leaving 1 element empty allows the SW to easily distinguish
-	 * between a full ring vs. an empty ring.
-	 */
-	if (size >= htt->rx_ring.size)
-		size = htt->rx_ring.size - 1;
-
-	return size;
-}
-
 static void ath10k_htt_rx_ring_free(struct ath10k_htt *htt)
 {
 	struct sk_buff *skb;
@@ -301,40 +228,29 @@
 	return msdu;
 }
 
-static void ath10k_htt_rx_free_msdu_chain(struct sk_buff *skb)
-{
-	struct sk_buff *next;
-
-	while (skb) {
-		next = skb->next;
-		dev_kfree_skb_any(skb);
-		skb = next;
-	}
-}
-
 /* return: < 0 fatal error, 0 - non chained msdu, 1 chained msdu */
 static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt,
 				   u8 **fw_desc, int *fw_desc_len,
-				   struct sk_buff **head_msdu,
-				   struct sk_buff **tail_msdu,
-				   u32 *attention)
+				   struct sk_buff_head *amsdu)
 {
 	struct ath10k *ar = htt->ar;
 	int msdu_len, msdu_chaining = 0;
-	struct sk_buff *msdu, *next;
+	struct sk_buff *msdu;
 	struct htt_rx_desc *rx_desc;
 
 	lockdep_assert_held(&htt->rx_ring.lock);
 
-	if (htt->rx_confused) {
-		ath10k_warn(ar, "htt is confused. refusing rx\n");
-		return -1;
-	}
-
-	msdu = *head_msdu = ath10k_htt_rx_netbuf_pop(htt);
-	while (msdu) {
+	for (;;) {
 		int last_msdu, msdu_len_invalid, msdu_chained;
 
+		msdu = ath10k_htt_rx_netbuf_pop(htt);
+		if (!msdu) {
+			__skb_queue_purge(amsdu);
+			return -ENOENT;
+		}
+
+		__skb_queue_tail(amsdu, msdu);
+
 		rx_desc = (struct htt_rx_desc *)msdu->data;
 
 		/* FIXME: we must report msdu payload since this is what caller
@@ -352,19 +268,10 @@
 		 */
 		if (!(__le32_to_cpu(rx_desc->attention.flags)
 				& RX_ATTENTION_FLAGS_MSDU_DONE)) {
-			ath10k_htt_rx_free_msdu_chain(*head_msdu);
-			*head_msdu = NULL;
-			msdu = NULL;
-			ath10k_err(ar, "htt rx stopped. cannot recover\n");
-			htt->rx_confused = true;
-			break;
+			__skb_queue_purge(amsdu);
+			return -EIO;
 		}
 
-		*attention |= __le32_to_cpu(rx_desc->attention.flags) &
-					    (RX_ATTENTION_FLAGS_TKIP_MIC_ERR |
-					     RX_ATTENTION_FLAGS_DECRYPT_ERR |
-					     RX_ATTENTION_FLAGS_FCS_ERR |
-					     RX_ATTENTION_FLAGS_MGMT_TYPE);
 		/*
 		 * Copy the FW rx descriptor for this MSDU from the rx
 		 * indication message into the MSDU's netbuf. HL uses the
@@ -421,25 +328,18 @@
 		skb_put(msdu, min(msdu_len, HTT_RX_MSDU_SIZE));
 		msdu_len -= msdu->len;
 
-		/* FIXME: Do chained buffers include htt_rx_desc or not? */
+		/* Note: Chained buffers do not contain rx descriptor */
 		while (msdu_chained--) {
-			struct sk_buff *next = ath10k_htt_rx_netbuf_pop(htt);
-
-			if (!next) {
-				ath10k_warn(ar, "failed to pop chained msdu\n");
-				ath10k_htt_rx_free_msdu_chain(*head_msdu);
-				*head_msdu = NULL;
-				msdu = NULL;
-				htt->rx_confused = true;
-				break;
+			msdu = ath10k_htt_rx_netbuf_pop(htt);
+			if (!msdu) {
+				__skb_queue_purge(amsdu);
+				return -ENOENT;
 			}
 
-			skb_trim(next, 0);
-			skb_put(next, min(msdu_len, HTT_RX_BUF_SIZE));
-			msdu_len -= next->len;
-
-			msdu->next = next;
-			msdu = next;
+			__skb_queue_tail(amsdu, msdu);
+			skb_trim(msdu, 0);
+			skb_put(msdu, min(msdu_len, HTT_RX_BUF_SIZE));
+			msdu_len -= msdu->len;
 			msdu_chaining = 1;
 		}
 
@@ -448,18 +348,12 @@
 
 		trace_ath10k_htt_rx_desc(ar, &rx_desc->attention,
 					 sizeof(*rx_desc) - sizeof(u32));
-		if (last_msdu) {
-			msdu->next = NULL;
+
+		if (last_msdu)
 			break;
-		}
-
-		next = ath10k_htt_rx_netbuf_pop(htt);
-		msdu->next = next;
-		msdu = next;
 	}
-	*tail_msdu = msdu;
 
-	if (*head_msdu == NULL)
+	if (skb_queue_empty(amsdu))
 		msdu_chaining = -1;
 
 	/*
@@ -495,25 +389,18 @@
 
 	htt->rx_confused = false;
 
-	htt->rx_ring.size = ath10k_htt_rx_ring_size(htt);
+	/* XXX: The fill level could be changed during runtime in response to
+	 * the host processing latency. Is this really worth it?
+	 */
+	htt->rx_ring.size = HTT_RX_RING_SIZE;
+	htt->rx_ring.size_mask = htt->rx_ring.size - 1;
+	htt->rx_ring.fill_level = HTT_RX_RING_FILL_LEVEL;
+
 	if (!is_power_of_2(htt->rx_ring.size)) {
 		ath10k_warn(ar, "htt rx ring size is not power of 2\n");
 		return -EINVAL;
 	}
 
-	htt->rx_ring.size_mask = htt->rx_ring.size - 1;
-
-	/*
-	 * Set the initial value for the level to which the rx ring
-	 * should be filled, based on the max throughput and the
-	 * worst likely latency for the host to fill the rx ring
-	 * with new buffers. In theory, this fill level can be
-	 * dynamically adjusted from the initial value set here, to
-	 * reflect the actual host latency rather than a
-	 * conservative assumption about the host latency.
-	 */
-	htt->rx_ring.fill_level = ath10k_htt_rx_ring_fill_level(htt);
-
 	htt->rx_ring.netbufs_ring =
 		kzalloc(htt->rx_ring.size * sizeof(struct sk_buff *),
 			GFP_KERNEL);
@@ -628,35 +515,6 @@
 	return 0;
 }
 
-/* Applies for first msdu in chain, before altering it. */
-static struct ieee80211_hdr *ath10k_htt_rx_skb_get_hdr(struct sk_buff *skb)
-{
-	struct htt_rx_desc *rxd;
-	enum rx_msdu_decap_format fmt;
-
-	rxd = (void *)skb->data - sizeof(*rxd);
-	fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
-		 RX_MSDU_START_INFO1_DECAP_FORMAT);
-
-	if (fmt == RX_MSDU_DECAP_RAW)
-		return (void *)skb->data;
-
-	return (void *)skb->data - RX_HTT_HDR_STATUS_LEN;
-}
-
-/* This function only applies for first msdu in an msdu chain */
-static bool ath10k_htt_rx_hdr_is_amsdu(struct ieee80211_hdr *hdr)
-{
-	u8 *qc;
-
-	if (ieee80211_is_data_qos(hdr->frame_control)) {
-		qc = ieee80211_get_qos_ctl(hdr);
-		if (qc[0] & 0x80)
-			return true;
-	}
-	return false;
-}
-
 struct rfc1042_hdr {
 	u8 llc_dsap;
 	u8 llc_ssap;
@@ -691,23 +549,34 @@
 };
 
 static void ath10k_htt_rx_h_rates(struct ath10k *ar,
-				  enum ieee80211_band band,
-				  u8 info0, u32 info1, u32 info2,
-				  struct ieee80211_rx_status *status)
+				  struct ieee80211_rx_status *status,
+				  struct htt_rx_desc *rxd)
 {
+	enum ieee80211_band band;
 	u8 cck, rate, rate_idx, bw, sgi, mcs, nss;
 	u8 preamble = 0;
+	u32 info1, info2, info3;
 
-	/* Check if valid fields */
-	if (!(info0 & HTT_RX_INDICATION_INFO0_START_VALID))
+	/* Band value can't be set as undefined but freq can be 0 - use that to
+	 * determine whether band is provided.
+	 *
+	 * FIXME: Perhaps this can go away if CCK rate reporting is a little
+	 * reworked?
+	 */
+	if (!status->freq)
 		return;
 
-	preamble = MS(info1, HTT_RX_INDICATION_INFO1_PREAMBLE_TYPE);
+	band = status->band;
+	info1 = __le32_to_cpu(rxd->ppdu_start.info1);
+	info2 = __le32_to_cpu(rxd->ppdu_start.info2);
+	info3 = __le32_to_cpu(rxd->ppdu_start.info3);
+
+	preamble = MS(info1, RX_PPDU_START_INFO1_PREAMBLE_TYPE);
 
 	switch (preamble) {
 	case HTT_RX_LEGACY:
-		cck = info0 & HTT_RX_INDICATION_INFO0_LEGACY_RATE_CCK;
-		rate = MS(info0, HTT_RX_INDICATION_INFO0_LEGACY_RATE);
+		cck = info1 & RX_PPDU_START_INFO1_L_SIG_RATE_SELECT;
+		rate = MS(info1, RX_PPDU_START_INFO1_L_SIG_RATE);
 		rate_idx = 0;
 
 		if (rate < 0x08 || rate > 0x0F)
@@ -734,11 +603,11 @@
 		break;
 	case HTT_RX_HT:
 	case HTT_RX_HT_WITH_TXBF:
-		/* HT-SIG - Table 20-11 in info1 and info2 */
-		mcs = info1 & 0x1F;
+		/* HT-SIG - Table 20-11 in info2 and info3 */
+		mcs = info2 & 0x1F;
 		nss = mcs >> 3;
-		bw = (info1 >> 7) & 1;
-		sgi = (info2 >> 7) & 1;
+		bw = (info2 >> 7) & 1;
+		sgi = (info3 >> 7) & 1;
 
 		status->rate_idx = mcs;
 		status->flag |= RX_FLAG_HT;
@@ -749,12 +618,12 @@
 		break;
 	case HTT_RX_VHT:
 	case HTT_RX_VHT_WITH_TXBF:
-		/* VHT-SIG-A1 in info 1, VHT-SIG-A2 in info2
+		/* VHT-SIG-A1 in info2, VHT-SIG-A2 in info3
 		   TODO check this */
-		mcs = (info2 >> 4) & 0x0F;
-		nss = ((info1 >> 10) & 0x07) + 1;
-		bw = info1 & 3;
-		sgi = info2 & 1;
+		mcs = (info3 >> 4) & 0x0F;
+		nss = ((info2 >> 10) & 0x07) + 1;
+		bw = info2 & 3;
+		sgi = info3 & 1;
 
 		status->rate_idx = mcs;
 		status->vht_nss = nss;
@@ -782,41 +651,6 @@
 	}
 }
 
-static void ath10k_htt_rx_h_protected(struct ath10k_htt *htt,
-				      struct ieee80211_rx_status *rx_status,
-				      struct sk_buff *skb,
-				      enum htt_rx_mpdu_encrypt_type enctype,
-				      enum rx_msdu_decap_format fmt,
-				      bool dot11frag)
-{
-	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
-
-	rx_status->flag &= ~(RX_FLAG_DECRYPTED |
-			     RX_FLAG_IV_STRIPPED |
-			     RX_FLAG_MMIC_STRIPPED);
-
-	if (enctype == HTT_RX_MPDU_ENCRYPT_NONE)
-		return;
-
-	/*
-	 * There's no explicit rx descriptor flag to indicate whether a given
-	 * frame has been decrypted or not. We're forced to use the decap
-	 * format as an implicit indication. However fragmentation rx is always
-	 * raw and it probably never reports undecrypted raws.
-	 *
-	 * This makes sure sniffed frames are reported as-is without stripping
-	 * the protected flag.
-	 */
-	if (fmt == RX_MSDU_DECAP_RAW && !dot11frag)
-		return;
-
-	rx_status->flag |= RX_FLAG_DECRYPTED |
-			   RX_FLAG_IV_STRIPPED |
-			   RX_FLAG_MMIC_STRIPPED;
-	hdr->frame_control = __cpu_to_le16(__le16_to_cpu(hdr->frame_control) &
-					   ~IEEE80211_FCTL_PROTECTED);
-}
-
 static bool ath10k_htt_rx_h_channel(struct ath10k *ar,
 				    struct ieee80211_rx_status *status)
 {
@@ -837,6 +671,72 @@
 	return true;
 }
 
+static void ath10k_htt_rx_h_signal(struct ath10k *ar,
+				   struct ieee80211_rx_status *status,
+				   struct htt_rx_desc *rxd)
+{
+	/* FIXME: Get real NF */
+	status->signal = ATH10K_DEFAULT_NOISE_FLOOR +
+			 rxd->ppdu_start.rssi_comb;
+	status->flag &= ~RX_FLAG_NO_SIGNAL_VAL;
+}
+
+static void ath10k_htt_rx_h_mactime(struct ath10k *ar,
+				    struct ieee80211_rx_status *status,
+				    struct htt_rx_desc *rxd)
+{
+	/* FIXME: TSF is known only at the end of PPDU, in the last MPDU. This
+	 * means all prior MSDUs in a PPDU are reported to mac80211 without the
+	 * TSF. Is it worth holding frames until end of PPDU is known?
+	 *
+	 * FIXME: Can we get/compute 64bit TSF?
+	 */
+	status->mactime = __le32_to_cpu(rxd->ppdu_end.tsf_timestamp);
+	status->flag |= RX_FLAG_MACTIME_END;
+}
+
+static void ath10k_htt_rx_h_ppdu(struct ath10k *ar,
+				 struct sk_buff_head *amsdu,
+				 struct ieee80211_rx_status *status)
+{
+	struct sk_buff *first;
+	struct htt_rx_desc *rxd;
+	bool is_first_ppdu;
+	bool is_last_ppdu;
+
+	if (skb_queue_empty(amsdu))
+		return;
+
+	first = skb_peek(amsdu);
+	rxd = (void *)first->data - sizeof(*rxd);
+
+	is_first_ppdu = !!(rxd->attention.flags &
+			   __cpu_to_le32(RX_ATTENTION_FLAGS_FIRST_MPDU));
+	is_last_ppdu = !!(rxd->attention.flags &
+			  __cpu_to_le32(RX_ATTENTION_FLAGS_LAST_MPDU));
+
+	if (is_first_ppdu) {
+		/* New PPDU starts so clear out the old per-PPDU status. */
+		status->freq = 0;
+		status->rate_idx = 0;
+		status->vht_nss = 0;
+		status->vht_flag &= ~RX_VHT_FLAG_80MHZ;
+		status->flag &= ~(RX_FLAG_HT |
+				  RX_FLAG_VHT |
+				  RX_FLAG_SHORT_GI |
+				  RX_FLAG_40MHZ |
+				  RX_FLAG_MACTIME_END);
+		status->flag |= RX_FLAG_NO_SIGNAL_VAL;
+
+		ath10k_htt_rx_h_signal(ar, status, rxd);
+		ath10k_htt_rx_h_channel(ar, status);
+		ath10k_htt_rx_h_rates(ar, status, rxd);
+	}
+
+	if (is_last_ppdu)
+		ath10k_htt_rx_h_mactime(ar, status, rxd);
+}
+
 static const char * const tid_to_ac[] = {
 	"BE",
 	"BK",
@@ -913,187 +813,263 @@
 	return round_up(ieee80211_hdrlen(hdr->frame_control), 4);
 }
 
-static void ath10k_htt_rx_amsdu(struct ath10k_htt *htt,
-				struct ieee80211_rx_status *rx_status,
-				struct sk_buff *skb_in)
+static void ath10k_htt_rx_h_undecap_raw(struct ath10k *ar,
+					struct sk_buff *msdu,
+					struct ieee80211_rx_status *status,
+					enum htt_rx_mpdu_encrypt_type enctype,
+					bool is_decrypted)
 {
-	struct ath10k *ar = htt->ar;
-	struct htt_rx_desc *rxd;
-	struct sk_buff *skb = skb_in;
-	struct sk_buff *first;
-	enum rx_msdu_decap_format fmt;
-	enum htt_rx_mpdu_encrypt_type enctype;
 	struct ieee80211_hdr *hdr;
-	u8 hdr_buf[64], da[ETH_ALEN], sa[ETH_ALEN], *qos;
-	unsigned int hdr_len;
+	struct htt_rx_desc *rxd;
+	size_t hdr_len;
+	size_t crypto_len;
+	bool is_first;
+	bool is_last;
 
-	rxd = (void *)skb->data - sizeof(*rxd);
-	enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0),
-		     RX_MPDU_START_INFO0_ENCRYPT_TYPE);
+	rxd = (void *)msdu->data - sizeof(*rxd);
+	is_first = !!(rxd->msdu_end.info0 &
+		      __cpu_to_le32(RX_MSDU_END_INFO0_FIRST_MSDU));
+	is_last = !!(rxd->msdu_end.info0 &
+		     __cpu_to_le32(RX_MSDU_END_INFO0_LAST_MSDU));
 
-	hdr = (struct ieee80211_hdr *)rxd->rx_hdr_status;
+	/* Delivered decapped frame:
+	 * [802.11 header]
+	 * [crypto param] <-- can be trimmed if !fcs_err &&
+	 *                    !decrypt_err && !peer_idx_invalid
+	 * [amsdu header] <-- only if A-MSDU
+	 * [rfc1042/llc]
+	 * [payload]
+	 * [FCS] <-- at end, needs to be trimmed
+	 */
+
+	/* This probably shouldn't happen but warn just in case */
+	if (unlikely(WARN_ON_ONCE(!is_first)))
+		return;
+
+	/* This probably shouldn't happen but warn just in case */
+	if (unlikely(WARN_ON_ONCE(!(is_first && is_last))))
+		return;
+
+	skb_trim(msdu, msdu->len - FCS_LEN);
+
+	/* In most cases this will be true for sniffed frames. It makes sense
+	 * to deliver them as-is without stripping the crypto param. This would
+	 * also make sense for software based decryption (which is not
+	 * implemented in ath10k).
+	 *
+	 * If there's no error then the frame is decrypted. At least that is
+	 * the case for frames that come in via fragmented rx indication.
+	 */
+	if (!is_decrypted)
+		return;
+
+	/* The payload is decrypted so strip crypto params. Start from tail
+	 * since hdr is used to compute some stuff.
+	 */
+
+	hdr = (void *)msdu->data;
+
+	/* Tail */
+	skb_trim(msdu, msdu->len - ath10k_htt_rx_crypto_tail_len(ar, enctype));
+
+	/* MMIC */
+	if (!ieee80211_has_morefrags(hdr->frame_control) &&
+	    enctype == HTT_RX_MPDU_ENCRYPT_TKIP_WPA)
+		skb_trim(msdu, msdu->len - 8);
+
+	/* Head */
 	hdr_len = ieee80211_hdrlen(hdr->frame_control);
-	memcpy(hdr_buf, hdr, hdr_len);
-	hdr = (struct ieee80211_hdr *)hdr_buf;
+	crypto_len = ath10k_htt_rx_crypto_param_len(ar, enctype);
 
-	first = skb;
-	while (skb) {
-		void *decap_hdr;
-		int len;
-
-		rxd = (void *)skb->data - sizeof(*rxd);
-		fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
-			 RX_MSDU_START_INFO1_DECAP_FORMAT);
-		decap_hdr = (void *)rxd->rx_hdr_status;
-
-		skb->ip_summed = ath10k_htt_rx_get_csum_state(skb);
-
-		/* First frame in an A-MSDU chain has more decapped data. */
-		if (skb == first) {
-			len = round_up(ieee80211_hdrlen(hdr->frame_control), 4);
-			len += round_up(ath10k_htt_rx_crypto_param_len(ar,
-						enctype), 4);
-			decap_hdr += len;
-		}
-
-		switch (fmt) {
-		case RX_MSDU_DECAP_RAW:
-			/* remove trailing FCS */
-			skb_trim(skb, skb->len - FCS_LEN);
-			break;
-		case RX_MSDU_DECAP_NATIVE_WIFI:
-			/* pull decapped header and copy SA & DA */
-			hdr = (struct ieee80211_hdr *)skb->data;
-			hdr_len = ath10k_htt_rx_nwifi_hdrlen(hdr);
-			ether_addr_copy(da, ieee80211_get_DA(hdr));
-			ether_addr_copy(sa, ieee80211_get_SA(hdr));
-			skb_pull(skb, hdr_len);
-
-			/* push original 802.11 header */
-			hdr = (struct ieee80211_hdr *)hdr_buf;
-			hdr_len = ieee80211_hdrlen(hdr->frame_control);
-			memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
-
-			/* original A-MSDU header has the bit set but we're
-			 * not including A-MSDU subframe header */
-			hdr = (struct ieee80211_hdr *)skb->data;
-			qos = ieee80211_get_qos_ctl(hdr);
-			qos[0] &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
-
-			/* original 802.11 header has a different DA and in
-			 * case of 4addr it may also have different SA
-			 */
-			ether_addr_copy(ieee80211_get_DA(hdr), da);
-			ether_addr_copy(ieee80211_get_SA(hdr), sa);
-			break;
-		case RX_MSDU_DECAP_ETHERNET2_DIX:
-			/* strip ethernet header and insert decapped 802.11
-			 * header, amsdu subframe header and rfc1042 header */
-
-			len = 0;
-			len += sizeof(struct rfc1042_hdr);
-			len += sizeof(struct amsdu_subframe_hdr);
-
-			skb_pull(skb, sizeof(struct ethhdr));
-			memcpy(skb_push(skb, len), decap_hdr, len);
-			memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
-			break;
-		case RX_MSDU_DECAP_8023_SNAP_LLC:
-			/* insert decapped 802.11 header making a singly
-			 * A-MSDU */
-			memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
-			break;
-		}
-
-		skb_in = skb;
-		ath10k_htt_rx_h_protected(htt, rx_status, skb_in, enctype, fmt,
-					  false);
-		skb = skb->next;
-		skb_in->next = NULL;
-
-		if (skb)
-			rx_status->flag |= RX_FLAG_AMSDU_MORE;
-		else
-			rx_status->flag &= ~RX_FLAG_AMSDU_MORE;
-
-		ath10k_process_rx(htt->ar, rx_status, skb_in);
-	}
-
-	/* FIXME: It might be nice to re-assemble the A-MSDU when there's a
-	 * monitor interface active for sniffing purposes. */
+	memmove((void *)msdu->data + crypto_len,
+		(void *)msdu->data, hdr_len);
+	skb_pull(msdu, crypto_len);
 }
 
-static void ath10k_htt_rx_msdu(struct ath10k_htt *htt,
-			       struct ieee80211_rx_status *rx_status,
-			       struct sk_buff *skb)
+static void ath10k_htt_rx_h_undecap_nwifi(struct ath10k *ar,
+					  struct sk_buff *msdu,
+					  struct ieee80211_rx_status *status,
+					  const u8 first_hdr[64])
 {
-	struct ath10k *ar = htt->ar;
-	struct htt_rx_desc *rxd;
 	struct ieee80211_hdr *hdr;
-	enum rx_msdu_decap_format fmt;
-	enum htt_rx_mpdu_encrypt_type enctype;
-	int hdr_len;
-	void *rfc1042;
+	size_t hdr_len;
+	u8 da[ETH_ALEN];
+	u8 sa[ETH_ALEN];
 
-	/* This shouldn't happen. If it does than it may be a FW bug. */
-	if (skb->next) {
-		ath10k_warn(ar, "htt rx received chained non A-MSDU frame\n");
-		ath10k_htt_rx_free_msdu_chain(skb->next);
-		skb->next = NULL;
+	/* Delivered decapped frame:
+	 * [nwifi 802.11 header] <-- replaced with 802.11 hdr
+	 * [rfc1042/llc]
+	 *
+	 * Note: The nwifi header doesn't have QoS Control and is
+	 * (always?) a 3addr frame.
+	 *
+	 * Note2: There's no A-MSDU subframe header. Even if it's part
+	 * of an A-MSDU.
+	 */
+
+	/* pull decapped header and copy SA & DA */
+	hdr = (struct ieee80211_hdr *)msdu->data;
+	hdr_len = ath10k_htt_rx_nwifi_hdrlen(hdr);
+	ether_addr_copy(da, ieee80211_get_DA(hdr));
+	ether_addr_copy(sa, ieee80211_get_SA(hdr));
+	skb_pull(msdu, hdr_len);
+
+	/* push original 802.11 header */
+	hdr = (struct ieee80211_hdr *)first_hdr;
+	hdr_len = ieee80211_hdrlen(hdr->frame_control);
+	memcpy(skb_push(msdu, hdr_len), hdr, hdr_len);
+
+	/* original 802.11 header has a different DA and in
+	 * case of 4addr it may also have different SA
+	 */
+	hdr = (struct ieee80211_hdr *)msdu->data;
+	ether_addr_copy(ieee80211_get_DA(hdr), da);
+	ether_addr_copy(ieee80211_get_SA(hdr), sa);
+}
+
+static void *ath10k_htt_rx_h_find_rfc1042(struct ath10k *ar,
+					  struct sk_buff *msdu,
+					  enum htt_rx_mpdu_encrypt_type enctype)
+{
+	struct ieee80211_hdr *hdr;
+	struct htt_rx_desc *rxd;
+	size_t hdr_len, crypto_len;
+	void *rfc1042;
+	bool is_first, is_last, is_amsdu;
+
+	rxd = (void *)msdu->data - sizeof(*rxd);
+	hdr = (void *)rxd->rx_hdr_status;
+
+	is_first = !!(rxd->msdu_end.info0 &
+		      __cpu_to_le32(RX_MSDU_END_INFO0_FIRST_MSDU));
+	is_last = !!(rxd->msdu_end.info0 &
+		     __cpu_to_le32(RX_MSDU_END_INFO0_LAST_MSDU));
+	is_amsdu = !(is_first && is_last);
+
+	rfc1042 = hdr;
+
+	if (is_first) {
+		hdr_len = ieee80211_hdrlen(hdr->frame_control);
+		crypto_len = ath10k_htt_rx_crypto_param_len(ar, enctype);
+
+		rfc1042 += round_up(hdr_len, 4) +
+			   round_up(crypto_len, 4);
 	}
 
-	rxd = (void *)skb->data - sizeof(*rxd);
-	fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
-		 RX_MSDU_START_INFO1_DECAP_FORMAT);
-	enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0),
-		     RX_MPDU_START_INFO0_ENCRYPT_TYPE);
-	hdr = (struct ieee80211_hdr *)rxd->rx_hdr_status;
+	if (is_amsdu)
+		rfc1042 += sizeof(struct amsdu_subframe_hdr);
+
+	return rfc1042;
+}
+
+static void ath10k_htt_rx_h_undecap_eth(struct ath10k *ar,
+					struct sk_buff *msdu,
+					struct ieee80211_rx_status *status,
+					const u8 first_hdr[64],
+					enum htt_rx_mpdu_encrypt_type enctype)
+{
+	struct ieee80211_hdr *hdr;
+	struct ethhdr *eth;
+	size_t hdr_len;
+	void *rfc1042;
+	u8 da[ETH_ALEN];
+	u8 sa[ETH_ALEN];
+
+	/* Delivered decapped frame:
+	 * [eth header] <-- replaced with 802.11 hdr & rfc1042/llc
+	 * [payload]
+	 */
+
+	rfc1042 = ath10k_htt_rx_h_find_rfc1042(ar, msdu, enctype);
+	if (WARN_ON_ONCE(!rfc1042))
+		return;
+
+	/* pull decapped header and copy SA & DA */
+	eth = (struct ethhdr *)msdu->data;
+	ether_addr_copy(da, eth->h_dest);
+	ether_addr_copy(sa, eth->h_source);
+	skb_pull(msdu, sizeof(struct ethhdr));
+
+	/* push rfc1042/llc/snap */
+	memcpy(skb_push(msdu, sizeof(struct rfc1042_hdr)), rfc1042,
+	       sizeof(struct rfc1042_hdr));
+
+	/* push original 802.11 header */
+	hdr = (struct ieee80211_hdr *)first_hdr;
 	hdr_len = ieee80211_hdrlen(hdr->frame_control);
+	memcpy(skb_push(msdu, hdr_len), hdr, hdr_len);
 
-	skb->ip_summed = ath10k_htt_rx_get_csum_state(skb);
+	/* original 802.11 header has a different DA and in
+	 * case of 4addr it may also have different SA
+	 */
+	hdr = (struct ieee80211_hdr *)msdu->data;
+	ether_addr_copy(ieee80211_get_DA(hdr), da);
+	ether_addr_copy(ieee80211_get_SA(hdr), sa);
+}
 
-	switch (fmt) {
+static void ath10k_htt_rx_h_undecap_snap(struct ath10k *ar,
+					 struct sk_buff *msdu,
+					 struct ieee80211_rx_status *status,
+					 const u8 first_hdr[64])
+{
+	struct ieee80211_hdr *hdr;
+	size_t hdr_len;
+
+	/* Delivered decapped frame:
+	 * [amsdu header] <-- replaced with 802.11 hdr
+	 * [rfc1042/llc]
+	 * [payload]
+	 */
+
+	skb_pull(msdu, sizeof(struct amsdu_subframe_hdr));
+
+	hdr = (struct ieee80211_hdr *)first_hdr;
+	hdr_len = ieee80211_hdrlen(hdr->frame_control);
+	memcpy(skb_push(msdu, hdr_len), hdr, hdr_len);
+}
+
+static void ath10k_htt_rx_h_undecap(struct ath10k *ar,
+				    struct sk_buff *msdu,
+				    struct ieee80211_rx_status *status,
+				    u8 first_hdr[64],
+				    enum htt_rx_mpdu_encrypt_type enctype,
+				    bool is_decrypted)
+{
+	struct htt_rx_desc *rxd;
+	enum rx_msdu_decap_format decap;
+	struct ieee80211_hdr *hdr;
+
+	/* First msdu's decapped header:
+	 * [802.11 header] <-- padded to 4 bytes long
+	 * [crypto param] <-- padded to 4 bytes long
+	 * [amsdu header] <-- only if A-MSDU
+	 * [rfc1042/llc]
+	 *
+	 * Other (2nd, 3rd, ..) msdu's decapped header:
+	 * [amsdu header] <-- only if A-MSDU
+	 * [rfc1042/llc]
+	 */
+
+	rxd = (void *)msdu->data - sizeof(*rxd);
+	hdr = (void *)rxd->rx_hdr_status;
+	decap = MS(__le32_to_cpu(rxd->msdu_start.info1),
+		   RX_MSDU_START_INFO1_DECAP_FORMAT);
+
+	switch (decap) {
 	case RX_MSDU_DECAP_RAW:
-		/* remove trailing FCS */
-		skb_trim(skb, skb->len - FCS_LEN);
+		ath10k_htt_rx_h_undecap_raw(ar, msdu, status, enctype,
+					    is_decrypted);
 		break;
 	case RX_MSDU_DECAP_NATIVE_WIFI:
-		/* Pull decapped header */
-		hdr = (struct ieee80211_hdr *)skb->data;
-		hdr_len = ath10k_htt_rx_nwifi_hdrlen(hdr);
-		skb_pull(skb, hdr_len);
-
-		/* Push original header */
-		hdr = (struct ieee80211_hdr *)rxd->rx_hdr_status;
-		hdr_len = ieee80211_hdrlen(hdr->frame_control);
-		memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
+		ath10k_htt_rx_h_undecap_nwifi(ar, msdu, status, first_hdr);
 		break;
 	case RX_MSDU_DECAP_ETHERNET2_DIX:
-		/* strip ethernet header and insert decapped 802.11 header and
-		 * rfc1042 header */
-
-		rfc1042 = hdr;
-		rfc1042 += roundup(hdr_len, 4);
-		rfc1042 += roundup(ath10k_htt_rx_crypto_param_len(ar,
-					enctype), 4);
-
-		skb_pull(skb, sizeof(struct ethhdr));
-		memcpy(skb_push(skb, sizeof(struct rfc1042_hdr)),
-		       rfc1042, sizeof(struct rfc1042_hdr));
-		memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
+		ath10k_htt_rx_h_undecap_eth(ar, msdu, status, first_hdr, enctype);
 		break;
 	case RX_MSDU_DECAP_8023_SNAP_LLC:
-		/* remove A-MSDU subframe header and insert
-		 * decapped 802.11 header. rfc1042 header is already there */
-
-		skb_pull(skb, sizeof(struct amsdu_subframe_hdr));
-		memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
+		ath10k_htt_rx_h_undecap_snap(ar, msdu, status, first_hdr);
 		break;
 	}
-
-	ath10k_htt_rx_h_protected(htt, rx_status, skb, enctype, fmt, false);
-
-	ath10k_process_rx(htt->ar, rx_status, skb);
 }
 
 static int ath10k_htt_rx_get_csum_state(struct sk_buff *skb)
@@ -1127,10 +1103,128 @@
 	return CHECKSUM_UNNECESSARY;
 }
 
-static int ath10k_unchain_msdu(struct sk_buff *msdu_head)
+static void ath10k_htt_rx_h_csum_offload(struct sk_buff *msdu)
 {
-	struct sk_buff *next = msdu_head->next;
-	struct sk_buff *to_free = next;
+	msdu->ip_summed = ath10k_htt_rx_get_csum_state(msdu);
+}
+
+static void ath10k_htt_rx_h_mpdu(struct ath10k *ar,
+				 struct sk_buff_head *amsdu,
+				 struct ieee80211_rx_status *status)
+{
+	struct sk_buff *first;
+	struct sk_buff *last;
+	struct sk_buff *msdu;
+	struct htt_rx_desc *rxd;
+	struct ieee80211_hdr *hdr;
+	enum htt_rx_mpdu_encrypt_type enctype;
+	u8 first_hdr[64];
+	u8 *qos;
+	size_t hdr_len;
+	bool has_fcs_err;
+	bool has_crypto_err;
+	bool has_tkip_err;
+	bool has_peer_idx_invalid;
+	bool is_decrypted;
+	u32 attention;
+
+	if (skb_queue_empty(amsdu))
+		return;
+
+	first = skb_peek(amsdu);
+	rxd = (void *)first->data - sizeof(*rxd);
+
+	enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0),
+		     RX_MPDU_START_INFO0_ENCRYPT_TYPE);
+
+	/* First MSDU's Rx descriptor in an A-MSDU contains full 802.11
+	 * decapped header. It'll be used for undecapping of each MSDU.
+	 */
+	hdr = (void *)rxd->rx_hdr_status;
+	hdr_len = ieee80211_hdrlen(hdr->frame_control);
+	memcpy(first_hdr, hdr, hdr_len);
+
+	/* Each A-MSDU subframe will use the original header as the base and be
+	 * reported as a separate MSDU so strip the A-MSDU bit from QoS Ctl.
+	 */
+	hdr = (void *)first_hdr;
+	qos = ieee80211_get_qos_ctl(hdr);
+	qos[0] &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
+
+	/* Some attention flags are valid only in the last MSDU. */
+	last = skb_peek_tail(amsdu);
+	rxd = (void *)last->data - sizeof(*rxd);
+	attention = __le32_to_cpu(rxd->attention.flags);
+
+	has_fcs_err = !!(attention & RX_ATTENTION_FLAGS_FCS_ERR);
+	has_crypto_err = !!(attention & RX_ATTENTION_FLAGS_DECRYPT_ERR);
+	has_tkip_err = !!(attention & RX_ATTENTION_FLAGS_TKIP_MIC_ERR);
+	has_peer_idx_invalid = !!(attention & RX_ATTENTION_FLAGS_PEER_IDX_INVALID);
+
+	/* Note: If hardware captures an encrypted frame that it can't decrypt,
+	 * e.g. due to fcs error, missing peer or invalid key data it will
+	 * report the frame as raw.
+	 */
+	is_decrypted = (enctype != HTT_RX_MPDU_ENCRYPT_NONE &&
+			!has_fcs_err &&
+			!has_crypto_err &&
+			!has_peer_idx_invalid);
+
+	/* Clear per-MPDU flags while leaving per-PPDU flags intact. */
+	status->flag &= ~(RX_FLAG_FAILED_FCS_CRC |
+			  RX_FLAG_MMIC_ERROR |
+			  RX_FLAG_DECRYPTED |
+			  RX_FLAG_IV_STRIPPED |
+			  RX_FLAG_MMIC_STRIPPED);
+
+	if (has_fcs_err)
+		status->flag |= RX_FLAG_FAILED_FCS_CRC;
+
+	if (has_tkip_err)
+		status->flag |= RX_FLAG_MMIC_ERROR;
+
+	if (is_decrypted)
+		status->flag |= RX_FLAG_DECRYPTED |
+				RX_FLAG_IV_STRIPPED |
+				RX_FLAG_MMIC_STRIPPED;
+
+	skb_queue_walk(amsdu, msdu) {
+		ath10k_htt_rx_h_csum_offload(msdu);
+		ath10k_htt_rx_h_undecap(ar, msdu, status, first_hdr, enctype,
+					is_decrypted);
+
+		/* Undecapping involves copying the original 802.11 header back
+		 * to sk_buff. If frame is protected and hardware has decrypted
+		 * it then remove the protected bit.
+		 */
+		if (!is_decrypted)
+			continue;
+
+		hdr = (void *)msdu->data;
+		hdr->frame_control &= ~__cpu_to_le16(IEEE80211_FCTL_PROTECTED);
+	}
+}
+
+static void ath10k_htt_rx_h_deliver(struct ath10k *ar,
+				    struct sk_buff_head *amsdu,
+				    struct ieee80211_rx_status *status)
+{
+	struct sk_buff *msdu;
+
+	while ((msdu = __skb_dequeue(amsdu))) {
+		/* Setup per-MSDU flags */
+		if (skb_queue_empty(amsdu))
+			status->flag &= ~RX_FLAG_AMSDU_MORE;
+		else
+			status->flag |= RX_FLAG_AMSDU_MORE;
+
+		ath10k_process_rx(ar, status, msdu);
+	}
+}
+
+static int ath10k_unchain_msdu(struct sk_buff_head *amsdu)
+{
+	struct sk_buff *skb, *first;
 	int space;
 	int total_len = 0;
 
@@ -1141,99 +1235,142 @@
 	 * skb?
 	 */
 
-	msdu_head->next = NULL;
+	first = __skb_dequeue(amsdu);
 
 	/* Allocate total length all at once. */
-	while (next) {
-		total_len += next->len;
-		next = next->next;
-	}
+	skb_queue_walk(amsdu, skb)
+		total_len += skb->len;
 
-	space = total_len - skb_tailroom(msdu_head);
+	space = total_len - skb_tailroom(first);
 	if ((space > 0) &&
-	    (pskb_expand_head(msdu_head, 0, space, GFP_ATOMIC) < 0)) {
+	    (pskb_expand_head(first, 0, space, GFP_ATOMIC) < 0)) {
 		/* TODO:  bump some rx-oom error stat */
 		/* put it back together so we can free the
 		 * whole list at once.
 		 */
-		msdu_head->next = to_free;
+		__skb_queue_head(amsdu, first);
 		return -1;
 	}
 
 	/* Walk list again, copying contents into
 	 * msdu_head
 	 */
-	next = to_free;
-	while (next) {
-		skb_copy_from_linear_data(next, skb_put(msdu_head, next->len),
-					  next->len);
-		next = next->next;
+	while ((skb = __skb_dequeue(amsdu))) {
+		skb_copy_from_linear_data(skb, skb_put(first, skb->len),
+					  skb->len);
+		dev_kfree_skb_any(skb);
 	}
 
-	/* If here, we have consolidated skb.  Free the
-	 * fragments and pass the main skb on up the
-	 * stack.
-	 */
-	ath10k_htt_rx_free_msdu_chain(to_free);
+	__skb_queue_head(amsdu, first);
 	return 0;
 }
 
-static bool ath10k_htt_rx_amsdu_allowed(struct ath10k_htt *htt,
-					struct sk_buff *head,
-					bool channel_set,
-					u32 attention)
+static void ath10k_htt_rx_h_unchain(struct ath10k *ar,
+				    struct sk_buff_head *amsdu,
+				    bool chained)
 {
-	struct ath10k *ar = htt->ar;
+	struct sk_buff *first;
+	struct htt_rx_desc *rxd;
+	enum rx_msdu_decap_format decap;
 
-	if (head->len == 0) {
-		ath10k_dbg(ar, ATH10K_DBG_HTT,
-			   "htt rx dropping due to zero-len\n");
+	first = skb_peek(amsdu);
+	rxd = (void *)first->data - sizeof(*rxd);
+	decap = MS(__le32_to_cpu(rxd->msdu_start.info1),
+		   RX_MSDU_START_INFO1_DECAP_FORMAT);
+
+	if (!chained)
+		return;
+
+	/* FIXME: Current unchaining logic can only handle simple case of raw
+	 * msdu chaining. If decapping is other than raw the chaining may be
+	 * more complex and this isn't handled by the current code. Don't even
+	 * try re-constructing such frames - it'll be pretty much garbage.
+	 */
+	if (decap != RX_MSDU_DECAP_RAW ||
+	    skb_queue_len(amsdu) != 1 + rxd->frag_info.ring2_more_count) {
+		__skb_queue_purge(amsdu);
+		return;
+	}
+
+	ath10k_unchain_msdu(amsdu);
+}
+
+static bool ath10k_htt_rx_amsdu_allowed(struct ath10k *ar,
+					struct sk_buff_head *amsdu,
+					struct ieee80211_rx_status *rx_status)
+{
+	struct sk_buff *msdu;
+	struct htt_rx_desc *rxd;
+	bool is_mgmt;
+	bool has_fcs_err;
+
+	msdu = skb_peek(amsdu);
+	rxd = (void *)msdu->data - sizeof(*rxd);
+
+	/* FIXME: It might be a good idea to do some fuzzy-testing to drop
+	 * invalid/dangerous frames.
+	 */
+
+	if (!rx_status->freq) {
+		ath10k_warn(ar, "no channel configured; ignoring frame(s)!\n");
 		return false;
 	}
 
-	if (attention & RX_ATTENTION_FLAGS_DECRYPT_ERR) {
-		ath10k_dbg(ar, ATH10K_DBG_HTT,
-			   "htt rx dropping due to decrypt-err\n");
-		return false;
-	}
+	is_mgmt = !!(rxd->attention.flags &
+		     __cpu_to_le32(RX_ATTENTION_FLAGS_MGMT_TYPE));
+	has_fcs_err = !!(rxd->attention.flags &
+			 __cpu_to_le32(RX_ATTENTION_FLAGS_FCS_ERR));
 
-	if (!channel_set) {
-		ath10k_warn(ar, "no channel configured; ignoring frame!\n");
-		return false;
-	}
-
-	/* Skip mgmt frames while we handle this in WMI */
-	if (attention & RX_ATTENTION_FLAGS_MGMT_TYPE) {
+	/* Management frames are handled via WMI events. The pros of such
+	 * approach is that channel is explicitly provided in WMI events
+	 * whereas HTT doesn't provide channel information for Rxed frames.
+	 *
+	 * However some firmware revisions don't report corrupted frames via
+	 * WMI so don't drop them.
+	 */
+	if (is_mgmt && !has_fcs_err) {
 		ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx mgmt ctrl\n");
 		return false;
 	}
 
-	if (test_bit(ATH10K_CAC_RUNNING, &htt->ar->dev_flags)) {
-		ath10k_dbg(ar, ATH10K_DBG_HTT,
-			   "htt rx CAC running\n");
+	if (test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags)) {
+		ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx cac running\n");
 		return false;
 	}
 
 	return true;
 }
 
+static void ath10k_htt_rx_h_filter(struct ath10k *ar,
+				   struct sk_buff_head *amsdu,
+				   struct ieee80211_rx_status *rx_status)
+{
+	if (skb_queue_empty(amsdu))
+		return;
+
+	if (ath10k_htt_rx_amsdu_allowed(ar, amsdu, rx_status))
+		return;
+
+	__skb_queue_purge(amsdu);
+}
+
 static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
 				  struct htt_rx_indication *rx)
 {
 	struct ath10k *ar = htt->ar;
 	struct ieee80211_rx_status *rx_status = &htt->rx_status;
 	struct htt_rx_indication_mpdu_range *mpdu_ranges;
-	struct ieee80211_hdr *hdr;
+	struct sk_buff_head amsdu;
 	int num_mpdu_ranges;
-	u32 attention;
 	int fw_desc_len;
 	u8 *fw_desc;
-	bool channel_set;
-	int i, j;
-	int ret;
+	int i, ret, mpdu_count = 0;
 
 	lockdep_assert_held(&htt->rx_ring.lock);
 
+	if (htt->rx_confused)
+		return;
+
 	fw_desc_len = __le16_to_cpu(rx->prefix.fw_rx_desc_bytes);
 	fw_desc = (u8 *)&rx->fw_desc;
 
@@ -1241,85 +1378,33 @@
 			     HTT_RX_INDICATION_INFO1_NUM_MPDU_RANGES);
 	mpdu_ranges = htt_rx_ind_get_mpdu_ranges(rx);
 
-	/* Fill this once, while this is per-ppdu */
-	if (rx->ppdu.info0 & HTT_RX_INDICATION_INFO0_START_VALID) {
-		memset(rx_status, 0, sizeof(*rx_status));
-		rx_status->signal  = ATH10K_DEFAULT_NOISE_FLOOR +
-				     rx->ppdu.combined_rssi;
-	}
-
-	if (rx->ppdu.info0 & HTT_RX_INDICATION_INFO0_END_VALID) {
-		/* TSF available only in 32-bit */
-		rx_status->mactime = __le32_to_cpu(rx->ppdu.tsf) & 0xffffffff;
-		rx_status->flag |= RX_FLAG_MACTIME_END;
-	}
-
-	channel_set = ath10k_htt_rx_h_channel(htt->ar, rx_status);
-
-	if (channel_set) {
-		ath10k_htt_rx_h_rates(htt->ar, rx_status->band,
-				      rx->ppdu.info0,
-				      __le32_to_cpu(rx->ppdu.info1),
-				      __le32_to_cpu(rx->ppdu.info2),
-				      rx_status);
-	}
-
 	ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt rx ind: ",
 			rx, sizeof(*rx) +
 			(sizeof(struct htt_rx_indication_mpdu_range) *
 				num_mpdu_ranges));
 
-	for (i = 0; i < num_mpdu_ranges; i++) {
-		for (j = 0; j < mpdu_ranges[i].mpdu_count; j++) {
-			struct sk_buff *msdu_head, *msdu_tail;
+	for (i = 0; i < num_mpdu_ranges; i++)
+		mpdu_count += mpdu_ranges[i].mpdu_count;
 
-			attention = 0;
-			msdu_head = NULL;
-			msdu_tail = NULL;
-			ret = ath10k_htt_rx_amsdu_pop(htt,
-						      &fw_desc,
-						      &fw_desc_len,
-						      &msdu_head,
-						      &msdu_tail,
-						      &attention);
-
-			if (ret < 0) {
-				ath10k_warn(ar, "failed to pop amsdu from htt rx ring %d\n",
-					    ret);
-				ath10k_htt_rx_free_msdu_chain(msdu_head);
-				continue;
-			}
-
-			if (!ath10k_htt_rx_amsdu_allowed(htt, msdu_head,
-							 channel_set,
-							 attention)) {
-				ath10k_htt_rx_free_msdu_chain(msdu_head);
-				continue;
-			}
-
-			if (ret > 0 &&
-			    ath10k_unchain_msdu(msdu_head) < 0) {
-				ath10k_htt_rx_free_msdu_chain(msdu_head);
-				continue;
-			}
-
-			if (attention & RX_ATTENTION_FLAGS_FCS_ERR)
-				rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
-			else
-				rx_status->flag &= ~RX_FLAG_FAILED_FCS_CRC;
-
-			if (attention & RX_ATTENTION_FLAGS_TKIP_MIC_ERR)
-				rx_status->flag |= RX_FLAG_MMIC_ERROR;
-			else
-				rx_status->flag &= ~RX_FLAG_MMIC_ERROR;
-
-			hdr = ath10k_htt_rx_skb_get_hdr(msdu_head);
-
-			if (ath10k_htt_rx_hdr_is_amsdu(hdr))
-				ath10k_htt_rx_amsdu(htt, rx_status, msdu_head);
-			else
-				ath10k_htt_rx_msdu(htt, rx_status, msdu_head);
+	while (mpdu_count--) {
+		__skb_queue_head_init(&amsdu);
+		ret = ath10k_htt_rx_amsdu_pop(htt, &fw_desc,
+					      &fw_desc_len, &amsdu);
+		if (ret < 0) {
+			ath10k_warn(ar, "rx ring became corrupted: %d\n", ret);
+			__skb_queue_purge(&amsdu);
+			/* FIXME: It's probably a good idea to reboot the
+			 * device instead of leaving it inoperable.
+			 */
+			htt->rx_confused = true;
+			break;
 		}
+
+		ath10k_htt_rx_h_ppdu(ar, &amsdu, rx_status);
+		ath10k_htt_rx_h_unchain(ar, &amsdu, ret > 0);
+		ath10k_htt_rx_h_filter(ar, &amsdu, rx_status);
+		ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status);
+		ath10k_htt_rx_h_deliver(ar, &amsdu, rx_status);
 	}
 
 	tasklet_schedule(&htt->rx_replenish_task);
@@ -1329,30 +1414,20 @@
 				       struct htt_rx_fragment_indication *frag)
 {
 	struct ath10k *ar = htt->ar;
-	struct sk_buff *msdu_head, *msdu_tail;
-	enum htt_rx_mpdu_encrypt_type enctype;
-	struct htt_rx_desc *rxd;
-	enum rx_msdu_decap_format fmt;
 	struct ieee80211_rx_status *rx_status = &htt->rx_status;
-	struct ieee80211_hdr *hdr;
+	struct sk_buff_head amsdu;
 	int ret;
-	bool tkip_mic_err;
-	bool decrypt_err;
 	u8 *fw_desc;
-	int fw_desc_len, hdrlen, paramlen;
-	int trim;
-	u32 attention = 0;
+	int fw_desc_len;
 
 	fw_desc_len = __le16_to_cpu(frag->fw_rx_desc_bytes);
 	fw_desc = (u8 *)frag->fw_msdu_rx_desc;
 
-	msdu_head = NULL;
-	msdu_tail = NULL;
+	__skb_queue_head_init(&amsdu);
 
 	spin_lock_bh(&htt->rx_ring.lock);
 	ret = ath10k_htt_rx_amsdu_pop(htt, &fw_desc, &fw_desc_len,
-				      &msdu_head, &msdu_tail,
-				      &attention);
+				      &amsdu);
 	spin_unlock_bh(&htt->rx_ring.lock);
 
 	tasklet_schedule(&htt->rx_replenish_task);
@@ -1362,77 +1437,21 @@
 	if (ret) {
 		ath10k_warn(ar, "failed to pop amsdu from httr rx ring for fragmented rx %d\n",
 			    ret);
-		ath10k_htt_rx_free_msdu_chain(msdu_head);
+		__skb_queue_purge(&amsdu);
 		return;
 	}
 
-	/* FIXME: implement signal strength */
-	rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL;
-
-	hdr = (struct ieee80211_hdr *)msdu_head->data;
-	rxd = (void *)msdu_head->data - sizeof(*rxd);
-	tkip_mic_err = !!(attention & RX_ATTENTION_FLAGS_TKIP_MIC_ERR);
-	decrypt_err = !!(attention & RX_ATTENTION_FLAGS_DECRYPT_ERR);
-	fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
-		 RX_MSDU_START_INFO1_DECAP_FORMAT);
-
-	if (fmt != RX_MSDU_DECAP_RAW) {
-		ath10k_warn(ar, "we dont support non-raw fragmented rx yet\n");
-		dev_kfree_skb_any(msdu_head);
-		goto end;
+	if (skb_queue_len(&amsdu) != 1) {
+		ath10k_warn(ar, "failed to pop frag amsdu: too many msdus\n");
+		__skb_queue_purge(&amsdu);
+		return;
 	}
 
-	enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0),
-		     RX_MPDU_START_INFO0_ENCRYPT_TYPE);
-	ath10k_htt_rx_h_protected(htt, rx_status, msdu_head, enctype, fmt,
-				  true);
-	msdu_head->ip_summed = ath10k_htt_rx_get_csum_state(msdu_head);
+	ath10k_htt_rx_h_ppdu(ar, &amsdu, rx_status);
+	ath10k_htt_rx_h_filter(ar, &amsdu, rx_status);
+	ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status);
+	ath10k_htt_rx_h_deliver(ar, &amsdu, rx_status);
 
-	if (tkip_mic_err)
-		ath10k_warn(ar, "tkip mic error\n");
-
-	if (decrypt_err) {
-		ath10k_warn(ar, "decryption err in fragmented rx\n");
-		dev_kfree_skb_any(msdu_head);
-		goto end;
-	}
-
-	if (enctype != HTT_RX_MPDU_ENCRYPT_NONE) {
-		hdrlen = ieee80211_hdrlen(hdr->frame_control);
-		paramlen = ath10k_htt_rx_crypto_param_len(ar, enctype);
-
-		/* It is more efficient to move the header than the payload */
-		memmove((void *)msdu_head->data + paramlen,
-			(void *)msdu_head->data,
-			hdrlen);
-		skb_pull(msdu_head, paramlen);
-		hdr = (struct ieee80211_hdr *)msdu_head->data;
-	}
-
-	/* remove trailing FCS */
-	trim  = 4;
-
-	/* remove crypto trailer */
-	trim += ath10k_htt_rx_crypto_tail_len(ar, enctype);
-
-	/* last fragment of TKIP frags has MIC */
-	if (!ieee80211_has_morefrags(hdr->frame_control) &&
-	    enctype == HTT_RX_MPDU_ENCRYPT_TKIP_WPA)
-		trim += MICHAEL_MIC_LEN;
-
-	if (trim > msdu_head->len) {
-		ath10k_warn(ar, "htt rx fragment: trailer longer than the frame itself? drop\n");
-		dev_kfree_skb_any(msdu_head);
-		goto end;
-	}
-
-	skb_trim(msdu_head, msdu_head->len - trim);
-
-	ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt rx frag mpdu: ",
-			msdu_head->data, msdu_head->len);
-	ath10k_process_rx(htt->ar, rx_status, msdu_head);
-
-end:
 	if (fw_desc_len > 0) {
 		ath10k_dbg(ar, ATH10K_DBG_HTT,
 			   "expecting more fragmented rx in one indication %d\n",
diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c
index 5b7e42f..4bc51d8 100644
--- a/drivers/net/wireless/ath/ath10k/htt_tx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_tx.c
@@ -554,13 +554,14 @@
 	skb_cb->htt.txbuf->cmd_tx.len = __cpu_to_le16(msdu->len);
 	skb_cb->htt.txbuf->cmd_tx.id = __cpu_to_le16(msdu_id);
 	skb_cb->htt.txbuf->cmd_tx.frags_paddr = __cpu_to_le32(frags_paddr);
-	skb_cb->htt.txbuf->cmd_tx.peerid = __cpu_to_le32(HTT_INVALID_PEERID);
+	skb_cb->htt.txbuf->cmd_tx.peerid = __cpu_to_le16(HTT_INVALID_PEERID);
+	skb_cb->htt.txbuf->cmd_tx.freq = __cpu_to_le16(skb_cb->htt.freq);
 
 	trace_ath10k_htt_tx(ar, msdu_id, msdu->len, vdev_id, tid);
 	ath10k_dbg(ar, ATH10K_DBG_HTT,
-		   "htt tx flags0 %hhu flags1 %hu len %d id %hu frags_paddr %08x, msdu_paddr %08x vdev %hhu tid %hhu\n",
+		   "htt tx flags0 %hhu flags1 %hu len %d id %hu frags_paddr %08x, msdu_paddr %08x vdev %hhu tid %hhu freq %hu\n",
 		   flags0, flags1, msdu->len, msdu_id, frags_paddr,
-		   (u32)skb_cb->paddr, vdev_id, tid);
+		   (u32)skb_cb->paddr, vdev_id, tid, skb_cb->htt.freq);
 	ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt tx msdu: ",
 			msdu->data, msdu->len);
 	trace_ath10k_tx_hdr(ar, msdu->data, msdu->len);
diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h
index 392c250..dfedfd0 100644
--- a/drivers/net/wireless/ath/ath10k/hw.h
+++ b/drivers/net/wireless/ath/ath10k/hw.h
@@ -97,11 +97,13 @@
 #define TARGET_DMA_BURST_SIZE			0
 #define TARGET_MAC_AGGR_DELIM			0
 #define TARGET_AST_SKID_LIMIT			16
-#define TARGET_NUM_PEERS			16
+#define TARGET_NUM_STATIONS			16
+#define TARGET_NUM_PEERS			((TARGET_NUM_STATIONS) + \
+						 (TARGET_NUM_VDEVS))
 #define TARGET_NUM_OFFLOAD_PEERS		0
 #define TARGET_NUM_OFFLOAD_REORDER_BUFS         0
 #define TARGET_NUM_PEER_KEYS			2
-#define TARGET_NUM_TIDS		(2 * ((TARGET_NUM_PEERS) + (TARGET_NUM_VDEVS)))
+#define TARGET_NUM_TIDS				((TARGET_NUM_PEERS) * 2)
 #define TARGET_TX_CHAIN_MASK			(BIT(0) | BIT(1) | BIT(2))
 #define TARGET_RX_CHAIN_MASK			(BIT(0) | BIT(1) | BIT(2))
 #define TARGET_RX_TIMEOUT_LO_PRI		100
@@ -132,12 +134,15 @@
 #define TARGET_10X_DMA_BURST_SIZE		0
 #define TARGET_10X_MAC_AGGR_DELIM		0
 #define TARGET_10X_AST_SKID_LIMIT		16
-#define TARGET_10X_NUM_PEERS			(128 + (TARGET_10X_NUM_VDEVS))
-#define TARGET_10X_NUM_PEERS_MAX		128
+#define TARGET_10X_NUM_STATIONS			128
+#define TARGET_10X_NUM_PEERS			((TARGET_10X_NUM_STATIONS) + \
+						 (TARGET_10X_NUM_VDEVS))
 #define TARGET_10X_NUM_OFFLOAD_PEERS		0
 #define TARGET_10X_NUM_OFFLOAD_REORDER_BUFS	0
 #define TARGET_10X_NUM_PEER_KEYS		2
-#define TARGET_10X_NUM_TIDS			256
+#define TARGET_10X_NUM_TIDS_MAX			256
+#define TARGET_10X_NUM_TIDS			min((TARGET_10X_NUM_TIDS_MAX), \
+						    (TARGET_10X_NUM_PEERS) * 2)
 #define TARGET_10X_TX_CHAIN_MASK		(BIT(0) | BIT(1) | BIT(2))
 #define TARGET_10X_RX_CHAIN_MASK		(BIT(0) | BIT(1) | BIT(2))
 #define TARGET_10X_RX_TIMEOUT_LO_PRI		100
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 1245ac8..c400567 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -136,7 +136,9 @@
 		if (ret)
 			return ret;
 
+		spin_lock_bh(&ar->data_lock);
 		peer->keys[i] = arvif->wep_keys[i];
+		spin_unlock_bh(&ar->data_lock);
 	}
 
 	return 0;
@@ -173,12 +175,39 @@
 			ath10k_warn(ar, "failed to remove peer wep key %d: %d\n",
 				    i, ret);
 
+		spin_lock_bh(&ar->data_lock);
 		peer->keys[i] = NULL;
+		spin_unlock_bh(&ar->data_lock);
 	}
 
 	return first_errno;
 }
 
+bool ath10k_mac_is_peer_wep_key_set(struct ath10k *ar, const u8 *addr,
+				    u8 keyidx)
+{
+	struct ath10k_peer *peer;
+	int i;
+
+	lockdep_assert_held(&ar->data_lock);
+
+	/* We don't know which vdev this peer belongs to,
+	 * since WMI doesn't give us that information.
+	 *
+	 * FIXME: multi-bss needs to be handled.
+	 */
+	peer = ath10k_peer_find(ar, 0, addr);
+	if (!peer)
+		return false;
+
+	for (i = 0; i < ARRAY_SIZE(peer->keys); i++) {
+		if (peer->keys[i] && peer->keys[i]->keyidx == keyidx)
+			return true;
+	}
+
+	return false;
+}
+
 static int ath10k_clear_vdev_key(struct ath10k_vif *arvif,
 				 struct ieee80211_key_conf *key)
 {
@@ -326,6 +355,9 @@
 
 	lockdep_assert_held(&ar->conf_mutex);
 
+	if (ar->num_peers >= ar->max_num_peers)
+		return -ENOBUFS;
+
 	ret = ath10k_wmi_peer_create(ar, vdev_id, addr);
 	if (ret) {
 		ath10k_warn(ar, "failed to create wmi peer %pM on vdev %i: %i\n",
@@ -339,9 +371,8 @@
 			    addr, vdev_id, ret);
 		return ret;
 	}
-	spin_lock_bh(&ar->data_lock);
+
 	ar->num_peers++;
-	spin_unlock_bh(&ar->data_lock);
 
 	return 0;
 }
@@ -391,15 +422,11 @@
 	return 0;
 }
 
-static int  ath10k_mac_set_rts(struct ath10k_vif *arvif, u32 value)
+static int ath10k_mac_set_rts(struct ath10k_vif *arvif, u32 value)
 {
 	struct ath10k *ar = arvif->ar;
 	u32 vdev_param;
 
-	if (value != 0xFFFFFFFF)
-		value = min_t(u32, arvif->ar->hw->wiphy->rts_threshold,
-			      ATH10K_RTS_MAX);
-
 	vdev_param = ar->wmi.vdev_param->rts_threshold;
 	return ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, value);
 }
@@ -432,9 +459,7 @@
 	if (ret)
 		return ret;
 
-	spin_lock_bh(&ar->data_lock);
 	ar->num_peers--;
-	spin_unlock_bh(&ar->data_lock);
 
 	return 0;
 }
@@ -471,8 +496,10 @@
 		list_del(&peer->list);
 		kfree(peer);
 	}
-	ar->num_peers = 0;
 	spin_unlock_bh(&ar->data_lock);
+
+	ar->num_peers = 0;
+	ar->num_stations = 0;
 }
 
 /************************/
@@ -1997,6 +2024,18 @@
 	}
 }
 
+static bool ath10k_mac_need_offchan_tx_work(struct ath10k *ar)
+{
+	/* FIXME: Not really sure since when the behaviour changed. At some
+	 * point new firmware stopped requiring creation of peer entries for
+	 * offchannel tx (and actually creating them causes issues with wmi-htc
+	 * tx credit replenishment and reliability). Assuming it's at least 3.4
+	 * because that's when the `freq` was introduced to TX_FRM HTT command.
+	 */
+	return !(ar->htt.target_version_major >= 3 &&
+		 ar->htt.target_version_minor >= 4);
+}
+
 static void ath10k_tx_htt(struct ath10k *ar, struct sk_buff *skb)
 {
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
@@ -2172,10 +2211,10 @@
 	case ATH10K_SCAN_IDLE:
 		break;
 	case ATH10K_SCAN_RUNNING:
-	case ATH10K_SCAN_ABORTING:
 		if (ar->scan.is_roc)
 			ieee80211_remain_on_channel_expired(ar->hw);
-		else
+	case ATH10K_SCAN_ABORTING:
+		if (!ar->scan.is_roc)
 			ieee80211_scan_completed(ar->hw,
 						 (ar->scan.state ==
 						  ATH10K_SCAN_ABORTING));
@@ -2341,16 +2380,21 @@
 
 	if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) {
 		spin_lock_bh(&ar->data_lock);
-		ATH10K_SKB_CB(skb)->htt.is_offchan = true;
+		ATH10K_SKB_CB(skb)->htt.freq = ar->scan.roc_freq;
 		ATH10K_SKB_CB(skb)->vdev_id = ar->scan.vdev_id;
 		spin_unlock_bh(&ar->data_lock);
 
-		ath10k_dbg(ar, ATH10K_DBG_MAC, "queued offchannel skb %p\n",
-			   skb);
+		if (ath10k_mac_need_offchan_tx_work(ar)) {
+			ATH10K_SKB_CB(skb)->htt.freq = 0;
+			ATH10K_SKB_CB(skb)->htt.is_offchan = true;
 
-		skb_queue_tail(&ar->offchan_tx_queue, skb);
-		ieee80211_queue_work(hw, &ar->offchan_tx_work);
-		return;
+			ath10k_dbg(ar, ATH10K_DBG_MAC, "queued offchannel skb %p\n",
+				   skb);
+
+			skb_queue_tail(&ar->offchan_tx_queue, skb);
+			ieee80211_queue_work(hw, &ar->offchan_tx_work);
+			return;
+		}
 	}
 
 	ath10k_tx_htt(ar, skb);
@@ -2414,12 +2458,28 @@
 	return 0;
 }
 
+static void ath10k_check_chain_mask(struct ath10k *ar, u32 cm, const char *dbg)
+{
+	/* It is not clear that allowing gaps in chainmask
+	 * is helpful.  Probably it will not do what user
+	 * is hoping for, so warn in that case.
+	 */
+	if (cm == 15 || cm == 7 || cm == 3 || cm == 1 || cm == 0)
+		return;
+
+	ath10k_warn(ar, "mac %s antenna chainmask may be invalid: 0x%x.  Suggested values: 15, 7, 3, 1 or 0.\n",
+		    dbg, cm);
+}
+
 static int __ath10k_set_antenna(struct ath10k *ar, u32 tx_ant, u32 rx_ant)
 {
 	int ret;
 
 	lockdep_assert_held(&ar->conf_mutex);
 
+	ath10k_check_chain_mask(ar, tx_ant, "tx");
+	ath10k_check_chain_mask(ar, rx_ant, "rx");
+
 	ar->cfg_tx_chainmask = tx_ant;
 	ar->cfg_rx_chainmask = rx_ant;
 
@@ -2782,6 +2842,17 @@
 	return ret;
 }
 
+static u32 get_nss_from_chainmask(u16 chain_mask)
+{
+	if ((chain_mask & 0x15) == 0x15)
+		return 4;
+	else if ((chain_mask & 0x7) == 0x7)
+		return 3;
+	else if ((chain_mask & 0x3) == 0x3)
+		return 2;
+	return 1;
+}
+
 /*
  * TODO:
  * Figure out how to handle WMI_VDEV_SUBTYPE_P2P_DEVICE,
@@ -2914,6 +2985,20 @@
 		goto err_vdev_delete;
 	}
 
+	if (ar->cfg_tx_chainmask) {
+		u16 nss = get_nss_from_chainmask(ar->cfg_tx_chainmask);
+
+		vdev_param = ar->wmi.vdev_param->nss;
+		ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
+						nss);
+		if (ret) {
+			ath10k_warn(ar, "failed to set vdev %i chainmask 0x%x, nss %i: %d\n",
+				    arvif->vdev_id, ar->cfg_tx_chainmask, nss,
+				    ret);
+			goto err_vdev_delete;
+		}
+	}
+
 	if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
 		ret = ath10k_peer_create(ar, arvif->vdev_id, vif->addr);
 		if (ret) {
@@ -3014,10 +3099,10 @@
 	struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
 	int ret;
 
-	mutex_lock(&ar->conf_mutex);
-
 	cancel_work_sync(&arvif->wep_key_work);
 
+	mutex_lock(&ar->conf_mutex);
+
 	spin_lock_bh(&ar->data_lock);
 	ath10k_mac_vif_beacon_cleanup(arvif);
 	spin_unlock_bh(&ar->data_lock);
@@ -3511,6 +3596,37 @@
 	mutex_unlock(&ar->conf_mutex);
 }
 
+static int ath10k_mac_inc_num_stations(struct ath10k_vif *arvif)
+{
+	struct ath10k *ar = arvif->ar;
+
+	lockdep_assert_held(&ar->conf_mutex);
+
+	if (arvif->vdev_type != WMI_VDEV_TYPE_AP &&
+	    arvif->vdev_type != WMI_VDEV_TYPE_IBSS)
+		return 0;
+
+	if (ar->num_stations >= ar->max_num_stations)
+		return -ENOBUFS;
+
+	ar->num_stations++;
+
+	return 0;
+}
+
+static void ath10k_mac_dec_num_stations(struct ath10k_vif *arvif)
+{
+	struct ath10k *ar = arvif->ar;
+
+	lockdep_assert_held(&ar->conf_mutex);
+
+	if (arvif->vdev_type != WMI_VDEV_TYPE_AP &&
+	    arvif->vdev_type != WMI_VDEV_TYPE_IBSS)
+		return;
+
+	ar->num_stations--;
+}
+
 static int ath10k_sta_state(struct ieee80211_hw *hw,
 			    struct ieee80211_vif *vif,
 			    struct ieee80211_sta *sta,
@@ -3520,7 +3636,6 @@
 	struct ath10k *ar = hw->priv;
 	struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
 	struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
-	int max_num_peers;
 	int ret = 0;
 
 	if (old_state == IEEE80211_STA_NOTEXIST &&
@@ -3542,26 +3657,26 @@
 		/*
 		 * New station addition.
 		 */
-		if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features))
-			max_num_peers = TARGET_10X_NUM_PEERS_MAX - 1;
-		else
-			max_num_peers = TARGET_NUM_PEERS;
+		ath10k_dbg(ar, ATH10K_DBG_MAC,
+			   "mac vdev %d peer create %pM (new sta) sta %d / %d peer %d / %d\n",
+			   arvif->vdev_id, sta->addr,
+			   ar->num_stations + 1, ar->max_num_stations,
+			   ar->num_peers + 1, ar->max_num_peers);
 
-		if (ar->num_peers >= max_num_peers) {
-			ath10k_warn(ar, "number of peers exceeded: peers number %d (max peers %d)\n",
-				    ar->num_peers, max_num_peers);
-			ret = -ENOBUFS;
+		ret = ath10k_mac_inc_num_stations(arvif);
+		if (ret) {
+			ath10k_warn(ar, "refusing to associate station: too many connected already (%d)\n",
+				    ar->max_num_stations);
 			goto exit;
 		}
 
-		ath10k_dbg(ar, ATH10K_DBG_MAC,
-			   "mac vdev %d peer create %pM (new sta) num_peers %d\n",
-			   arvif->vdev_id, sta->addr, ar->num_peers);
-
 		ret = ath10k_peer_create(ar, arvif->vdev_id, sta->addr);
-		if (ret)
+		if (ret) {
 			ath10k_warn(ar, "failed to add peer %pM for vdev %d when adding a new sta: %i\n",
 				    sta->addr, arvif->vdev_id, ret);
+			ath10k_mac_dec_num_stations(arvif);
+			goto exit;
+		}
 
 		if (vif->type == NL80211_IFTYPE_STATION) {
 			WARN_ON(arvif->is_started);
@@ -3572,6 +3687,7 @@
 					    arvif->vdev_id, ret);
 				WARN_ON(ath10k_peer_delete(ar, arvif->vdev_id,
 							   sta->addr));
+				ath10k_mac_dec_num_stations(arvif);
 				goto exit;
 			}
 
@@ -3602,6 +3718,7 @@
 			ath10k_warn(ar, "failed to delete peer %pM for vdev %d: %i\n",
 				    sta->addr, arvif->vdev_id, ret);
 
+		ath10k_mac_dec_num_stations(arvif);
 	} else if (old_state == IEEE80211_STA_AUTH &&
 		   new_state == IEEE80211_STA_ASSOC &&
 		   (vif->type == NL80211_IFTYPE_AP ||
@@ -3790,6 +3907,8 @@
 	if (ret)
 		goto exit;
 
+	duration = max(duration, WMI_SCAN_CHAN_MIN_TIME_MSEC);
+
 	memset(&arg, 0, sizeof(arg));
 	ath10k_wmi_start_scan_init(ar, &arg);
 	arg.vdev_id = arvif->vdev_id;
@@ -4106,6 +4225,10 @@
 	u32 legacy = 0x00ff;
 	u8 ht = 0xff, i;
 	u16 vht = 0x3ff;
+	u16 nrf = ar->num_rf_chains;
+
+	if (ar->cfg_tx_chainmask)
+		nrf = get_nss_from_chainmask(ar->cfg_tx_chainmask);
 
 	switch (band) {
 	case IEEE80211_BAND_2GHZ:
@@ -4121,11 +4244,11 @@
 	if (mask->control[band].legacy != legacy)
 		return false;
 
-	for (i = 0; i < ar->num_rf_chains; i++)
+	for (i = 0; i < nrf; i++)
 		if (mask->control[band].ht_mcs[i] != ht)
 			return false;
 
-	for (i = 0; i < ar->num_rf_chains; i++)
+	for (i = 0; i < nrf; i++)
 		if (mask->control[band].vht_mcs[i] != vht)
 			return false;
 
@@ -4376,6 +4499,9 @@
 	u8 fixed_nss = ar->num_rf_chains;
 	u8 force_sgi;
 
+	if (ar->cfg_tx_chainmask)
+		fixed_nss = get_nss_from_chainmask(ar->cfg_tx_chainmask);
+
 	force_sgi = mask->control[band].gi;
 	if (force_sgi == NL80211_TXRATE_FORCE_LGI)
 		return -EINVAL;
@@ -4905,10 +5031,6 @@
 			IEEE80211_HW_AP_LINK_PS |
 			IEEE80211_HW_SPECTRUM_MGMT;
 
-	/* MSDU can have HTT TX fragment pushed in front. The additional 4
-	 * bytes is used for padding/alignment if necessary. */
-	ar->hw->extra_tx_headroom += sizeof(struct htt_data_tx_desc_frag)*2 + 4;
-
 	ar->hw->wiphy->features |= NL80211_FEATURE_STATIC_SMPS;
 
 	if (ar->ht_cap_info & WMI_HT_CAP_DYNAMIC_SMPS)
diff --git a/drivers/net/wireless/ath/ath10k/mac.h b/drivers/net/wireless/ath/ath10k/mac.h
index 4e3c989..6829611 100644
--- a/drivers/net/wireless/ath/ath10k/mac.h
+++ b/drivers/net/wireless/ath/ath10k/mac.h
@@ -21,6 +21,8 @@
 #include <net/mac80211.h>
 #include "core.h"
 
+#define WEP_KEYID_SHIFT 6
+
 struct ath10k_generic_iter {
 	struct ath10k *ar;
 	int ret;
@@ -41,6 +43,8 @@
 void ath10k_halt(struct ath10k *ar);
 void ath10k_mac_vif_beacon_free(struct ath10k_vif *arvif);
 void ath10k_drain_tx(struct ath10k *ar);
+bool ath10k_mac_is_peer_wep_key_set(struct ath10k *ar, const u8 *addr,
+				    u8 keyidx);
 
 static inline struct ath10k_vif *ath10k_vif_to_arvif(struct ieee80211_vif *vif)
 {
diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
index 3a6b8a5..7abb836 100644
--- a/drivers/net/wireless/ath/ath10k/pci.c
+++ b/drivers/net/wireless/ath/ath10k/pci.c
@@ -823,20 +823,24 @@
 	struct ath10k *ar = ce_state->ar;
 	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
 	struct ath10k_hif_cb *cb = &ar_pci->msg_callbacks_current;
-	void *transfer_context;
+	struct sk_buff_head list;
+	struct sk_buff *skb;
 	u32 ce_data;
 	unsigned int nbytes;
 	unsigned int transfer_id;
 
-	while (ath10k_ce_completed_send_next(ce_state, &transfer_context,
-					     &ce_data, &nbytes,
-					     &transfer_id) == 0) {
+	__skb_queue_head_init(&list);
+	while (ath10k_ce_completed_send_next(ce_state, (void **)&skb, &ce_data,
+					     &nbytes, &transfer_id) == 0) {
 		/* no need to call tx completion for NULL pointers */
-		if (transfer_context == NULL)
+		if (skb == NULL)
 			continue;
 
-		cb->tx_completion(ar, transfer_context, transfer_id);
+		__skb_queue_tail(&list, skb);
 	}
+
+	while ((skb = __skb_dequeue(&list)))
+		cb->tx_completion(ar, skb);
 }
 
 /* Called by lower (CE) layer when data is received from the Target. */
@@ -847,12 +851,14 @@
 	struct ath10k_pci_pipe *pipe_info =  &ar_pci->pipe_info[ce_state->id];
 	struct ath10k_hif_cb *cb = &ar_pci->msg_callbacks_current;
 	struct sk_buff *skb;
+	struct sk_buff_head list;
 	void *transfer_context;
 	u32 ce_data;
 	unsigned int nbytes, max_nbytes;
 	unsigned int transfer_id;
 	unsigned int flags;
 
+	__skb_queue_head_init(&list);
 	while (ath10k_ce_completed_recv_next(ce_state, &transfer_context,
 					     &ce_data, &nbytes, &transfer_id,
 					     &flags) == 0) {
@@ -869,13 +875,16 @@
 		}
 
 		skb_put(skb, nbytes);
+		__skb_queue_tail(&list, skb);
+	}
 
+	while ((skb = __skb_dequeue(&list))) {
 		ath10k_dbg(ar, ATH10K_DBG_PCI, "pci rx ce pipe %d len %d\n",
 			   ce_state->id, skb->len);
 		ath10k_dbg_dump(ar, ATH10K_DBG_PCI_DUMP, NULL, "pci rx: ",
 				skb->data, skb->len);
 
-		cb->rx_completion(ar, skb, pipe_info->pipe_num);
+		cb->rx_completion(ar, skb);
 	}
 
 	ath10k_pci_rx_post_pipe(pipe_info);
@@ -1263,7 +1272,7 @@
 		id = MS(__le16_to_cpu(ce_desc[i].flags),
 			CE_DESC_FLAGS_META_DATA);
 
-		ar_pci->msg_callbacks_current.tx_completion(ar, skb, id);
+		ar_pci->msg_callbacks_current.tx_completion(ar, skb);
 	}
 }
 
@@ -1988,6 +1997,7 @@
 static const struct ath10k_hif_ops ath10k_pci_hif_ops = {
 	.tx_sg			= ath10k_pci_hif_tx_sg,
 	.diag_read		= ath10k_pci_hif_diag_read,
+	.diag_write		= ath10k_pci_diag_write_mem,
 	.exchange_bmi_msg	= ath10k_pci_hif_exchange_bmi_msg,
 	.start			= ath10k_pci_hif_start,
 	.stop			= ath10k_pci_hif_stop,
@@ -1998,6 +2008,8 @@
 	.get_free_queue_number	= ath10k_pci_hif_get_free_queue_number,
 	.power_up		= ath10k_pci_hif_power_up,
 	.power_down		= ath10k_pci_hif_power_down,
+	.read32			= ath10k_pci_read32,
+	.write32		= ath10k_pci_write32,
 #ifdef CONFIG_PM
 	.suspend		= ath10k_pci_hif_suspend,
 	.resume			= ath10k_pci_hif_resume,
diff --git a/drivers/net/wireless/ath/ath10k/trace.h b/drivers/net/wireless/ath/ath10k/trace.h
index ceea566..b289378 100644
--- a/drivers/net/wireless/ath/ath10k/trace.h
+++ b/drivers/net/wireless/ath/ath10k/trace.h
@@ -21,9 +21,11 @@
 #include "core.h"
 
 #if !defined(_TRACE_H_)
-static inline u32 ath10k_frm_hdr_len(void *buf)
+static inline u32 ath10k_frm_hdr_len(const void *buf)
 {
-	return ieee80211_hdrlen(((struct ieee80211_hdr *)buf)->frame_control);
+	const struct ieee80211_hdr *hdr = buf;
+
+	return ieee80211_hdrlen(hdr->frame_control);
 }
 #endif
 
@@ -145,7 +147,8 @@
 );
 
 TRACE_EVENT(ath10k_wmi_cmd,
-	TP_PROTO(struct ath10k *ar, int id, void *buf, size_t buf_len, int ret),
+	TP_PROTO(struct ath10k *ar, int id, const void *buf, size_t buf_len,
+		 int ret),
 
 	TP_ARGS(ar, id, buf, buf_len, ret),
 
@@ -178,7 +181,7 @@
 );
 
 TRACE_EVENT(ath10k_wmi_event,
-	TP_PROTO(struct ath10k *ar, int id, void *buf, size_t buf_len),
+	TP_PROTO(struct ath10k *ar, int id, const void *buf, size_t buf_len),
 
 	TP_ARGS(ar, id, buf, buf_len),
 
@@ -208,7 +211,7 @@
 );
 
 TRACE_EVENT(ath10k_htt_stats,
-	TP_PROTO(struct ath10k *ar, void *buf, size_t buf_len),
+	TP_PROTO(struct ath10k *ar, const void *buf, size_t buf_len),
 
 	TP_ARGS(ar, buf, buf_len),
 
@@ -235,7 +238,7 @@
 );
 
 TRACE_EVENT(ath10k_wmi_dbglog,
-	TP_PROTO(struct ath10k *ar, void *buf, size_t buf_len),
+	TP_PROTO(struct ath10k *ar, const void *buf, size_t buf_len),
 
 	TP_ARGS(ar, buf, buf_len),
 
@@ -262,7 +265,7 @@
 );
 
 TRACE_EVENT(ath10k_htt_pktlog,
-	    TP_PROTO(struct ath10k *ar, void *buf, u16 buf_len),
+	    TP_PROTO(struct ath10k *ar, const void *buf, u16 buf_len),
 
 	TP_ARGS(ar, buf, buf_len),
 
@@ -349,7 +352,7 @@
 );
 
 DECLARE_EVENT_CLASS(ath10k_hdr_event,
-		    TP_PROTO(struct ath10k *ar, void *data, size_t len),
+		    TP_PROTO(struct ath10k *ar, const void *data, size_t len),
 
 	TP_ARGS(ar, data, len),
 
@@ -376,7 +379,7 @@
 );
 
 DECLARE_EVENT_CLASS(ath10k_payload_event,
-		    TP_PROTO(struct ath10k *ar, void *data, size_t len),
+		    TP_PROTO(struct ath10k *ar, const void *data, size_t len),
 
 	TP_ARGS(ar, data, len),
 
@@ -404,27 +407,27 @@
 );
 
 DEFINE_EVENT(ath10k_hdr_event, ath10k_tx_hdr,
-	     TP_PROTO(struct ath10k *ar, void *data, size_t len),
+	     TP_PROTO(struct ath10k *ar, const void *data, size_t len),
 	     TP_ARGS(ar, data, len)
 );
 
 DEFINE_EVENT(ath10k_payload_event, ath10k_tx_payload,
-	     TP_PROTO(struct ath10k *ar, void *data, size_t len),
+	     TP_PROTO(struct ath10k *ar, const void *data, size_t len),
 	     TP_ARGS(ar, data, len)
 );
 
 DEFINE_EVENT(ath10k_hdr_event, ath10k_rx_hdr,
-	     TP_PROTO(struct ath10k *ar, void *data, size_t len),
+	     TP_PROTO(struct ath10k *ar, const void *data, size_t len),
 	     TP_ARGS(ar, data, len)
 );
 
 DEFINE_EVENT(ath10k_payload_event, ath10k_rx_payload,
-	     TP_PROTO(struct ath10k *ar, void *data, size_t len),
+	     TP_PROTO(struct ath10k *ar, const void *data, size_t len),
 	     TP_ARGS(ar, data, len)
 );
 
 TRACE_EVENT(ath10k_htt_rx_desc,
-	    TP_PROTO(struct ath10k *ar, void *data, size_t len),
+	    TP_PROTO(struct ath10k *ar, const void *data, size_t len),
 
 	TP_ARGS(ar, data, len),
 
diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c
index c2bc828..c0f3e4d 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.c
+++ b/drivers/net/wireless/ath/ath10k/wmi.c
@@ -1113,6 +1113,40 @@
 	return rate_idx;
 }
 
+/* If keys are configured, HW decrypts all frames
+ * with protected bit set. Mark such frames as decrypted.
+ */
+static void ath10k_wmi_handle_wep_reauth(struct ath10k *ar,
+					 struct sk_buff *skb,
+					 struct ieee80211_rx_status *status)
+{
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+	unsigned int hdrlen;
+	bool peer_key;
+	u8 *addr, keyidx;
+
+	if (!ieee80211_is_auth(hdr->frame_control) ||
+	    !ieee80211_has_protected(hdr->frame_control))
+		return;
+
+	hdrlen = ieee80211_hdrlen(hdr->frame_control);
+	if (skb->len < (hdrlen + IEEE80211_WEP_IV_LEN))
+		return;
+
+	keyidx = skb->data[hdrlen + (IEEE80211_WEP_IV_LEN - 1)] >> WEP_KEYID_SHIFT;
+	addr = ieee80211_get_SA(hdr);
+
+	spin_lock_bh(&ar->data_lock);
+	peer_key = ath10k_mac_is_peer_wep_key_set(ar, addr, keyidx);
+	spin_unlock_bh(&ar->data_lock);
+
+	if (peer_key) {
+		ath10k_dbg(ar, ATH10K_DBG_MAC,
+			   "mac wep key present for peer %pM\n", addr);
+		status->flag |= RX_FLAG_DECRYPTED;
+	}
+}
+
 static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb)
 {
 	struct wmi_mgmt_rx_event_v1 *ev_v1;
@@ -1166,8 +1200,11 @@
 		return 0;
 	}
 
-	if (rx_status & WMI_RX_STATUS_ERR_CRC)
-		status->flag |= RX_FLAG_FAILED_FCS_CRC;
+	if (rx_status & WMI_RX_STATUS_ERR_CRC) {
+		dev_kfree_skb(skb);
+		return 0;
+	}
+
 	if (rx_status & WMI_RX_STATUS_ERR_MIC)
 		status->flag |= RX_FLAG_MMIC_ERROR;
 
@@ -1200,6 +1237,8 @@
 	hdr = (struct ieee80211_hdr *)skb->data;
 	fc = le16_to_cpu(hdr->frame_control);
 
+	ath10k_wmi_handle_wep_reauth(ar, skb, status);
+
 	/* FW delivers WEP Shared Auth frame with Protected Bit set and
 	 * encrypted payload. However in case of PMF it delivers decrypted
 	 * frames with Protected Bit set. */
@@ -2261,7 +2300,7 @@
 	/* the last byte is always reserved for the null character */
 	buf[i] = '\0';
 
-	ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi event debug print '%s'\n", buf);
+	ath10k_dbg(ar, ATH10K_DBG_WMI_PRINT, "wmi print '%s'\n", buf);
 }
 
 static void ath10k_wmi_event_pdev_qvit(struct ath10k *ar, struct sk_buff *skb)
@@ -2418,6 +2457,7 @@
 	arg->eeprom_rd = ev->hal_reg_capabilities.eeprom_rd;
 	arg->num_mem_reqs = ev->num_mem_reqs;
 	arg->service_map = ev->wmi_service_bitmap;
+	arg->service_map_len = sizeof(ev->wmi_service_bitmap);
 
 	n = min_t(size_t, __le32_to_cpu(arg->num_mem_reqs),
 		  ARRAY_SIZE(arg->mem_reqs));
@@ -2452,6 +2492,7 @@
 	arg->eeprom_rd = ev->hal_reg_capabilities.eeprom_rd;
 	arg->num_mem_reqs = ev->num_mem_reqs;
 	arg->service_map = ev->wmi_service_bitmap;
+	arg->service_map_len = sizeof(ev->wmi_service_bitmap);
 
 	n = min_t(size_t, __le32_to_cpu(arg->num_mem_reqs),
 		  ARRAY_SIZE(arg->mem_reqs));
@@ -2470,15 +2511,18 @@
 {
 	struct wmi_svc_rdy_ev_arg arg = {};
 	u32 num_units, req_id, unit_size, num_mem_reqs, num_unit_info, i;
-	DECLARE_BITMAP(svc_bmap, WMI_SERVICE_MAX) = {};
 	int ret;
 
+	memset(&ar->wmi.svc_map, 0, sizeof(ar->wmi.svc_map));
+
 	if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
 		ret = ath10k_wmi_10x_pull_svc_rdy_ev(skb, &arg);
-		wmi_10x_svc_map(arg.service_map, svc_bmap);
+		wmi_10x_svc_map(arg.service_map, ar->wmi.svc_map,
+				arg.service_map_len);
 	} else {
 		ret = ath10k_wmi_main_pull_svc_rdy_ev(skb, &arg);
-		wmi_main_svc_map(arg.service_map, svc_bmap);
+		wmi_main_svc_map(arg.service_map, ar->wmi.svc_map,
+				 arg.service_map_len);
 	}
 
 	if (ret) {
@@ -2500,9 +2544,8 @@
 	ar->num_rf_chains = __le32_to_cpu(arg.num_rf_chains);
 	ar->ath_common.regulatory.current_rd = __le32_to_cpu(arg.eeprom_rd);
 
-	ath10k_debug_read_service_map(ar, svc_bmap, sizeof(svc_bmap));
 	ath10k_dbg_dump(ar, ATH10K_DBG_WMI, NULL, "wmi svc: ",
-			arg.service_map, sizeof(arg.service_map));
+			arg.service_map, arg.service_map_len);
 
 	/* only manually set fw features when not using FW IE format */
 	if (ar->fw_api == 1 && ar->fw_version_build > 636)
@@ -3142,7 +3185,7 @@
 	u32 len, val;
 
 	config.num_vdevs = __cpu_to_le32(TARGET_NUM_VDEVS);
-	config.num_peers = __cpu_to_le32(TARGET_NUM_PEERS + TARGET_NUM_VDEVS);
+	config.num_peers = __cpu_to_le32(TARGET_NUM_PEERS);
 	config.num_offload_peers = __cpu_to_le32(TARGET_NUM_OFFLOAD_PEERS);
 
 	config.num_offload_reorder_bufs =
diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h
index a38d788..2139192 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.h
+++ b/drivers/net/wireless/ath/ath10k/wmi.h
@@ -222,128 +222,131 @@
 #undef SVCSTR
 }
 
-#define WMI_SERVICE_IS_ENABLED(wmi_svc_bmap, svc_id) \
-	(__le32_to_cpu((wmi_svc_bmap)[(svc_id)/(sizeof(u32))]) & \
+#define WMI_SERVICE_IS_ENABLED(wmi_svc_bmap, svc_id, len) \
+	((svc_id) < (len) && \
+	 __le32_to_cpu((wmi_svc_bmap)[(svc_id)/(sizeof(u32))]) & \
 	 BIT((svc_id)%(sizeof(u32))))
 
-#define SVCMAP(x, y) \
+#define SVCMAP(x, y, len) \
 	do { \
-		if (WMI_SERVICE_IS_ENABLED((in), (x))) \
+		if (WMI_SERVICE_IS_ENABLED((in), (x), (len))) \
 			__set_bit(y, out); \
 	} while (0)
 
-static inline void wmi_10x_svc_map(const __le32 *in, unsigned long *out)
+static inline void wmi_10x_svc_map(const __le32 *in, unsigned long *out,
+				   size_t len)
 {
 	SVCMAP(WMI_10X_SERVICE_BEACON_OFFLOAD,
-	       WMI_SERVICE_BEACON_OFFLOAD);
+	       WMI_SERVICE_BEACON_OFFLOAD, len);
 	SVCMAP(WMI_10X_SERVICE_SCAN_OFFLOAD,
-	       WMI_SERVICE_SCAN_OFFLOAD);
+	       WMI_SERVICE_SCAN_OFFLOAD, len);
 	SVCMAP(WMI_10X_SERVICE_ROAM_OFFLOAD,
-	       WMI_SERVICE_ROAM_OFFLOAD);
+	       WMI_SERVICE_ROAM_OFFLOAD, len);
 	SVCMAP(WMI_10X_SERVICE_BCN_MISS_OFFLOAD,
-	       WMI_SERVICE_BCN_MISS_OFFLOAD);
+	       WMI_SERVICE_BCN_MISS_OFFLOAD, len);
 	SVCMAP(WMI_10X_SERVICE_STA_PWRSAVE,
-	       WMI_SERVICE_STA_PWRSAVE);
+	       WMI_SERVICE_STA_PWRSAVE, len);
 	SVCMAP(WMI_10X_SERVICE_STA_ADVANCED_PWRSAVE,
-	       WMI_SERVICE_STA_ADVANCED_PWRSAVE);
+	       WMI_SERVICE_STA_ADVANCED_PWRSAVE, len);
 	SVCMAP(WMI_10X_SERVICE_AP_UAPSD,
-	       WMI_SERVICE_AP_UAPSD);
+	       WMI_SERVICE_AP_UAPSD, len);
 	SVCMAP(WMI_10X_SERVICE_AP_DFS,
-	       WMI_SERVICE_AP_DFS);
+	       WMI_SERVICE_AP_DFS, len);
 	SVCMAP(WMI_10X_SERVICE_11AC,
-	       WMI_SERVICE_11AC);
+	       WMI_SERVICE_11AC, len);
 	SVCMAP(WMI_10X_SERVICE_BLOCKACK,
-	       WMI_SERVICE_BLOCKACK);
+	       WMI_SERVICE_BLOCKACK, len);
 	SVCMAP(WMI_10X_SERVICE_PHYERR,
-	       WMI_SERVICE_PHYERR);
+	       WMI_SERVICE_PHYERR, len);
 	SVCMAP(WMI_10X_SERVICE_BCN_FILTER,
-	       WMI_SERVICE_BCN_FILTER);
+	       WMI_SERVICE_BCN_FILTER, len);
 	SVCMAP(WMI_10X_SERVICE_RTT,
-	       WMI_SERVICE_RTT);
+	       WMI_SERVICE_RTT, len);
 	SVCMAP(WMI_10X_SERVICE_RATECTRL,
-	       WMI_SERVICE_RATECTRL);
+	       WMI_SERVICE_RATECTRL, len);
 	SVCMAP(WMI_10X_SERVICE_WOW,
-	       WMI_SERVICE_WOW);
+	       WMI_SERVICE_WOW, len);
 	SVCMAP(WMI_10X_SERVICE_RATECTRL_CACHE,
-	       WMI_SERVICE_RATECTRL_CACHE);
+	       WMI_SERVICE_RATECTRL_CACHE, len);
 	SVCMAP(WMI_10X_SERVICE_IRAM_TIDS,
-	       WMI_SERVICE_IRAM_TIDS);
+	       WMI_SERVICE_IRAM_TIDS, len);
 	SVCMAP(WMI_10X_SERVICE_BURST,
-	       WMI_SERVICE_BURST);
+	       WMI_SERVICE_BURST, len);
 	SVCMAP(WMI_10X_SERVICE_SMART_ANTENNA_SW_SUPPORT,
-	       WMI_SERVICE_SMART_ANTENNA_SW_SUPPORT);
+	       WMI_SERVICE_SMART_ANTENNA_SW_SUPPORT, len);
 	SVCMAP(WMI_10X_SERVICE_FORCE_FW_HANG,
-	       WMI_SERVICE_FORCE_FW_HANG);
+	       WMI_SERVICE_FORCE_FW_HANG, len);
 	SVCMAP(WMI_10X_SERVICE_SMART_ANTENNA_HW_SUPPORT,
-	       WMI_SERVICE_SMART_ANTENNA_HW_SUPPORT);
+	       WMI_SERVICE_SMART_ANTENNA_HW_SUPPORT, len);
 }
 
-static inline void wmi_main_svc_map(const __le32 *in, unsigned long *out)
+static inline void wmi_main_svc_map(const __le32 *in, unsigned long *out,
+				    size_t len)
 {
 	SVCMAP(WMI_MAIN_SERVICE_BEACON_OFFLOAD,
-	       WMI_SERVICE_BEACON_OFFLOAD);
+	       WMI_SERVICE_BEACON_OFFLOAD, len);
 	SVCMAP(WMI_MAIN_SERVICE_SCAN_OFFLOAD,
-	       WMI_SERVICE_SCAN_OFFLOAD);
+	       WMI_SERVICE_SCAN_OFFLOAD, len);
 	SVCMAP(WMI_MAIN_SERVICE_ROAM_OFFLOAD,
-	       WMI_SERVICE_ROAM_OFFLOAD);
+	       WMI_SERVICE_ROAM_OFFLOAD, len);
 	SVCMAP(WMI_MAIN_SERVICE_BCN_MISS_OFFLOAD,
-	       WMI_SERVICE_BCN_MISS_OFFLOAD);
+	       WMI_SERVICE_BCN_MISS_OFFLOAD, len);
 	SVCMAP(WMI_MAIN_SERVICE_STA_PWRSAVE,
-	       WMI_SERVICE_STA_PWRSAVE);
+	       WMI_SERVICE_STA_PWRSAVE, len);
 	SVCMAP(WMI_MAIN_SERVICE_STA_ADVANCED_PWRSAVE,
-	       WMI_SERVICE_STA_ADVANCED_PWRSAVE);
+	       WMI_SERVICE_STA_ADVANCED_PWRSAVE, len);
 	SVCMAP(WMI_MAIN_SERVICE_AP_UAPSD,
-	       WMI_SERVICE_AP_UAPSD);
+	       WMI_SERVICE_AP_UAPSD, len);
 	SVCMAP(WMI_MAIN_SERVICE_AP_DFS,
-	       WMI_SERVICE_AP_DFS);
+	       WMI_SERVICE_AP_DFS, len);
 	SVCMAP(WMI_MAIN_SERVICE_11AC,
-	       WMI_SERVICE_11AC);
+	       WMI_SERVICE_11AC, len);
 	SVCMAP(WMI_MAIN_SERVICE_BLOCKACK,
-	       WMI_SERVICE_BLOCKACK);
+	       WMI_SERVICE_BLOCKACK, len);
 	SVCMAP(WMI_MAIN_SERVICE_PHYERR,
-	       WMI_SERVICE_PHYERR);
+	       WMI_SERVICE_PHYERR, len);
 	SVCMAP(WMI_MAIN_SERVICE_BCN_FILTER,
-	       WMI_SERVICE_BCN_FILTER);
+	       WMI_SERVICE_BCN_FILTER, len);
 	SVCMAP(WMI_MAIN_SERVICE_RTT,
-	       WMI_SERVICE_RTT);
+	       WMI_SERVICE_RTT, len);
 	SVCMAP(WMI_MAIN_SERVICE_RATECTRL,
-	       WMI_SERVICE_RATECTRL);
+	       WMI_SERVICE_RATECTRL, len);
 	SVCMAP(WMI_MAIN_SERVICE_WOW,
-	       WMI_SERVICE_WOW);
+	       WMI_SERVICE_WOW, len);
 	SVCMAP(WMI_MAIN_SERVICE_RATECTRL_CACHE,
-	       WMI_SERVICE_RATECTRL_CACHE);
+	       WMI_SERVICE_RATECTRL_CACHE, len);
 	SVCMAP(WMI_MAIN_SERVICE_IRAM_TIDS,
-	       WMI_SERVICE_IRAM_TIDS);
+	       WMI_SERVICE_IRAM_TIDS, len);
 	SVCMAP(WMI_MAIN_SERVICE_ARPNS_OFFLOAD,
-	       WMI_SERVICE_ARPNS_OFFLOAD);
+	       WMI_SERVICE_ARPNS_OFFLOAD, len);
 	SVCMAP(WMI_MAIN_SERVICE_NLO,
-	       WMI_SERVICE_NLO);
+	       WMI_SERVICE_NLO, len);
 	SVCMAP(WMI_MAIN_SERVICE_GTK_OFFLOAD,
-	       WMI_SERVICE_GTK_OFFLOAD);
+	       WMI_SERVICE_GTK_OFFLOAD, len);
 	SVCMAP(WMI_MAIN_SERVICE_SCAN_SCH,
-	       WMI_SERVICE_SCAN_SCH);
+	       WMI_SERVICE_SCAN_SCH, len);
 	SVCMAP(WMI_MAIN_SERVICE_CSA_OFFLOAD,
-	       WMI_SERVICE_CSA_OFFLOAD);
+	       WMI_SERVICE_CSA_OFFLOAD, len);
 	SVCMAP(WMI_MAIN_SERVICE_CHATTER,
-	       WMI_SERVICE_CHATTER);
+	       WMI_SERVICE_CHATTER, len);
 	SVCMAP(WMI_MAIN_SERVICE_COEX_FREQAVOID,
-	       WMI_SERVICE_COEX_FREQAVOID);
+	       WMI_SERVICE_COEX_FREQAVOID, len);
 	SVCMAP(WMI_MAIN_SERVICE_PACKET_POWER_SAVE,
-	       WMI_SERVICE_PACKET_POWER_SAVE);
+	       WMI_SERVICE_PACKET_POWER_SAVE, len);
 	SVCMAP(WMI_MAIN_SERVICE_FORCE_FW_HANG,
-	       WMI_SERVICE_FORCE_FW_HANG);
+	       WMI_SERVICE_FORCE_FW_HANG, len);
 	SVCMAP(WMI_MAIN_SERVICE_GPIO,
-	       WMI_SERVICE_GPIO);
+	       WMI_SERVICE_GPIO, len);
 	SVCMAP(WMI_MAIN_SERVICE_STA_DTIM_PS_MODULATED_DTIM,
-	       WMI_SERVICE_STA_DTIM_PS_MODULATED_DTIM);
+	       WMI_SERVICE_STA_DTIM_PS_MODULATED_DTIM, len);
 	SVCMAP(WMI_MAIN_SERVICE_STA_UAPSD_BASIC_AUTO_TRIG,
-	       WMI_SERVICE_STA_UAPSD_BASIC_AUTO_TRIG);
+	       WMI_SERVICE_STA_UAPSD_BASIC_AUTO_TRIG, len);
 	SVCMAP(WMI_MAIN_SERVICE_STA_UAPSD_VAR_AUTO_TRIG,
-	       WMI_SERVICE_STA_UAPSD_VAR_AUTO_TRIG);
+	       WMI_SERVICE_STA_UAPSD_VAR_AUTO_TRIG, len);
 	SVCMAP(WMI_MAIN_SERVICE_STA_KEEP_ALIVE,
-	       WMI_SERVICE_STA_KEEP_ALIVE);
+	       WMI_SERVICE_STA_KEEP_ALIVE, len);
 	SVCMAP(WMI_MAIN_SERVICE_TX_ENCAP,
-	       WMI_SERVICE_TX_ENCAP);
+	       WMI_SERVICE_TX_ENCAP, len);
 }
 
 #undef SVCMAP
@@ -1952,6 +1955,11 @@
 #define WLAN_SCAN_PARAMS_MAX_BSSID   4
 #define WLAN_SCAN_PARAMS_MAX_IE_LEN  256
 
+/* Values lower than this may be refused by some firmware revisions with a scan
+ * completion with a timedout reason.
+ */
+#define WMI_SCAN_CHAN_MIN_TIME_MSEC 40
+
 /* Scan priority numbers must be sequential, starting with 0 */
 enum wmi_scan_priority {
 	WMI_SCAN_PRIORITY_VERY_LOW = 0,
@@ -4547,7 +4555,6 @@
 	__le32 config_valid;
 } __packed;
 
-#define ATH10K_RTS_MAX		2347
 #define ATH10K_FRAGMT_THRESHOLD_MIN	540
 #define ATH10K_FRAGMT_THRESHOLD_MAX	2346
 
@@ -4572,6 +4579,7 @@
 	__le32 eeprom_rd;
 	__le32 num_mem_reqs;
 	const __le32 *service_map;
+	size_t service_map_len;
 	const struct wlan_host_mem_req *mem_reqs[WMI_MAX_MEM_REQS];
 };
 
diff --git a/drivers/net/wireless/ath/ath5k/qcu.c b/drivers/net/wireless/ath/ath5k/qcu.c
index 0583c69..ddaad71 100644
--- a/drivers/net/wireless/ath/ath5k/qcu.c
+++ b/drivers/net/wireless/ath/ath5k/qcu.c
@@ -225,13 +225,7 @@
 	} else {
 		switch (queue_type) {
 		case AR5K_TX_QUEUE_DATA:
-			for (queue = AR5K_TX_QUEUE_ID_DATA_MIN;
-				ah->ah_txq[queue].tqi_type !=
-				AR5K_TX_QUEUE_INACTIVE; queue++) {
-
-				if (queue > AR5K_TX_QUEUE_ID_DATA_MAX)
-					return -EINVAL;
-			}
+			queue = queue_info->tqi_subtype;
 			break;
 		case AR5K_TX_QUEUE_UAPSD:
 			queue = AR5K_TX_QUEUE_ID_UAPSD;
diff --git a/drivers/net/wireless/ath/ath9k/Kconfig b/drivers/net/wireless/ath/ath9k/Kconfig
index ca101d7..fee0cad 100644
--- a/drivers/net/wireless/ath/ath9k/Kconfig
+++ b/drivers/net/wireless/ath/ath9k/Kconfig
@@ -3,6 +3,8 @@
 config ATH9K_COMMON
 	tristate
 	select ATH_COMMON
+	select DEBUG_FS
+	select RELAY
 config ATH9K_DFS_DEBUGFS
 	def_bool y
 	depends on ATH9K_DEBUGFS && ATH9K_DFS_CERTIFIED
diff --git a/drivers/net/wireless/ath/ath9k/ar9002_mac.c b/drivers/net/wireless/ath/ath9k/ar9002_mac.c
index 2a93519..f816909 100644
--- a/drivers/net/wireless/ath/ath9k/ar9002_mac.c
+++ b/drivers/net/wireless/ath/ath9k/ar9002_mac.c
@@ -281,7 +281,7 @@
 
 	ACCESS_ONCE(ads->ds_ctl0) = (i->pkt_len & AR_FrameLen)
 		| (i->flags & ATH9K_TXDESC_VMF ? AR_VirtMoreFrag : 0)
-		| SM(i->txpower, AR_XmitPower0)
+		| SM(i->txpower[0], AR_XmitPower0)
 		| (i->flags & ATH9K_TXDESC_VEOL ? AR_VEOL : 0)
 		| (i->flags & ATH9K_TXDESC_INTREQ ? AR_TxIntrReq : 0)
 		| (i->keyix != ATH9K_TXKEYIX_INVALID ? AR_DestIdxValid : 0)
@@ -307,9 +307,9 @@
 		| set11nRateFlags(i->rates, 3)
 		| SM(i->rtscts_rate, AR_RTSCTSRate);
 
-	ACCESS_ONCE(ads->ds_ctl9) = SM(i->txpower, AR_XmitPower1);
-	ACCESS_ONCE(ads->ds_ctl10) = SM(i->txpower, AR_XmitPower2);
-	ACCESS_ONCE(ads->ds_ctl11) = SM(i->txpower, AR_XmitPower3);
+	ACCESS_ONCE(ads->ds_ctl9) = SM(i->txpower[1], AR_XmitPower1);
+	ACCESS_ONCE(ads->ds_ctl10) = SM(i->txpower[2], AR_XmitPower2);
+	ACCESS_ONCE(ads->ds_ctl11) = SM(i->txpower[3], AR_XmitPower3);
 }
 
 static int ar9002_hw_proc_txdesc(struct ath_hw *ah, void *ds,
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
index e726e40..08225a0 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
@@ -4377,6 +4377,25 @@
 						 targetPowerArray, numPiers);
 }
 
+static void ar9003_hw_selfgen_tpc_txpower(struct ath_hw *ah,
+					  struct ath9k_channel *chan,
+					  u8 *pwr_array)
+{
+	u32 val;
+
+	/* target power values for self generated frames (ACK,RTS/CTS) */
+	if (IS_CHAN_2GHZ(chan)) {
+		val = SM(pwr_array[ALL_TARGET_LEGACY_1L_5L], AR_TPC_ACK) |
+		      SM(pwr_array[ALL_TARGET_LEGACY_1L_5L], AR_TPC_CTS) |
+		      SM(0x3f, AR_TPC_CHIRP) | SM(0x3f, AR_TPC_RPT);
+	} else {
+		val = SM(pwr_array[ALL_TARGET_LEGACY_6_24], AR_TPC_ACK) |
+		      SM(pwr_array[ALL_TARGET_LEGACY_6_24], AR_TPC_CTS) |
+		      SM(0x3f, AR_TPC_CHIRP) | SM(0x3f, AR_TPC_RPT);
+	}
+	REG_WRITE(ah, AR_TPC, val);
+}
+
 /* Set tx power registers to array of values passed in */
 static int ar9003_hw_tx_power_regwrite(struct ath_hw *ah, u8 * pPwrArray)
 {
@@ -5312,6 +5331,7 @@
 	struct ar9300_modal_eep_header *modal_hdr;
 	u8 targetPowerValT2[ar9300RateSize];
 	u8 target_power_val_t2_eep[ar9300RateSize];
+	u8 targetPowerValT2_tpc[ar9300RateSize];
 	unsigned int i = 0, paprd_scale_factor = 0;
 	u8 pwr_idx, min_pwridx = 0;
 
@@ -5363,6 +5383,9 @@
 					   twiceAntennaReduction,
 					   powerLimit);
 
+	memcpy(targetPowerValT2_tpc, targetPowerValT2,
+	       sizeof(targetPowerValT2));
+
 	if (ar9003_is_paprd_enabled(ah)) {
 		for (i = 0; i < ar9300RateSize; i++) {
 			if ((ah->paprd_ratemask & (1 << i)) &&
@@ -5396,6 +5419,30 @@
 	ar9003_hw_tx_power_regwrite(ah, targetPowerValT2);
 	ar9003_hw_calibration_apply(ah, chan->channel);
 	ar9003_paprd_set_txpower(ah, chan, targetPowerValT2);
+
+	ar9003_hw_selfgen_tpc_txpower(ah, chan, targetPowerValT2);
+
+	/* TPC initializations */
+	if (ah->tpc_enabled) {
+		u32 val;
+
+		ar9003_hw_init_rate_txpower(ah, targetPowerValT2_tpc, chan);
+
+		/* Enable TPC */
+		REG_WRITE(ah, AR_PHY_PWRTX_MAX,
+			  AR_PHY_POWER_TX_RATE_MAX_TPC_ENABLE);
+		/* Disable per chain power reduction */
+		val = REG_READ(ah, AR_PHY_POWER_TX_SUB);
+		if (AR_SREV_9340(ah))
+			REG_WRITE(ah, AR_PHY_POWER_TX_SUB,
+				  val & 0xFFFFFFC0);
+		else
+			REG_WRITE(ah, AR_PHY_POWER_TX_SUB,
+				  val & 0xFFFFF000);
+	} else {
+		/* Disable TPC */
+		REG_WRITE(ah, AR_PHY_PWRTX_MAX, 0);
+	}
 }
 
 static u16 ath9k_hw_ar9300_get_spur_channel(struct ath_hw *ah,
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mac.c b/drivers/net/wireless/ath/ath9k/ar9003_mac.c
index 057b165..da84b70 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_mac.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_mac.c
@@ -101,7 +101,7 @@
 
 	ACCESS_ONCE(ads->ctl11) = (i->pkt_len & AR_FrameLen)
 		| (i->flags & ATH9K_TXDESC_VMF ? AR_VirtMoreFrag : 0)
-		| SM(i->txpower, AR_XmitPower0)
+		| SM(i->txpower[0], AR_XmitPower0)
 		| (i->flags & ATH9K_TXDESC_VEOL ? AR_VEOL : 0)
 		| (i->keyix != ATH9K_TXKEYIX_INVALID ? AR_DestIdxValid : 0)
 		| (i->flags & ATH9K_TXDESC_LOWRXCHAIN ? AR_LowRxChain : 0)
@@ -152,9 +152,9 @@
 
 	ACCESS_ONCE(ads->ctl19) = AR_Not_Sounding;
 
-	ACCESS_ONCE(ads->ctl20) = SM(i->txpower, AR_XmitPower1);
-	ACCESS_ONCE(ads->ctl21) = SM(i->txpower, AR_XmitPower2);
-	ACCESS_ONCE(ads->ctl22) = SM(i->txpower, AR_XmitPower3);
+	ACCESS_ONCE(ads->ctl20) = SM(i->txpower[1], AR_XmitPower1);
+	ACCESS_ONCE(ads->ctl21) = SM(i->txpower[2], AR_XmitPower2);
+	ACCESS_ONCE(ads->ctl22) = SM(i->txpower[3], AR_XmitPower3);
 }
 
 static u16 ar9003_calc_ptr_chksum(struct ar9003_txc *ads)
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
index 9bdaa0a..ae6cde2 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
@@ -18,6 +18,21 @@
 #include "hw.h"
 #include "ar9003_phy.h"
 
+#define AR9300_OFDM_RATES	8
+#define AR9300_HT_SS_RATES	8
+#define AR9300_HT_DS_RATES	8
+#define AR9300_HT_TS_RATES	8
+
+#define AR9300_11NA_OFDM_SHIFT		0
+#define AR9300_11NA_HT_SS_SHIFT		8
+#define AR9300_11NA_HT_DS_SHIFT		16
+#define AR9300_11NA_HT_TS_SHIFT		24
+
+#define AR9300_11NG_OFDM_SHIFT		4
+#define AR9300_11NG_HT_SS_SHIFT		12
+#define AR9300_11NG_HT_DS_SHIFT		20
+#define AR9300_11NG_HT_TS_SHIFT		28
+
 static const int firstep_table[] =
 /* level:  0   1   2   3   4   5   6   7   8  */
 	{ -4, -2,  0,  2,  4,  6,  8, 10, 12 }; /* lvl 0-8, default 2 */
@@ -40,6 +55,71 @@
 static const int m1ThreshExt_off = 127;
 static const int m2ThreshExt_off = 127;
 
+static const u8 ofdm2pwr[] = {
+	ALL_TARGET_LEGACY_6_24,
+	ALL_TARGET_LEGACY_6_24,
+	ALL_TARGET_LEGACY_6_24,
+	ALL_TARGET_LEGACY_6_24,
+	ALL_TARGET_LEGACY_6_24,
+	ALL_TARGET_LEGACY_36,
+	ALL_TARGET_LEGACY_48,
+	ALL_TARGET_LEGACY_54
+};
+
+static const u8 mcs2pwr_ht20[] = {
+	ALL_TARGET_HT20_0_8_16,
+	ALL_TARGET_HT20_1_3_9_11_17_19,
+	ALL_TARGET_HT20_1_3_9_11_17_19,
+	ALL_TARGET_HT20_1_3_9_11_17_19,
+	ALL_TARGET_HT20_4,
+	ALL_TARGET_HT20_5,
+	ALL_TARGET_HT20_6,
+	ALL_TARGET_HT20_7,
+	ALL_TARGET_HT20_0_8_16,
+	ALL_TARGET_HT20_1_3_9_11_17_19,
+	ALL_TARGET_HT20_1_3_9_11_17_19,
+	ALL_TARGET_HT20_1_3_9_11_17_19,
+	ALL_TARGET_HT20_12,
+	ALL_TARGET_HT20_13,
+	ALL_TARGET_HT20_14,
+	ALL_TARGET_HT20_15,
+	ALL_TARGET_HT20_0_8_16,
+	ALL_TARGET_HT20_1_3_9_11_17_19,
+	ALL_TARGET_HT20_1_3_9_11_17_19,
+	ALL_TARGET_HT20_1_3_9_11_17_19,
+	ALL_TARGET_HT20_20,
+	ALL_TARGET_HT20_21,
+	ALL_TARGET_HT20_22,
+	ALL_TARGET_HT20_23
+};
+
+static const u8 mcs2pwr_ht40[] = {
+	ALL_TARGET_HT40_0_8_16,
+	ALL_TARGET_HT40_1_3_9_11_17_19,
+	ALL_TARGET_HT40_1_3_9_11_17_19,
+	ALL_TARGET_HT40_1_3_9_11_17_19,
+	ALL_TARGET_HT40_4,
+	ALL_TARGET_HT40_5,
+	ALL_TARGET_HT40_6,
+	ALL_TARGET_HT40_7,
+	ALL_TARGET_HT40_0_8_16,
+	ALL_TARGET_HT40_1_3_9_11_17_19,
+	ALL_TARGET_HT40_1_3_9_11_17_19,
+	ALL_TARGET_HT40_1_3_9_11_17_19,
+	ALL_TARGET_HT40_12,
+	ALL_TARGET_HT40_13,
+	ALL_TARGET_HT40_14,
+	ALL_TARGET_HT40_15,
+	ALL_TARGET_HT40_0_8_16,
+	ALL_TARGET_HT40_1_3_9_11_17_19,
+	ALL_TARGET_HT40_1_3_9_11_17_19,
+	ALL_TARGET_HT40_1_3_9_11_17_19,
+	ALL_TARGET_HT40_20,
+	ALL_TARGET_HT40_21,
+	ALL_TARGET_HT40_22,
+	ALL_TARGET_HT40_23,
+};
+
 /**
  * ar9003_hw_set_channel - set channel on single-chip device
  * @ah: atheros hardware structure
@@ -664,6 +744,19 @@
 		ah->enabled_cals |= TX_CL_CAL;
 	else
 		ah->enabled_cals &= ~TX_CL_CAL;
+
+	if (AR_SREV_9340(ah) || AR_SREV_9531(ah) || AR_SREV_9550(ah)) {
+		if (ah->is_clk_25mhz) {
+			REG_WRITE(ah, AR_RTC_DERIVED_CLK, 0x17c << 1);
+			REG_WRITE(ah, AR_SLP32_MODE, 0x0010f3d7);
+			REG_WRITE(ah, AR_SLP32_INC, 0x0001e7ae);
+		} else {
+			REG_WRITE(ah, AR_RTC_DERIVED_CLK, 0x261 << 1);
+			REG_WRITE(ah, AR_SLP32_MODE, 0x0010f400);
+			REG_WRITE(ah, AR_SLP32_INC, 0x0001e800);
+		}
+		udelay(100);
+	}
 }
 
 static void ar9003_hw_prog_ini(struct ath_hw *ah,
@@ -1786,6 +1879,100 @@
 		  ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_14],  0));
 }
 
+static void ar9003_hw_init_txpower_cck(struct ath_hw *ah, u8 *rate_array)
+{
+	ah->tx_power[0] = rate_array[ALL_TARGET_LEGACY_1L_5L];
+	ah->tx_power[1] = rate_array[ALL_TARGET_LEGACY_1L_5L];
+	ah->tx_power[2] = min(rate_array[ALL_TARGET_LEGACY_1L_5L],
+			      rate_array[ALL_TARGET_LEGACY_5S]);
+	ah->tx_power[3] = min(rate_array[ALL_TARGET_LEGACY_11L],
+			      rate_array[ALL_TARGET_LEGACY_11S]);
+}
+
+static void ar9003_hw_init_txpower_ofdm(struct ath_hw *ah, u8 *rate_array,
+					int offset)
+{
+	int i, j;
+
+	for (i = offset; i < offset + AR9300_OFDM_RATES; i++) {
+		/* OFDM rate to power table idx */
+		j = ofdm2pwr[i - offset];
+		ah->tx_power[i] = rate_array[j];
+	}
+}
+
+static void ar9003_hw_init_txpower_ht(struct ath_hw *ah, u8 *rate_array,
+				      int ss_offset, int ds_offset,
+				      int ts_offset, bool is_40)
+{
+	int i, j, mcs_idx = 0;
+	const u8 *mcs2pwr = (is_40) ? mcs2pwr_ht40 : mcs2pwr_ht20;
+
+	for (i = ss_offset; i < ss_offset + AR9300_HT_SS_RATES; i++) {
+		j = mcs2pwr[mcs_idx];
+		ah->tx_power[i] = rate_array[j];
+		mcs_idx++;
+	}
+
+	for (i = ds_offset; i < ds_offset + AR9300_HT_DS_RATES; i++) {
+		j = mcs2pwr[mcs_idx];
+		ah->tx_power[i] = rate_array[j];
+		mcs_idx++;
+	}
+
+	for (i = ts_offset; i < ts_offset + AR9300_HT_TS_RATES; i++) {
+		j = mcs2pwr[mcs_idx];
+		ah->tx_power[i] = rate_array[j];
+		mcs_idx++;
+	}
+}
+
+static void ar9003_hw_init_txpower_stbc(struct ath_hw *ah, int ss_offset,
+					int ds_offset, int ts_offset)
+{
+	memcpy(&ah->tx_power_stbc[ss_offset], &ah->tx_power[ss_offset],
+	       AR9300_HT_SS_RATES);
+	memcpy(&ah->tx_power_stbc[ds_offset], &ah->tx_power[ds_offset],
+	       AR9300_HT_DS_RATES);
+	memcpy(&ah->tx_power_stbc[ts_offset], &ah->tx_power[ts_offset],
+	       AR9300_HT_TS_RATES);
+}
+
+void ar9003_hw_init_rate_txpower(struct ath_hw *ah, u8 *rate_array,
+				 struct ath9k_channel *chan)
+{
+	if (IS_CHAN_5GHZ(chan)) {
+		ar9003_hw_init_txpower_ofdm(ah, rate_array,
+					    AR9300_11NA_OFDM_SHIFT);
+		if (IS_CHAN_HT20(chan) || IS_CHAN_HT40(chan)) {
+			ar9003_hw_init_txpower_ht(ah, rate_array,
+						  AR9300_11NA_HT_SS_SHIFT,
+						  AR9300_11NA_HT_DS_SHIFT,
+						  AR9300_11NA_HT_TS_SHIFT,
+						  IS_CHAN_HT40(chan));
+			ar9003_hw_init_txpower_stbc(ah,
+						    AR9300_11NA_HT_SS_SHIFT,
+						    AR9300_11NA_HT_DS_SHIFT,
+						    AR9300_11NA_HT_TS_SHIFT);
+		}
+	} else {
+		ar9003_hw_init_txpower_cck(ah, rate_array);
+		ar9003_hw_init_txpower_ofdm(ah, rate_array,
+					    AR9300_11NG_OFDM_SHIFT);
+		if (IS_CHAN_HT20(chan) || IS_CHAN_HT40(chan)) {
+			ar9003_hw_init_txpower_ht(ah, rate_array,
+						  AR9300_11NG_HT_SS_SHIFT,
+						  AR9300_11NG_HT_DS_SHIFT,
+						  AR9300_11NG_HT_TS_SHIFT,
+						  IS_CHAN_HT40(chan));
+			ar9003_hw_init_txpower_stbc(ah,
+						    AR9300_11NG_HT_SS_SHIFT,
+						    AR9300_11NG_HT_DS_SHIFT,
+						    AR9300_11NG_HT_TS_SHIFT);
+		}
+	}
+}
+
 void ar9003_hw_attach_phy_ops(struct ath_hw *ah)
 {
 	struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah);
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index abe8bd6..1a9fe09 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -189,6 +189,7 @@
 	u8 rtscts_rate;
 	u8 retries : 7;
 	u8 baw_tracked : 1;
+	u8 tx_power;
 };
 
 struct ath_rxbuf {
diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c
index ecb783b..cb366ad 100644
--- a/drivers/net/wireless/ath/ath9k/beacon.c
+++ b/drivers/net/wireless/ath/ath9k/beacon.c
@@ -78,7 +78,7 @@
 	struct ath_tx_info info;
 	struct ieee80211_supported_band *sband;
 	u8 chainmask = ah->txchainmask;
-	u8 rate = 0;
+	u8 i, rate = 0;
 
 	sband = &common->sbands[sc->cur_chandef.chan->band];
 	rate = sband->bitrates[rateidx].hw_value;
@@ -88,7 +88,8 @@
 	memset(&info, 0, sizeof(info));
 	info.pkt_len = skb->len + FCS_LEN;
 	info.type = ATH9K_PKT_TYPE_BEACON;
-	info.txpower = MAX_RATE_POWER;
+	for (i = 0; i < 4; i++)
+		info.txpower[i] = MAX_RATE_POWER;
 	info.keyix = ATH9K_TXKEYIX_INVALID;
 	info.keytype = ATH9K_KEY_TYPE_CLEAR;
 	info.flags = ATH9K_TXDESC_NOACK | ATH9K_TXDESC_CLRDMASK;
diff --git a/drivers/net/wireless/ath/ath9k/common.c b/drivers/net/wireless/ath/ath9k/common.c
index eb62c58..e8c6994 100644
--- a/drivers/net/wireless/ath/ath9k/common.c
+++ b/drivers/net/wireless/ath/ath9k/common.c
@@ -368,11 +368,11 @@
 {
 	struct ath_regulatory *reg = ath9k_hw_regulatory(ah);
 
-	if (reg->power_limit != new_txpow) {
+	if (reg->power_limit != new_txpow)
 		ath9k_hw_set_txpowerlimit(ah, new_txpow, false);
-		/* read back in case value is clamped */
-		*txpower = reg->max_power_level;
-	}
+
+	/* read back in case value is clamped */
+	*txpower = reg->max_power_level;
 }
 EXPORT_SYMBOL(ath9k_cmn_update_txpow);
 
diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c
index 3f21b1b..696e3d5 100644
--- a/drivers/net/wireless/ath/ath9k/debug.c
+++ b/drivers/net/wireless/ath/ath9k/debug.c
@@ -455,7 +455,7 @@
 			 "%2d          %2x      %1x     %2x           %2x\n",
 			 i, (*qcuBase & (0x7 << qcuOffset)) >> qcuOffset,
 			 (*qcuBase & (0x8 << qcuOffset)) >> (qcuOffset + 3),
-			 val[2] & (0x7 << (i * 3)) >> (i * 3),
+			 (val[2] & (0x7 << (i * 3))) >> (i * 3),
 			 (*dcuBase & (0x1f << dcuOffset)) >> dcuOffset);
 	}
 
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index fbc78d8..6d4b273 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -870,19 +870,6 @@
 	udelay(RTC_PLL_SETTLE_DELAY);
 
 	REG_WRITE(ah, AR_RTC_SLEEP_CLK, AR_RTC_FORCE_DERIVED_CLK);
-
-	if (AR_SREV_9340(ah) || AR_SREV_9550(ah)) {
-		if (ah->is_clk_25mhz) {
-			REG_WRITE(ah, AR_RTC_DERIVED_CLK, 0x17c << 1);
-			REG_WRITE(ah, AR_SLP32_MODE, 0x0010f3d7);
-			REG_WRITE(ah,  AR_SLP32_INC, 0x0001e7ae);
-		} else {
-			REG_WRITE(ah, AR_RTC_DERIVED_CLK, 0x261 << 1);
-			REG_WRITE(ah, AR_SLP32_MODE, 0x0010f400);
-			REG_WRITE(ah,  AR_SLP32_INC, 0x0001e800);
-		}
-		udelay(100);
-	}
 }
 
 static void ath9k_hw_init_interrupt_masks(struct ath_hw *ah,
diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h
index 4cf9e0a..1cbd335 100644
--- a/drivers/net/wireless/ath/ath9k/hw.h
+++ b/drivers/net/wireless/ath/ath9k/hw.h
@@ -217,8 +217,8 @@
 #define AH_WOW_BEACON_MISS		BIT(3)
 
 enum ath_hw_txq_subtype {
-	ATH_TXQ_AC_BE = 0,
-	ATH_TXQ_AC_BK = 1,
+	ATH_TXQ_AC_BK = 0,
+	ATH_TXQ_AC_BE = 1,
 	ATH_TXQ_AC_VI = 2,
 	ATH_TXQ_AC_VO = 3,
 };
@@ -940,6 +940,10 @@
 	const struct firmware *eeprom_blob;
 
 	struct ath_dynack dynack;
+
+	bool tpc_enabled;
+	u8 tx_power[Ar5416RateSize];
+	u8 tx_power_stbc[Ar5416RateSize];
 };
 
 struct ath_bus_ops {
@@ -1080,6 +1084,8 @@
 bool ar9003_paprd_is_done(struct ath_hw *ah);
 bool ar9003_is_paprd_enabled(struct ath_hw *ah);
 void ar9003_hw_set_chain_masks(struct ath_hw *ah, u8 rx, u8 tx);
+void ar9003_hw_init_rate_txpower(struct ath_hw *ah, u8 *rate_array,
+				 struct ath9k_channel *chan);
 
 /* Hardware family op attach helpers */
 int ar5008_hw_attach_phy_ops(struct ath_hw *ah);
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index 39157ca..d1c3934 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -532,10 +532,14 @@
 	ah->reg_ops.read = ath9k_ioread32;
 	ah->reg_ops.write = ath9k_iowrite32;
 	ah->reg_ops.rmw = ath9k_reg_rmw;
-	sc->sc_ah = ah;
 	pCap = &ah->caps;
 
 	common = ath9k_hw_common(ah);
+
+	/* Will be cleared in ath9k_start() */
+	set_bit(ATH_OP_INVALID, &common->op_flags);
+
+	sc->sc_ah = ah;
 	sc->dfs_detector = dfs_pattern_detector_init(common, NL80211_DFS_UNSET);
 	sc->tx99_power = MAX_RATE_POWER + 1;
 	init_waitqueue_head(&sc->tx_wait);
@@ -764,6 +768,32 @@
 #endif
 };
 
+#ifdef CONFIG_ATH9K_CHANNEL_CONTEXT
+static void ath9k_set_mcc_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
+{
+	struct ath_hw *ah = sc->sc_ah;
+	struct ath_common *common = ath9k_hw_common(ah);
+
+	if (!ath9k_is_chanctx_enabled())
+		return;
+
+	hw->flags |= IEEE80211_HW_QUEUE_CONTROL;
+	hw->queues = ATH9K_NUM_TX_QUEUES;
+	hw->offchannel_tx_hw_queue = hw->queues - 1;
+	hw->wiphy->interface_modes &= ~ BIT(NL80211_IFTYPE_WDS);
+	hw->wiphy->iface_combinations = if_comb_multi;
+	hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb_multi);
+	hw->wiphy->max_scan_ssids = 255;
+	hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
+	hw->wiphy->max_remain_on_channel_duration = 10000;
+	hw->chanctx_data_size = sizeof(void *);
+	hw->extra_beacon_tailroom =
+		sizeof(struct ieee80211_p2p_noa_attr) + 9;
+
+	ath_dbg(common, CHAN_CTX, "Use channel contexts\n");
+}
+#endif /* CONFIG_ATH9K_CHANNEL_CONTEXT */
+
 static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
 {
 	struct ath_hw *ah = sc->sc_ah;
@@ -776,7 +806,6 @@
 		IEEE80211_HW_SPECTRUM_MGMT |
 		IEEE80211_HW_REPORTS_TX_ACK_STATUS |
 		IEEE80211_HW_SUPPORTS_RC_TABLE |
-		IEEE80211_HW_QUEUE_CONTROL |
 		IEEE80211_HW_SUPPORTS_HT_CCK_RATES;
 
 	if (ath9k_ps_enable)
@@ -811,24 +840,6 @@
 			hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb);
 	}
 
-#ifdef CONFIG_ATH9K_CHANNEL_CONTEXT
-
-	if (ath9k_is_chanctx_enabled()) {
-		hw->wiphy->interface_modes &= ~ BIT(NL80211_IFTYPE_WDS);
-		hw->wiphy->iface_combinations = if_comb_multi;
-		hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb_multi);
-		hw->wiphy->max_scan_ssids = 255;
-		hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
-		hw->wiphy->max_remain_on_channel_duration = 10000;
-		hw->chanctx_data_size = sizeof(void *);
-		hw->extra_beacon_tailroom =
-			sizeof(struct ieee80211_p2p_noa_attr) + 9;
-
-		ath_dbg(common, CHAN_CTX, "Use channel contexts\n");
-	}
-
-#endif /* CONFIG_ATH9K_CHANNEL_CONTEXT */
-
 	hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
 
 	hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
@@ -838,12 +849,7 @@
 	hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
 	hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
 
-	/* allow 4 queues per channel context +
-	 * 1 cab queue + 1 offchannel tx queue
-	 */
-	hw->queues = ATH9K_NUM_TX_QUEUES;
-	/* last queue for offchannel */
-	hw->offchannel_tx_hw_queue = hw->queues - 1;
+	hw->queues = 4;
 	hw->max_rates = 4;
 	hw->max_listen_interval = 10;
 	hw->max_rate_tries = 10;
@@ -867,6 +873,9 @@
 		hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
 			&common->sbands[IEEE80211_BAND_5GHZ];
 
+#ifdef CONFIG_ATH9K_CHANNEL_CONTEXT
+	ath9k_set_mcc_capab(sc, hw);
+#endif
 	ath9k_init_wow(hw);
 	ath9k_cmn_reload_chainmask(ah);
 
@@ -891,9 +900,6 @@
 	common = ath9k_hw_common(ah);
 	ath9k_set_hw_capab(sc, hw);
 
-	/* Will be cleared in ath9k_start() */
-	set_bit(ATH_OP_INVALID, &common->op_flags);
-
 	/* Initialize regulatory */
 	error = ath_regd_init(&common->regulatory, sc->hw->wiphy,
 			      ath9k_reg_notifier);
diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c
index 275205a..3e58bfa 100644
--- a/drivers/net/wireless/ath/ath9k/mac.c
+++ b/drivers/net/wireless/ath/ath9k/mac.c
@@ -311,14 +311,7 @@
 		q = ATH9K_NUM_TX_QUEUES - 3;
 		break;
 	case ATH9K_TX_QUEUE_DATA:
-		for (q = 0; q < ATH9K_NUM_TX_QUEUES; q++)
-			if (ah->txq[q].tqi_type ==
-			    ATH9K_TX_QUEUE_INACTIVE)
-				break;
-		if (q == ATH9K_NUM_TX_QUEUES) {
-			ath_err(common, "No available TX queue\n");
-			return -1;
-		}
+		q = qinfo->tqi_subtype;
 		break;
 	default:
 		ath_err(common, "Invalid TX queue type: %u\n", type);
diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h
index aa69cea..e55fa11 100644
--- a/drivers/net/wireless/ath/ath9k/mac.h
+++ b/drivers/net/wireless/ath/ath9k/mac.h
@@ -704,7 +704,7 @@
 	enum ath9k_pkt_type type;
 	enum ath9k_key_type keytype;
 	u8 keyix;
-	u8 txpower;
+	u8 txpower[4];
 };
 
 struct ath_hw;
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 027ad71..cff070d 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -512,15 +512,12 @@
 	if (!ah || test_bit(ATH_OP_INVALID, &common->op_flags))
 		return IRQ_NONE;
 
-	/* shared irq, not for us */
-
-	if (!ath9k_hw_intrpend(ah))
+	if (!AR_SREV_9100(ah) && test_bit(ATH_OP_HW_RESET, &common->op_flags))
 		return IRQ_NONE;
 
-	if (test_bit(ATH_OP_HW_RESET, &common->op_flags)) {
-		ath9k_hw_kill_interrupts(ah);
-		return IRQ_HANDLED;
-	}
+	/* shared irq, not for us */
+	if (!ath9k_hw_intrpend(ah))
+		return IRQ_NONE;
 
 	/*
 	 * Figure out the reason(s) for the interrupt.  Note
@@ -532,6 +529,9 @@
 	ath9k_debug_sync_cause(sc, sync_cause);
 	status &= ah->imask;	/* discard unasked-for bits */
 
+	if (AR_SREV_9100(ah) && test_bit(ATH_OP_HW_RESET, &common->op_flags))
+		return IRQ_HANDLED;
+
 	/*
 	 * If there are no status bits set, then this interrupt was not
 	 * for me (should have been caught above).
@@ -613,6 +613,7 @@
 	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
 	int r;
 
+	ath9k_hw_kill_interrupts(sc->sc_ah);
 	set_bit(ATH_OP_HW_RESET, &common->op_flags);
 
 	ath9k_ps_wakeup(sc);
@@ -633,6 +634,7 @@
 #ifdef CONFIG_ATH9K_DEBUGFS
 	RESET_STAT_INC(sc, type);
 #endif
+	ath9k_hw_kill_interrupts(sc->sc_ah);
 	set_bit(ATH_OP_HW_RESET, &common->op_flags);
 	ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
 }
@@ -887,6 +889,9 @@
 						    &sc->cur_chan->chandef);
 
 	ath9k_hw_reset(ah, ah->curchan, ah->caldata, false);
+
+	set_bit(ATH_OP_INVALID, &common->op_flags);
+
 	ath9k_hw_phy_disable(ah);
 
 	ath9k_hw_configpcipowersave(ah, true);
@@ -895,7 +900,6 @@
 
 	ath9k_ps_restore(sc);
 
-	set_bit(ATH_OP_INVALID, &common->op_flags);
 	sc->ps_idle = prev_idle;
 
 	mutex_unlock(&sc->mutex);
@@ -1184,6 +1188,9 @@
 {
 	int i;
 
+	if (!ath9k_is_chanctx_enabled())
+		return;
+
 	for (i = 0; i < IEEE80211_NUM_ACS; i++)
 		vif->hw_queue[i] = i;
 
diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h
index ced36b4..fb11a91 100644
--- a/drivers/net/wireless/ath/ath9k/reg.h
+++ b/drivers/net/wireless/ath/ath9k/reg.h
@@ -1724,6 +1724,8 @@
 #define AR_TPC_CTS_S           8
 #define AR_TPC_CHIRP           0x003f0000
 #define AR_TPC_CHIRP_S         16
+#define AR_TPC_RPT	       0x3f000000
+#define AR_TPC_RPT_S	       24
 
 #define AR_QUIET1          0x80fc
 #define AR_QUIET1_NEXT_QUIET_S         0
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index 493a183..e9bd02c 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -169,7 +169,10 @@
 
 	if (txq->stopped &&
 	    txq->pending_frames < sc->tx.txq_max_pending[q]) {
-		ieee80211_wake_queue(sc->hw, info->hw_queue);
+		if (ath9k_is_chanctx_enabled())
+			ieee80211_wake_queue(sc->hw, info->hw_queue);
+		else
+			ieee80211_wake_queue(sc->hw, q);
 		txq->stopped = false;
 	}
 }
@@ -1093,6 +1096,37 @@
 	}
 }
 
+static u8 ath_get_rate_txpower(struct ath_softc *sc, struct ath_buf *bf,
+			       u8 rateidx)
+{
+	u8 max_power;
+	struct ath_hw *ah = sc->sc_ah;
+
+	if (sc->tx99_state)
+		return MAX_RATE_POWER;
+
+	if (!AR_SREV_9300_20_OR_LATER(ah)) {
+		/* ar9002 is not sipported for the moment */
+		return MAX_RATE_POWER;
+	}
+
+	if (!bf->bf_state.bfs_paprd) {
+		struct sk_buff *skb = bf->bf_mpdu;
+		struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+		struct ath_frame_info *fi = get_frame_info(skb);
+
+		if (rateidx < 8 && (info->flags & IEEE80211_TX_CTL_STBC))
+			max_power = min(ah->tx_power_stbc[rateidx],
+					fi->tx_power);
+		else
+			max_power = min(ah->tx_power[rateidx], fi->tx_power);
+	} else {
+		max_power = ah->paprd_training_power;
+	}
+
+	return max_power;
+}
+
 static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf,
 			     struct ath_tx_info *info, int len, bool rts)
 {
@@ -1163,6 +1197,8 @@
 				 is_40, is_sgi, is_sp);
 			if (rix < 8 && (tx_info->flags & IEEE80211_TX_CTL_STBC))
 				info->rates[i].RateFlags |= ATH9K_RATESERIES_STBC;
+
+			info->txpower[i] = ath_get_rate_txpower(sc, bf, rix);
 			continue;
 		}
 
@@ -1190,6 +1226,8 @@
 
 		info->rates[i].PktDuration = ath9k_hw_computetxtime(sc->sc_ah,
 			phy, rate->bitrate * 100, len, rix, is_sp);
+
+		info->txpower[i] = ath_get_rate_txpower(sc, bf, rix);
 	}
 
 	/* For AR5416 - RTS cannot be followed by a frame larger than 8K */
@@ -1236,7 +1274,6 @@
 	memset(&info, 0, sizeof(info));
 	info.is_first = true;
 	info.is_last = true;
-	info.txpower = MAX_RATE_POWER;
 	info.qcu = txq->axq_qnum;
 
 	while (bf) {
@@ -2060,6 +2097,7 @@
 		fi->keyix = ATH9K_TXKEYIX_INVALID;
 	fi->keytype = keytype;
 	fi->framelen = framelen;
+	fi->tx_power = MAX_RATE_POWER;
 
 	if (!rate)
 		return;
@@ -2247,7 +2285,10 @@
 		fi->txq = q;
 		if (++txq->pending_frames > sc->tx.txq_max_pending[q] &&
 		    !txq->stopped) {
-			ieee80211_stop_queue(sc->hw, info->hw_queue);
+			if (ath9k_is_chanctx_enabled())
+				ieee80211_stop_queue(sc->hw, info->hw_queue);
+			else
+				ieee80211_stop_queue(sc->hw, q);
 			txq->stopped = true;
 		}
 	}
diff --git a/drivers/net/wireless/ath/dfs_pattern_detector.c b/drivers/net/wireless/ath/dfs_pattern_detector.c
index 650be79..cfd0554 100644
--- a/drivers/net/wireless/ath/dfs_pattern_detector.c
+++ b/drivers/net/wireless/ath/dfs_pattern_detector.c
@@ -86,7 +86,7 @@
 	FCC_PATTERN(1, 0, 5, 150, 230, 1, 23),
 	FCC_PATTERN(2, 6, 10, 200, 500, 1, 16),
 	FCC_PATTERN(3, 11, 20, 200, 500, 1, 12),
-	FCC_PATTERN(4, 50, 100, 1000, 2000, 20, 1),
+	FCC_PATTERN(4, 50, 100, 1000, 2000, 1, 20),
 	FCC_PATTERN(5, 0, 1, 333, 333, 1, 9),
 };
 
@@ -105,7 +105,7 @@
 	JP_PATTERN(4, 0, 5, 150, 230, 1, 23),
 	JP_PATTERN(5, 6, 10, 200, 500, 1, 16),
 	JP_PATTERN(6, 11, 20, 200, 500, 1, 12),
-	JP_PATTERN(7, 50, 100, 1000, 2000, 20, 1),
+	JP_PATTERN(7, 50, 100, 1000, 2000, 1, 20),
 	JP_PATTERN(5, 0, 1, 333, 333, 1, 9),
 };
 
diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c
index 415393d..06ea6cc 100644
--- a/drivers/net/wireless/ath/regd.c
+++ b/drivers/net/wireless/ath/regd.c
@@ -515,6 +515,7 @@
 	if (!request)
 		return;
 
+	reg->region = request->dfs_region;
 	switch (request->initiator) {
 	case NL80211_REGDOM_SET_BY_CORE:
 		/*
@@ -779,6 +780,19 @@
 		return SD_NO_CTL;
 	}
 
+	if (ath_regd_get_eepromRD(reg) == CTRY_DEFAULT) {
+		switch (reg->region) {
+		case NL80211_DFS_FCC:
+			return CTL_FCC;
+		case NL80211_DFS_ETSI:
+			return CTL_ETSI;
+		case NL80211_DFS_JP:
+			return CTL_MKK;
+		default:
+			break;
+		}
+	}
+
 	switch (band) {
 	case IEEE80211_BAND_2GHZ:
 		return reg->regpair->reg_2ghz_ctl;
diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c
index 0fc0b9f..38332a6 100644
--- a/drivers/net/wireless/ath/wil6210/cfg80211.c
+++ b/drivers/net/wireless/ath/wil6210/cfg80211.c
@@ -798,7 +798,7 @@
 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
 
 	mutex_lock(&wil->mutex);
-	wil6210_disconnect(wil, params->mac, false);
+	wil6210_disconnect(wil, params->mac, params->reason_code, false);
 	mutex_unlock(&wil->mutex);
 
 	return 0;
diff --git a/drivers/net/wireless/ath/wil6210/debug.c b/drivers/net/wireless/ath/wil6210/debug.c
index 8d99021..3249562 100644
--- a/drivers/net/wireless/ath/wil6210/debug.c
+++ b/drivers/net/wireless/ath/wil6210/debug.c
@@ -32,6 +32,23 @@
 	va_end(args);
 }
 
+void wil_err_ratelimited(struct wil6210_priv *wil, const char *fmt, ...)
+{
+	if (net_ratelimit()) {
+		struct net_device *ndev = wil_to_ndev(wil);
+		struct va_format vaf = {
+			.fmt = fmt,
+		};
+		va_list args;
+
+		va_start(args, fmt);
+		vaf.va = &args;
+		netdev_err(ndev, "%pV", &vaf);
+		trace_wil6210_log_err(&vaf);
+		va_end(args);
+	}
+}
+
 void wil_info(struct wil6210_priv *wil, const char *fmt, ...)
 {
 	struct net_device *ndev = wil_to_ndev(wil);
diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c
index 54a6ddc..4e6e145 100644
--- a/drivers/net/wireless/ath/wil6210/debugfs.c
+++ b/drivers/net/wireless/ath/wil6210/debugfs.c
@@ -573,8 +573,10 @@
 	if (!frame)
 		return -ENOMEM;
 
-	if (copy_from_user(frame, buf, len))
+	if (copy_from_user(frame, buf, len)) {
+		kfree(frame);
 		return -EIO;
+	}
 
 	params.buf = frame;
 	params.len = len;
@@ -614,8 +616,10 @@
 		return -ENOMEM;
 
 	rc = simple_write_to_buffer(wmi, len, ppos, buf, len);
-	if (rc < 0)
+	if (rc < 0) {
+		kfree(wmi);
 		return rc;
+	}
 
 	cmd = &wmi[1];
 	cmdid = le16_to_cpu(wmi->id);
diff --git a/drivers/net/wireless/ath/wil6210/fw.c b/drivers/net/wireless/ath/wil6210/fw.c
index 8c6f3b0..93c5cc1 100644
--- a/drivers/net/wireless/ath/wil6210/fw.c
+++ b/drivers/net/wireless/ath/wil6210/fw.c
@@ -15,7 +15,6 @@
  */
 #include <linux/firmware.h>
 #include <linux/module.h>
-#include <linux/pci.h>
 #include <linux/crc32.h>
 #include "wil6210.h"
 #include "fw.h"
diff --git a/drivers/net/wireless/ath/wil6210/fw_inc.c b/drivers/net/wireless/ath/wil6210/fw_inc.c
index 44cb71f..d4acf93 100644
--- a/drivers/net/wireless/ath/wil6210/fw_inc.c
+++ b/drivers/net/wireless/ath/wil6210/fw_inc.c
@@ -446,7 +446,7 @@
 		if (size >= sizeof(*hdr)) {
 			wil_err_fw(wil, "Stop at offset %ld"
 				   " record type %d [%zd bytes]\n",
-				   (const void *)hdr - data,
+				   (long)((const void *)hdr - data),
 				   le16_to_cpu(hdr->type), hdr_sz);
 		}
 		return -EINVAL;
@@ -471,7 +471,7 @@
 	size_t sz;
 	const void *d;
 
-	rc = request_firmware(&fw, name, wil_to_pcie_dev(wil));
+	rc = request_firmware(&fw, name, wil_to_dev(wil));
 	if (rc) {
 		wil_err_fw(wil, "Failed to load firmware %s\n", name);
 		return rc;
diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c
index 90f416f..4bcbd62 100644
--- a/drivers/net/wireless/ath/wil6210/interrupt.c
+++ b/drivers/net/wireless/ath/wil6210/interrupt.c
@@ -36,7 +36,8 @@
  */
 
 #define WIL6210_IRQ_DISABLE	(0xFFFFFFFFUL)
-#define WIL6210_IMC_RX		BIT_DMA_EP_RX_ICR_RX_DONE
+#define WIL6210_IMC_RX		(BIT_DMA_EP_RX_ICR_RX_DONE | \
+				 BIT_DMA_EP_RX_ICR_RX_HTRSH)
 #define WIL6210_IMC_TX		(BIT_DMA_EP_TX_ICR_TX_DONE | \
 				BIT_DMA_EP_TX_ICR_TX_DONE_N(0))
 #define WIL6210_IMC_MISC	(ISR_MISC_FW_READY | \
@@ -171,6 +172,7 @@
 	u32 isr = wil_ioread32_and_clear(wil->csr +
 					 HOSTADDR(RGF_DMA_EP_RX_ICR) +
 					 offsetof(struct RGF_ICR, ICR));
+	bool need_unmask = true;
 
 	trace_wil6210_irq_rx(isr);
 	wil_dbg_irq(wil, "ISR RX 0x%08x\n", isr);
@@ -182,12 +184,24 @@
 
 	wil6210_mask_irq_rx(wil);
 
-	if (isr & BIT_DMA_EP_RX_ICR_RX_DONE) {
+	/* RX_DONE and RX_HTRSH interrupts are the same if interrupt
+	 * moderation is not used. Interrupt moderation may cause RX
+	 * buffer overflow while RX_DONE is delayed. The required
+	 * action is always the same - should empty the accumulated
+	 * packets from the RX ring.
+	 */
+	if (isr & (BIT_DMA_EP_RX_ICR_RX_DONE | BIT_DMA_EP_RX_ICR_RX_HTRSH)) {
 		wil_dbg_irq(wil, "RX done\n");
-		isr &= ~BIT_DMA_EP_RX_ICR_RX_DONE;
+
+		if (isr & BIT_DMA_EP_RX_ICR_RX_HTRSH)
+			wil_err_ratelimited(wil, "Received \"Rx buffer is in risk "
+				"of overflow\" interrupt\n");
+
+		isr &= ~(BIT_DMA_EP_RX_ICR_RX_DONE | BIT_DMA_EP_RX_ICR_RX_HTRSH);
 		if (test_bit(wil_status_reset_done, &wil->status)) {
 			if (test_bit(wil_status_napi_en, &wil->status)) {
 				wil_dbg_txrx(wil, "NAPI(Rx) schedule\n");
+				need_unmask = false;
 				napi_schedule(&wil->napi_rx);
 			} else {
 				wil_err(wil, "Got Rx interrupt while "
@@ -204,6 +218,10 @@
 	/* Rx IRQ will be enabled when NAPI processing finished */
 
 	atomic_inc(&wil->isr_count_rx);
+
+	if (unlikely(need_unmask))
+		wil6210_unmask_irq_rx(wil);
+
 	return IRQ_HANDLED;
 }
 
@@ -213,6 +231,7 @@
 	u32 isr = wil_ioread32_and_clear(wil->csr +
 					 HOSTADDR(RGF_DMA_EP_TX_ICR) +
 					 offsetof(struct RGF_ICR, ICR));
+	bool need_unmask = true;
 
 	trace_wil6210_irq_tx(isr);
 	wil_dbg_irq(wil, "ISR TX 0x%08x\n", isr);
@@ -231,6 +250,7 @@
 		isr &= ~(BIT(25) - 1UL);
 		if (test_bit(wil_status_reset_done, &wil->status)) {
 			wil_dbg_txrx(wil, "NAPI(Tx) schedule\n");
+			need_unmask = false;
 			napi_schedule(&wil->napi_tx);
 		} else {
 			wil_err(wil, "Got Tx interrupt while in reset\n");
@@ -243,6 +263,10 @@
 	/* Tx IRQ will be enabled when NAPI processing finished */
 
 	atomic_inc(&wil->isr_count_tx);
+
+	if (unlikely(need_unmask))
+		wil6210_unmask_irq_tx(wil);
+
 	return IRQ_HANDLED;
 }
 
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index 6212983..8ff3fe3 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -67,6 +67,36 @@
 module_param_cb(mtu_max, &mtu_max_ops, &mtu_max, S_IRUGO);
 MODULE_PARM_DESC(mtu_max, " Max MTU value.");
 
+static uint rx_ring_order = WIL_RX_RING_SIZE_ORDER_DEFAULT;
+static uint tx_ring_order = WIL_TX_RING_SIZE_ORDER_DEFAULT;
+
+static int ring_order_set(const char *val, const struct kernel_param *kp)
+{
+	int ret;
+	uint x;
+
+	ret = kstrtouint(val, 0, &x);
+	if (ret)
+		return ret;
+
+	if ((x < WIL_RING_SIZE_ORDER_MIN) || (x > WIL_RING_SIZE_ORDER_MAX))
+		return -EINVAL;
+
+	*((uint *)kp->arg) = x;
+
+	return 0;
+}
+
+static struct kernel_param_ops ring_order_ops = {
+	.set = ring_order_set,
+	.get = param_get_uint,
+};
+
+module_param_cb(rx_ring_order, &ring_order_ops, &rx_ring_order, S_IRUGO);
+MODULE_PARM_DESC(rx_ring_order, " Rx ring order; size = 1 << order");
+module_param_cb(tx_ring_order, &ring_order_ops, &tx_ring_order, S_IRUGO);
+MODULE_PARM_DESC(tx_ring_order, " Tx ring order; size = 1 << order");
+
 #define RST_DELAY (20) /* msec, for loop in @wil_target_reset */
 #define RST_COUNT (1 + 1000/RST_DELAY) /* round up to be above 1 sec total */
 
@@ -104,7 +134,7 @@
 }
 
 static void wil_disconnect_cid(struct wil6210_priv *wil, int cid,
-			       bool from_event)
+			       u16 reason_code, bool from_event)
 {
 	uint i;
 	struct net_device *ndev = wil_to_ndev(wil);
@@ -117,8 +147,7 @@
 	sta->data_port_open = false;
 	if (sta->status != wil_sta_unused) {
 		if (!from_event)
-			wmi_disconnect_sta(wil, sta->addr,
-					   WLAN_REASON_DEAUTH_LEAVING);
+			wmi_disconnect_sta(wil, sta->addr, reason_code);
 
 		switch (wdev->iftype) {
 		case NL80211_IFTYPE_AP:
@@ -152,7 +181,7 @@
 }
 
 static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid,
-				bool from_event)
+				u16 reason_code, bool from_event)
 {
 	int cid = -ENOENT;
 	struct net_device *ndev = wil_to_ndev(wil);
@@ -167,10 +196,10 @@
 	}
 
 	if (cid >= 0) /* disconnect 1 peer */
-		wil_disconnect_cid(wil, cid, from_event);
+		wil_disconnect_cid(wil, cid, reason_code, from_event);
 	else /* disconnect all */
 		for (cid = 0; cid < WIL6210_MAX_CID; cid++)
-			wil_disconnect_cid(wil, cid, from_event);
+			wil_disconnect_cid(wil, cid, reason_code, from_event);
 
 	/* link state */
 	switch (wdev->iftype) {
@@ -179,8 +208,7 @@
 		wil_link_off(wil);
 		if (test_bit(wil_status_fwconnected, &wil->status)) {
 			clear_bit(wil_status_fwconnected, &wil->status);
-			cfg80211_disconnected(ndev,
-					      WLAN_STATUS_UNSPECIFIED_FAILURE,
+			cfg80211_disconnected(ndev, reason_code,
 					      NULL, 0, GFP_KERNEL);
 		} else if (test_bit(wil_status_fwconnecting, &wil->status)) {
 			cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0,
@@ -200,7 +228,7 @@
 			struct wil6210_priv, disconnect_worker);
 
 	mutex_lock(&wil->mutex);
-	_wil6210_disconnect(wil, NULL, false);
+	_wil6210_disconnect(wil, NULL, WLAN_REASON_UNSPECIFIED, false);
 	mutex_unlock(&wil->mutex);
 }
 
@@ -222,6 +250,7 @@
 
 	clear_bit(wil_status_fwready, &wil->status);
 	wil_err(wil, "Scan timeout detected, start fw error recovery\n");
+	wil->recovery_state = fw_recovery_pending;
 	schedule_work(&wil->fw_error_worker);
 }
 
@@ -333,7 +362,7 @@
 
 	wil_dbg_wmi(wil, "Configure for connection CID %d\n", cid);
 
-	rc = wil_vring_init_tx(wil, ringid, WIL6210_TX_RING_SIZE, cid, 0);
+	rc = wil_vring_init_tx(wil, ringid, 1 << tx_ring_order, cid, 0);
 	wil->pending_connect_cid = -1;
 	if (rc == 0) {
 		wil->sta[cid].status = wil_sta_connected;
@@ -392,18 +421,19 @@
  * wil6210_disconnect - disconnect one connection
  * @wil: driver context
  * @bssid: peer to disconnect, NULL to disconnect all
+ * @reason_code: Reason code for the Disassociation frame
  * @from_event: whether is invoked from FW event handler
  *
  * Disconnect and release associated resources. If invoked not from the
  * FW event handler, issue WMI command(s) to trigger MAC disconnect.
  */
 void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid,
-			bool from_event)
+			u16 reason_code, bool from_event)
 {
 	wil_dbg_misc(wil, "%s()\n", __func__);
 
 	del_timer_sync(&wil->connect_timer);
-	_wil6210_disconnect(wil, bssid, from_event);
+	_wil6210_disconnect(wil, bssid, reason_code, from_event);
 }
 
 void wil_priv_deinit(struct wil6210_priv *wil)
@@ -415,7 +445,7 @@
 	cancel_work_sync(&wil->disconnect_worker);
 	cancel_work_sync(&wil->fw_error_worker);
 	mutex_lock(&wil->mutex);
-	wil6210_disconnect(wil, NULL, false);
+	wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false);
 	mutex_unlock(&wil->mutex);
 	wmi_event_flush(wil);
 	destroy_workqueue(wil->wmi_wq_conn);
@@ -463,6 +493,9 @@
 
 	wil_halt_cpu(wil);
 
+	/* Clear Fw Download notification */
+	C(RGF_USER_USAGE_6, BIT(0));
+
 	if (is_sparrow) {
 		S(RGF_CAF_OSC_CONTROL, BIT_CAF_OSC_XTAL_EN);
 		/* XTAL stabilization should take about 3ms */
@@ -600,7 +633,7 @@
 	WARN_ON(test_bit(wil_status_napi_en, &wil->status));
 
 	cancel_work_sync(&wil->disconnect_worker);
-	wil6210_disconnect(wil, NULL, false);
+	wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false);
 
 	wil->status = 0; /* prevent NAPI from being scheduled */
 
@@ -705,7 +738,7 @@
 		return rc;
 
 	/* Rx VRING. After MAC and beacon */
-	rc = wil_rx_init(wil);
+	rc = wil_rx_init(wil, 1 << rx_ring_order);
 	if (rc)
 		return rc;
 
diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c
index c680906..e3f8bdc 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.c
+++ b/drivers/net/wireless/ath/wil6210/txrx.c
@@ -210,8 +210,6 @@
 	struct vring_rx_desc dd, *d = &dd;
 	volatile struct vring_rx_desc *_d = &vring->va[i].rx;
 	dma_addr_t pa;
-
-	/* TODO align */
 	struct sk_buff *skb = dev_alloc_skb(sz + headroom);
 
 	if (unlikely(!skb))
@@ -596,7 +594,7 @@
 	wil_rx_refill(wil, v->size);
 }
 
-int wil_rx_init(struct wil6210_priv *wil)
+int wil_rx_init(struct wil6210_priv *wil, u16 size)
 {
 	struct vring *vring = &wil->vring_rx;
 	int rc;
@@ -608,7 +606,7 @@
 		return -EINVAL;
 	}
 
-	vring->size = WIL6210_RX_RING_SIZE;
+	vring->size = size;
 	rc = wil_vring_alloc(wil, vring);
 	if (rc)
 		return rc;
@@ -928,8 +926,9 @@
 	wil_dbg_txrx(wil, "%s()\n", __func__);
 
 	if (avail < 1 + nr_frags) {
-		wil_err(wil, "Tx ring full. No space for %d fragments\n",
-			1 + nr_frags);
+		wil_err_ratelimited(wil,
+				    "Tx ring full. No space for %d fragments\n",
+				    1 + nr_frags);
 		return -ENOMEM;
 	}
 	_d = &vring->va[i].tx;
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index 95d3a06..c6ec5b9 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -49,8 +49,11 @@
 
 #define WIL6210_MEM_SIZE (2*1024*1024UL)
 
-#define WIL6210_RX_RING_SIZE	(128)
-#define WIL6210_TX_RING_SIZE	(512)
+#define WIL_RX_RING_SIZE_ORDER_DEFAULT	(9)
+#define WIL_TX_RING_SIZE_ORDER_DEFAULT	(9)
+/* limit ring size in range [32..32k] */
+#define WIL_RING_SIZE_ORDER_MIN	(5)
+#define WIL_RING_SIZE_ORDER_MAX	(15)
 #define WIL6210_MAX_TX_RINGS	(24) /* HW limit */
 #define WIL6210_MAX_CID		(8) /* HW limit */
 #define WIL6210_NAPI_BUDGET	(16) /* arbitrary */
@@ -126,6 +129,7 @@
 	#define BIT_DMA_EP_TX_ICR_TX_DONE_N(n)	BIT(n+1) /* n = [0..23] */
 #define RGF_DMA_EP_RX_ICR		(0x881bd0) /* struct RGF_ICR */
 	#define BIT_DMA_EP_RX_ICR_RX_DONE	BIT(0)
+	#define BIT_DMA_EP_RX_ICR_RX_HTRSH	BIT(1)
 #define RGF_DMA_EP_MISC_ICR		(0x881bec) /* struct RGF_ICR */
 	#define BIT_DMA_EP_MISC_ICR_RX_HTRSH	BIT(0)
 	#define BIT_DMA_EP_MISC_ICR_TX_NO_ACT	BIT(1)
@@ -468,13 +472,14 @@
 #define wdev_to_wil(w) (struct wil6210_priv *)(wdev_priv(w))
 #define wil_to_ndev(i) (wil_to_wdev(i)->netdev)
 #define ndev_to_wil(n) (wdev_to_wil(n->ieee80211_ptr))
-#define wil_to_pcie_dev(i) (&i->pdev->dev)
 
 __printf(2, 3)
 void wil_dbg_trace(struct wil6210_priv *wil, const char *fmt, ...);
 __printf(2, 3)
 void wil_err(struct wil6210_priv *wil, const char *fmt, ...);
 __printf(2, 3)
+void wil_err_ratelimited(struct wil6210_priv *wil, const char *fmt, ...);
+__printf(2, 3)
 void wil_info(struct wil6210_priv *wil, const char *fmt, ...);
 #define wil_dbg(wil, fmt, arg...) do { \
 	netdev_dbg(wil_to_ndev(wil), fmt, ##arg); \
@@ -586,9 +591,9 @@
 int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype, u8 chan);
 int wmi_pcp_stop(struct wil6210_priv *wil);
 void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid,
-			bool from_event);
+			u16 reason_code, bool from_event);
 
-int wil_rx_init(struct wil6210_priv *wil);
+int wil_rx_init(struct wil6210_priv *wil, u16 size);
 void wil_rx_fini(struct wil6210_priv *wil);
 
 /* TX API */
diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c
index bb1e066..63476c8 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.c
+++ b/drivers/net/wireless/ath/wil6210/wmi.c
@@ -478,15 +478,15 @@
 			       void *d, int len)
 {
 	struct wmi_disconnect_event *evt = d;
+	u16 reason_code = le16_to_cpu(evt->protocol_reason_status);
 
-	wil_dbg_wmi(wil, "Disconnect %pM reason %d proto %d wmi\n",
-		    evt->bssid,
-		    evt->protocol_reason_status, evt->disconnect_reason);
+	wil_dbg_wmi(wil, "Disconnect %pM reason [proto %d wmi %d]\n",
+		    evt->bssid, reason_code, evt->disconnect_reason);
 
 	wil->sinfo_gen++;
 
 	mutex_lock(&wil->mutex);
-	wil6210_disconnect(wil, evt->bssid, true);
+	wil6210_disconnect(wil, evt->bssid, reason_code, true);
 	mutex_unlock(&wil->mutex);
 }
 
diff --git a/drivers/net/wireless/b43/phy_common.c b/drivers/net/wireless/b43/phy_common.c
index 1dfc682..ee27b06 100644
--- a/drivers/net/wireless/b43/phy_common.c
+++ b/drivers/net/wireless/b43/phy_common.c
@@ -300,9 +300,7 @@
 
 void b43_phy_copy(struct b43_wldev *dev, u16 destreg, u16 srcreg)
 {
-	assert_mac_suspended(dev);
-	dev->phy.ops->phy_write(dev, destreg,
-		dev->phy.ops->phy_read(dev, srcreg));
+	b43_phy_write(dev, destreg, b43_phy_read(dev, srcreg));
 }
 
 void b43_phy_mask(struct b43_wldev *dev, u16 offset, u16 mask)
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
index e418969..0ea3125 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
@@ -299,6 +299,7 @@
 	primary_offset = ch->center_freq1 - ch->chan->center_freq;
 	switch (ch->width) {
 	case NL80211_CHAN_WIDTH_20:
+	case NL80211_CHAN_WIDTH_20_NOHT:
 		ch_inf.bw = BRCMU_CHAN_BW_20;
 		WARN_ON(primary_offset != 0);
 		break;
@@ -323,6 +324,10 @@
 				ch_inf.sb = BRCMU_CHAN_SB_LU;
 		}
 		break;
+	case NL80211_CHAN_WIDTH_80P80:
+	case NL80211_CHAN_WIDTH_160:
+	case NL80211_CHAN_WIDTH_5:
+	case NL80211_CHAN_WIDTH_10:
 	default:
 		WARN_ON_ONCE(1);
 	}
@@ -333,6 +338,7 @@
 	case IEEE80211_BAND_5GHZ:
 		ch_inf.band = BRCMU_CHAN_BAND_5G;
 		break;
+	case IEEE80211_BAND_60GHZ:
 	default:
 		WARN_ON_ONCE(1);
 	}
@@ -1809,6 +1815,7 @@
 		return -EIO;
 
 	clear_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
+	clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
 	cfg80211_disconnected(ndev, reason_code, NULL, 0, GFP_KERNEL);
 
 	memcpy(&scbval.ea, &profile->bssid, ETH_ALEN);
@@ -2926,7 +2933,7 @@
 		     struct brcmf_cfg80211_pmk_list *pmk_list, s32 err)
 {
 	int i, j;
-	int pmkid_len;
+	u32 pmkid_len;
 
 	pmkid_len = le32_to_cpu(pmk_list->pmkids.npmkid);
 
@@ -2954,8 +2961,7 @@
 	struct brcmf_if *ifp = netdev_priv(ndev);
 	struct pmkid_list *pmkids = &cfg->pmk_list->pmkids;
 	s32 err = 0;
-	int i;
-	int pmkid_len;
+	u32 pmkid_len, i;
 
 	brcmf_dbg(TRACE, "Enter\n");
 	if (!check_vif_up(ifp->vif))
@@ -2994,7 +3000,7 @@
 	struct brcmf_if *ifp = netdev_priv(ndev);
 	struct pmkid_list pmkid;
 	s32 err = 0;
-	int i, pmkid_len;
+	u32 pmkid_len, i;
 
 	brcmf_dbg(TRACE, "Enter\n");
 	if (!check_vif_up(ifp->vif))
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c
index 0f157f1..1ff787d 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c
@@ -262,8 +262,7 @@
 
 fail:
 	brcmf_dbg(TRACE, "failed: dev=%s\n", dev_name(fwctx->dev));
-	if (fwctx->code)
-		release_firmware(fwctx->code);
+	release_firmware(fwctx->code);
 	device_release_driver(fwctx->dev);
 	kfree(fwctx);
 }
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
index 02d39ce..9f783db 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
@@ -518,8 +518,7 @@
 		memcpy(buf, skb->data, (len < msgbuf->ioctl_resp_ret_len) ?
 				       len : msgbuf->ioctl_resp_ret_len);
 	}
-	if (skb)
-		brcmu_pkt_buf_free_skb(skb);
+	brcmu_pkt_buf_free_skb(skb);
 
 	return msgbuf->ioctl_resp_status;
 }
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c
index 72e87b5..0b0d51a 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c
@@ -670,7 +670,6 @@
 				  struct brcmf_sdio_dev *sdiodev)
 {
 	int i;
-	uint fw_len, nv_len;
 	char end;
 
 	for (i = 0; i < ARRAY_SIZE(brcmf_fwname_data); i++) {
@@ -684,25 +683,25 @@
 		return -ENODEV;
 	}
 
-	fw_len = sizeof(sdiodev->fw_name) - 1;
-	nv_len = sizeof(sdiodev->nvram_name) - 1;
 	/* check if firmware path is provided by module parameter */
 	if (brcmf_firmware_path[0] != '\0') {
-		strncpy(sdiodev->fw_name, brcmf_firmware_path, fw_len);
-		strncpy(sdiodev->nvram_name, brcmf_firmware_path, nv_len);
-		fw_len -= strlen(sdiodev->fw_name);
-		nv_len -= strlen(sdiodev->nvram_name);
+		strlcpy(sdiodev->fw_name, brcmf_firmware_path,
+			sizeof(sdiodev->fw_name));
+		strlcpy(sdiodev->nvram_name, brcmf_firmware_path,
+			sizeof(sdiodev->nvram_name));
 
 		end = brcmf_firmware_path[strlen(brcmf_firmware_path) - 1];
 		if (end != '/') {
-			strncat(sdiodev->fw_name, "/", fw_len);
-			strncat(sdiodev->nvram_name, "/", nv_len);
-			fw_len--;
-			nv_len--;
+			strlcat(sdiodev->fw_name, "/",
+				sizeof(sdiodev->fw_name));
+			strlcat(sdiodev->nvram_name, "/",
+				sizeof(sdiodev->nvram_name));
 		}
 	}
-	strncat(sdiodev->fw_name, brcmf_fwname_data[i].bin, fw_len);
-	strncat(sdiodev->nvram_name, brcmf_fwname_data[i].nv, nv_len);
+	strlcat(sdiodev->fw_name, brcmf_fwname_data[i].bin,
+		sizeof(sdiodev->fw_name));
+	strlcat(sdiodev->nvram_name, brcmf_fwname_data[i].nv,
+		sizeof(sdiodev->nvram_name));
 
 	return 0;
 }
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/debug.c b/drivers/net/wireless/brcm80211/brcmsmac/debug.c
index 19740c1..c9a8b93 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/debug.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/debug.c
@@ -30,6 +30,7 @@
 #include "main.h"
 #include "debug.h"
 #include "brcms_trace_events.h"
+#include "phy/phy_int.h"
 
 static struct dentry *root_folder;
 
@@ -74,20 +75,33 @@
 int brcms_debugfs_hardware_read(struct seq_file *s, void *data)
 {
 	struct brcms_pub *drvr = s->private;
+	struct brcms_hardware *hw = drvr->wlc->hw;
+	struct bcma_device *core = hw->d11core;
+	struct bcma_bus *bus = core->bus;
+	char boardrev[10];
 
-	seq_printf(s, "board vendor: %x\n"
-		   "board type: %x\n"
-		   "board revision: %x\n"
-		   "board flags: %x\n"
-		   "board flags2: %x\n"
-		   "firmware revision: %x\n",
-		   drvr->wlc->hw->d11core->bus->boardinfo.vendor,
-		   drvr->wlc->hw->d11core->bus->boardinfo.type,
-		   drvr->wlc->hw->boardrev,
-		   drvr->wlc->hw->boardflags,
-		   drvr->wlc->hw->boardflags2,
-		   drvr->wlc->ucode_rev);
-
+	seq_printf(s, "chipnum 0x%x\n"
+		   "chiprev 0x%x\n"
+		   "chippackage 0x%x\n"
+		   "corerev 0x%x\n"
+		   "boardid 0x%x\n"
+		   "boardvendor 0x%x\n"
+		   "boardrev %s\n"
+		   "boardflags 0x%x\n"
+		   "boardflags2 0x%x\n"
+		   "ucoderev 0x%x\n"
+		   "radiorev 0x%x\n"
+		   "phytype 0x%x\n"
+		   "phyrev 0x%x\n"
+		   "anarev 0x%x\n"
+		   "nvramrev %d\n",
+		   bus->chipinfo.id, bus->chipinfo.rev, bus->chipinfo.pkg,
+		   core->id.rev, bus->boardinfo.type, bus->boardinfo.vendor,
+		   brcmu_boardrev_str(hw->boardrev, boardrev),
+		   drvr->wlc->hw->boardflags, drvr->wlc->hw->boardflags2,
+		   drvr->wlc->ucode_rev, hw->band->radiorev,
+		   hw->band->phytype, hw->band->phyrev, hw->band->pi->ana_rev,
+		   hw->sromrev);
 	return 0;
 }
 
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/main.c b/drivers/net/wireless/brcm80211/brcmsmac/main.c
index bc9be78..a104d7a 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/main.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/main.c
@@ -445,18 +445,18 @@
 	kfree(wlc->protection);
 	kfree(wlc->stf);
 	kfree(wlc->bandstate[0]);
-	kfree(wlc->corestate->macstat_snapshot);
+	if (wlc->corestate)
+		kfree(wlc->corestate->macstat_snapshot);
 	kfree(wlc->corestate);
-	kfree(wlc->hw->bandstate[0]);
+	if (wlc->hw)
+		kfree(wlc->hw->bandstate[0]);
 	kfree(wlc->hw);
 	if (wlc->beacon)
 		dev_kfree_skb_any(wlc->beacon);
 	if (wlc->probe_resp)
 		dev_kfree_skb_any(wlc->probe_resp);
 
-	/* free the wlc */
 	kfree(wlc);
-	wlc = NULL;
 }
 
 static struct brcms_bss_cfg *brcms_c_bsscfg_malloc(uint unit)
@@ -1009,8 +1009,7 @@
 		if (txh)
 			trace_brcms_txdesc(&wlc->hw->d11core->dev, txh,
 					   sizeof(*txh));
-		if (p)
-			brcmu_pkt_buf_free_skb(p);
+		brcmu_pkt_buf_free_skb(p);
 	}
 
 	if (dma && queue < NFIFO) {
diff --git a/drivers/net/wireless/brcm80211/brcmutil/utils.c b/drivers/net/wireless/brcm80211/brcmutil/utils.c
index 0f7e1c7..906e89d 100644
--- a/drivers/net/wireless/brcm80211/brcmutil/utils.c
+++ b/drivers/net/wireless/brcm80211/brcmutil/utils.c
@@ -261,6 +261,21 @@
 }
 EXPORT_SYMBOL(brcmu_pktq_mdeq);
 
+/* Produce a human-readable string for boardrev */
+char *brcmu_boardrev_str(u32 brev, char *buf)
+{
+	char c;
+
+	if (brev < 0x100) {
+		snprintf(buf, 8, "%d.%d", (brev & 0xf0) >> 4, brev & 0xf);
+	} else {
+		c = (brev & 0xf000) == 0x1000 ? 'P' : 'A';
+		snprintf(buf, 8, "%c%03x", c, brev & 0xfff);
+	}
+	return buf;
+}
+EXPORT_SYMBOL(brcmu_boardrev_str);
+
 #if defined(DEBUG)
 /* pretty hex print a pkt buffer chain */
 void brcmu_prpkt(const char *msg, struct sk_buff *p0)
@@ -292,4 +307,5 @@
 	print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, data, size);
 }
 EXPORT_SYMBOL(brcmu_dbg_hex_dump);
+
 #endif				/* defined(DEBUG) */
diff --git a/drivers/net/wireless/brcm80211/include/brcmu_utils.h b/drivers/net/wireless/brcm80211/include/brcmu_utils.h
index 8ba445b..a043e29 100644
--- a/drivers/net/wireless/brcm80211/include/brcmu_utils.h
+++ b/drivers/net/wireless/brcm80211/include/brcmu_utils.h
@@ -218,4 +218,6 @@
 }
 #endif
 
+char *brcmu_boardrev_str(u32 brev, char *buf);
+
 #endif				/* _BRCMU_UTILS_H_ */
diff --git a/drivers/net/wireless/iwlwifi/Kconfig b/drivers/net/wireless/iwlwifi/Kconfig
index 139de90..ab019b4 100644
--- a/drivers/net/wireless/iwlwifi/Kconfig
+++ b/drivers/net/wireless/iwlwifi/Kconfig
@@ -59,7 +59,7 @@
 
 config IWLMVM
 	tristate "Intel Wireless WiFi MVM Firmware support"
-	select BACKPORT_WANT_DEV_COREDUMP
+	select WANT_DEV_COREDUMP
 	help
 	  This is the driver that supports the MVM firmware which is
 	  currently only available for 7260 and 3160 devices.
diff --git a/drivers/net/wireless/iwlwifi/dvm/commands.h b/drivers/net/wireless/iwlwifi/dvm/commands.h
index 751ae1d..7a34e4d 100644
--- a/drivers/net/wireless/iwlwifi/dvm/commands.h
+++ b/drivers/net/wireless/iwlwifi/dvm/commands.h
@@ -966,21 +966,21 @@
 
 
 /* WiFi queues mask */
-#define IWL_SCD_BK_MSK			cpu_to_le32(BIT(0))
-#define IWL_SCD_BE_MSK			cpu_to_le32(BIT(1))
-#define IWL_SCD_VI_MSK			cpu_to_le32(BIT(2))
-#define IWL_SCD_VO_MSK			cpu_to_le32(BIT(3))
-#define IWL_SCD_MGMT_MSK		cpu_to_le32(BIT(3))
+#define IWL_SCD_BK_MSK			BIT(0)
+#define IWL_SCD_BE_MSK			BIT(1)
+#define IWL_SCD_VI_MSK			BIT(2)
+#define IWL_SCD_VO_MSK			BIT(3)
+#define IWL_SCD_MGMT_MSK		BIT(3)
 
 /* PAN queues mask */
-#define IWL_PAN_SCD_BK_MSK		cpu_to_le32(BIT(4))
-#define IWL_PAN_SCD_BE_MSK		cpu_to_le32(BIT(5))
-#define IWL_PAN_SCD_VI_MSK		cpu_to_le32(BIT(6))
-#define IWL_PAN_SCD_VO_MSK		cpu_to_le32(BIT(7))
-#define IWL_PAN_SCD_MGMT_MSK		cpu_to_le32(BIT(7))
-#define IWL_PAN_SCD_MULTICAST_MSK	cpu_to_le32(BIT(8))
+#define IWL_PAN_SCD_BK_MSK		BIT(4)
+#define IWL_PAN_SCD_BE_MSK		BIT(5)
+#define IWL_PAN_SCD_VI_MSK		BIT(6)
+#define IWL_PAN_SCD_VO_MSK		BIT(7)
+#define IWL_PAN_SCD_MGMT_MSK		BIT(7)
+#define IWL_PAN_SCD_MULTICAST_MSK	BIT(8)
 
-#define IWL_AGG_TX_QUEUE_MSK		cpu_to_le32(0xffc00)
+#define IWL_AGG_TX_QUEUE_MSK		0xffc00
 
 #define IWL_DROP_ALL			BIT(1)
 
@@ -1005,12 +1005,17 @@
  *	1: Dump multiple MSDU according to PS, INVALID STA, TTL, TID disable.
  *	2: Dump all FIFO
  */
-struct iwl_txfifo_flush_cmd {
+struct iwl_txfifo_flush_cmd_v3 {
 	__le32 queue_control;
 	__le16 flush_control;
 	__le16 reserved;
 } __packed;
 
+struct iwl_txfifo_flush_cmd_v2 {
+	__le16 queue_control;
+	__le16 flush_control;
+} __packed;
+
 /*
  * REPLY_WEP_KEY = 0x20
  */
diff --git a/drivers/net/wireless/iwlwifi/dvm/lib.c b/drivers/net/wireless/iwlwifi/dvm/lib.c
index 02e4ede..1d2223d 100644
--- a/drivers/net/wireless/iwlwifi/dvm/lib.c
+++ b/drivers/net/wireless/iwlwifi/dvm/lib.c
@@ -137,37 +137,38 @@
  */
 int iwlagn_txfifo_flush(struct iwl_priv *priv, u32 scd_q_msk)
 {
-	struct iwl_txfifo_flush_cmd flush_cmd;
-	struct iwl_host_cmd cmd = {
-		.id = REPLY_TXFIFO_FLUSH,
-		.len = { sizeof(struct iwl_txfifo_flush_cmd), },
-		.data = { &flush_cmd, },
+	struct iwl_txfifo_flush_cmd_v3 flush_cmd_v3 = {
+		.flush_control = cpu_to_le16(IWL_DROP_ALL),
+	};
+	struct iwl_txfifo_flush_cmd_v2 flush_cmd_v2 = {
+		.flush_control = cpu_to_le16(IWL_DROP_ALL),
 	};
 
-	memset(&flush_cmd, 0, sizeof(flush_cmd));
+	u32 queue_control = IWL_SCD_VO_MSK | IWL_SCD_VI_MSK |
+			    IWL_SCD_BE_MSK | IWL_SCD_BK_MSK | IWL_SCD_MGMT_MSK;
 
-	flush_cmd.queue_control = IWL_SCD_VO_MSK | IWL_SCD_VI_MSK |
-				  IWL_SCD_BE_MSK | IWL_SCD_BK_MSK |
-				  IWL_SCD_MGMT_MSK;
 	if ((priv->valid_contexts != BIT(IWL_RXON_CTX_BSS)))
-		flush_cmd.queue_control |= IWL_PAN_SCD_VO_MSK |
-					   IWL_PAN_SCD_VI_MSK |
-					   IWL_PAN_SCD_BE_MSK |
-					   IWL_PAN_SCD_BK_MSK |
-					   IWL_PAN_SCD_MGMT_MSK |
-					   IWL_PAN_SCD_MULTICAST_MSK;
+		queue_control |= IWL_PAN_SCD_VO_MSK | IWL_PAN_SCD_VI_MSK |
+				 IWL_PAN_SCD_BE_MSK | IWL_PAN_SCD_BK_MSK |
+				 IWL_PAN_SCD_MGMT_MSK |
+				 IWL_PAN_SCD_MULTICAST_MSK;
 
 	if (priv->nvm_data->sku_cap_11n_enable)
-		flush_cmd.queue_control |= IWL_AGG_TX_QUEUE_MSK;
+		queue_control |= IWL_AGG_TX_QUEUE_MSK;
 
 	if (scd_q_msk)
-		flush_cmd.queue_control = cpu_to_le32(scd_q_msk);
+		queue_control = scd_q_msk;
 
-	IWL_DEBUG_INFO(priv, "queue control: 0x%x\n",
-		       flush_cmd.queue_control);
-	flush_cmd.flush_control = cpu_to_le16(IWL_DROP_ALL);
+	IWL_DEBUG_INFO(priv, "queue control: 0x%x\n", queue_control);
+	flush_cmd_v3.queue_control = cpu_to_le32(queue_control);
+	flush_cmd_v2.queue_control = cpu_to_le16((u16)queue_control);
 
-	return iwl_dvm_send_cmd(priv, &cmd);
+	if (IWL_UCODE_API(priv->fw->ucode_ver) > 2)
+		return iwl_dvm_send_cmd_pdu(priv, REPLY_TXFIFO_FLUSH, 0,
+					    sizeof(flush_cmd_v3),
+					    &flush_cmd_v3);
+	return iwl_dvm_send_cmd_pdu(priv, REPLY_TXFIFO_FLUSH, 0,
+				    sizeof(flush_cmd_v2), &flush_cmd_v2);
 }
 
 void iwlagn_dev_txfifo_flush(struct iwl_priv *priv)
diff --git a/drivers/net/wireless/iwlwifi/iwl-7000.c b/drivers/net/wireless/iwlwifi/iwl-7000.c
index b04b885..e5be2d2 100644
--- a/drivers/net/wireless/iwlwifi/iwl-7000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-7000.c
@@ -73,12 +73,12 @@
 #define IWL3160_UCODE_API_MAX	10
 
 /* Oldest version we won't warn about */
-#define IWL7260_UCODE_API_OK	9
-#define IWL3160_UCODE_API_OK	9
+#define IWL7260_UCODE_API_OK	10
+#define IWL3160_UCODE_API_OK	10
 
 /* Lowest firmware API version supported */
-#define IWL7260_UCODE_API_MIN	8
-#define IWL3160_UCODE_API_MIN	8
+#define IWL7260_UCODE_API_MIN	9
+#define IWL3160_UCODE_API_MIN	9
 
 /* NVM versions */
 #define IWL7260_NVM_VERSION		0x0a1d
@@ -89,6 +89,8 @@
 #define IWL3165_TX_POWER_VERSION	0xffff /* meaningless */
 #define IWL7265_NVM_VERSION		0x0a1d
 #define IWL7265_TX_POWER_VERSION	0xffff /* meaningless */
+#define IWL7265D_NVM_VERSION		0x0c11
+#define IWL7265_TX_POWER_VERSION	0xffff /* meaningless */
 
 #define IWL7260_FW_PRE "iwlwifi-7260-"
 #define IWL7260_MODULE_FIRMWARE(api) IWL7260_FW_PRE __stringify(api) ".ucode"
@@ -102,6 +104,9 @@
 #define IWL7265_FW_PRE "iwlwifi-7265-"
 #define IWL7265_MODULE_FIRMWARE(api) IWL7265_FW_PRE __stringify(api) ".ucode"
 
+#define IWL7265D_FW_PRE "iwlwifi-7265D-"
+#define IWL7265D_MODULE_FIRMWARE(api) IWL7265_FW_PRE __stringify(api) ".ucode"
+
 #define NVM_HW_SECTION_NUM_FAMILY_7000		0
 
 static const struct iwl_base_params iwl7000_base_params = {
@@ -132,8 +137,8 @@
 	.base_params = &iwl7000_base_params,			\
 	.led_mode = IWL_LED_RF_STATE,				\
 	.nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_7000,	\
-	.non_shared_ant = ANT_A
-
+	.non_shared_ant = ANT_A,				\
+	.max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
 
 const struct iwl_cfg iwl7260_2ac_cfg = {
 	.name = "Intel(R) Dual Band Wireless AC 7260",
@@ -267,7 +272,38 @@
 	.pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
 };
 
+const struct iwl_cfg iwl7265d_2ac_cfg = {
+	.name = "Intel(R) Dual Band Wireless AC 7265",
+	.fw_name_pre = IWL7265D_FW_PRE,
+	IWL_DEVICE_7000,
+	.ht_params = &iwl7265_ht_params,
+	.nvm_ver = IWL7265D_NVM_VERSION,
+	.nvm_calib_ver = IWL7265_TX_POWER_VERSION,
+	.pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
+};
+
+const struct iwl_cfg iwl7265d_2n_cfg = {
+	.name = "Intel(R) Dual Band Wireless N 7265",
+	.fw_name_pre = IWL7265D_FW_PRE,
+	IWL_DEVICE_7000,
+	.ht_params = &iwl7265_ht_params,
+	.nvm_ver = IWL7265D_NVM_VERSION,
+	.nvm_calib_ver = IWL7265_TX_POWER_VERSION,
+	.pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
+};
+
+const struct iwl_cfg iwl7265d_n_cfg = {
+	.name = "Intel(R) Wireless N 7265",
+	.fw_name_pre = IWL7265D_FW_PRE,
+	IWL_DEVICE_7000,
+	.ht_params = &iwl7265_ht_params,
+	.nvm_ver = IWL7265D_NVM_VERSION,
+	.nvm_calib_ver = IWL7265_TX_POWER_VERSION,
+	.pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
+};
+
 MODULE_FIRMWARE(IWL7260_MODULE_FIRMWARE(IWL7260_UCODE_API_OK));
 MODULE_FIRMWARE(IWL3160_MODULE_FIRMWARE(IWL3160_UCODE_API_OK));
 MODULE_FIRMWARE(IWL3165_MODULE_FIRMWARE(IWL3160_UCODE_API_OK));
 MODULE_FIRMWARE(IWL7265_MODULE_FIRMWARE(IWL7260_UCODE_API_OK));
+MODULE_FIRMWARE(IWL7265D_MODULE_FIRMWARE(IWL7260_UCODE_API_OK));
diff --git a/drivers/net/wireless/iwlwifi/iwl-8000.c b/drivers/net/wireless/iwlwifi/iwl-8000.c
index 896ea90..bf0a95c 100644
--- a/drivers/net/wireless/iwlwifi/iwl-8000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-8000.c
@@ -72,10 +72,10 @@
 #define IWL8000_UCODE_API_MAX	10
 
 /* Oldest version we won't warn about */
-#define IWL8000_UCODE_API_OK	8
+#define IWL8000_UCODE_API_OK	10
 
 /* Lowest firmware API version supported */
-#define IWL8000_UCODE_API_MIN	8
+#define IWL8000_UCODE_API_MIN	9
 
 /* NVM versions */
 #define IWL8000_NVM_VERSION		0x0a1d
@@ -91,6 +91,10 @@
 /* Max SDIO RX aggregation size of the ADDBA request/response */
 #define MAX_RX_AGG_SIZE_8260_SDIO	28
 
+/* Max A-MPDU exponent for HT and VHT */
+#define MAX_HT_AMPDU_EXPONENT_8260_SDIO	IEEE80211_HT_MAX_AMPDU_32K
+#define MAX_VHT_AMPDU_EXPONENT_8260_SDIO	IEEE80211_VHT_MAX_AMPDU_32K
+
 static const struct iwl_base_params iwl8000_base_params = {
 	.eeprom_size = OTP_LOW_IMAGE_SIZE_FAMILY_8000,
 	.num_of_queues = IWLAGN_NUM_QUEUES,
@@ -119,6 +123,7 @@
 	.base_params = &iwl8000_base_params,			\
 	.led_mode = IWL_LED_RF_STATE,				\
 	.nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_8000,	\
+	.d0i3 = true,						\
 	.non_shared_ant = ANT_A
 
 const struct iwl_cfg iwl8260_2n_cfg = {
@@ -137,6 +142,7 @@
 	.ht_params = &iwl8000_ht_params,
 	.nvm_ver = IWL8000_NVM_VERSION,
 	.nvm_calib_ver = IWL8000_TX_POWER_VERSION,
+	.max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
 };
 
 const struct iwl_cfg iwl8260_2ac_sdio_cfg = {
@@ -149,6 +155,23 @@
 	.default_nvm_file = DEFAULT_NVM_FILE_FAMILY_8000,
 	.max_rx_agg_size = MAX_RX_AGG_SIZE_8260_SDIO,
 	.disable_dummy_notification = true,
+	.max_ht_ampdu_exponent  = MAX_HT_AMPDU_EXPONENT_8260_SDIO,
+	.max_vht_ampdu_exponent = MAX_VHT_AMPDU_EXPONENT_8260_SDIO,
+};
+
+const struct iwl_cfg iwl4165_2ac_sdio_cfg = {
+	.name = "Intel(R) Dual Band Wireless-AC 4165",
+	.fw_name_pre = IWL8000_FW_PRE,
+	IWL_DEVICE_8000,
+	.ht_params = &iwl8000_ht_params,
+	.nvm_ver = IWL8000_NVM_VERSION,
+	.nvm_calib_ver = IWL8000_TX_POWER_VERSION,
+	.default_nvm_file = DEFAULT_NVM_FILE_FAMILY_8000,
+	.max_rx_agg_size = MAX_RX_AGG_SIZE_8260_SDIO,
+	.bt_shared_single_ant = true,
+	.disable_dummy_notification = true,
+	.max_ht_ampdu_exponent  = MAX_HT_AMPDU_EXPONENT_8260_SDIO,
+	.max_vht_ampdu_exponent = MAX_VHT_AMPDU_EXPONENT_8260_SDIO,
 };
 
 MODULE_FIRMWARE(IWL8000_MODULE_FIRMWARE(IWL8000_UCODE_API_OK));
diff --git a/drivers/net/wireless/iwlwifi/iwl-config.h b/drivers/net/wireless/iwlwifi/iwl-config.h
index f8aa9cf..3a4b9c7 100644
--- a/drivers/net/wireless/iwlwifi/iwl-config.h
+++ b/drivers/net/wireless/iwlwifi/iwl-config.h
@@ -257,6 +257,10 @@
  * @pwr_tx_backoffs: translation table between power limits and backoffs
  * @max_rx_agg_size: max RX aggregation size of the ADDBA request/response
  * @max_tx_agg_size: max TX aggregation size of the ADDBA request/response
+ * @max_ht_ampdu_factor: the exponent of the max length of A-MPDU that the
+ *	station can receive in HT
+ * @max_vht_ampdu_exponent: the exponent of the max length of A-MPDU that the
+ *	station can receive in VHT
  *
  * We enable the driver to be backward compatible wrt. hardware features.
  * API differences in uCode shouldn't be handled here but through TLVs
@@ -297,6 +301,8 @@
 	unsigned int max_rx_agg_size;
 	bool disable_dummy_notification;
 	unsigned int max_tx_agg_size;
+	unsigned int max_ht_ampdu_exponent;
+	unsigned int max_vht_ampdu_exponent;
 };
 
 /*
@@ -358,9 +364,14 @@
 extern const struct iwl_cfg iwl7265_2ac_cfg;
 extern const struct iwl_cfg iwl7265_2n_cfg;
 extern const struct iwl_cfg iwl7265_n_cfg;
+extern const struct iwl_cfg iwl7265d_2ac_cfg;
+extern const struct iwl_cfg iwl7265d_2n_cfg;
+extern const struct iwl_cfg iwl7265d_n_cfg;
 extern const struct iwl_cfg iwl8260_2n_cfg;
 extern const struct iwl_cfg iwl8260_2ac_cfg;
 extern const struct iwl_cfg iwl8260_2ac_sdio_cfg;
+extern const struct iwl_cfg iwl4265_2ac_sdio_cfg;
+extern const struct iwl_cfg iwl4165_2ac_sdio_cfg;
 #endif /* CONFIG_IWLMVM */
 
 #endif /* __IWL_CONFIG_H__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-csr.h b/drivers/net/wireless/iwlwifi/iwl-csr.h
index 3f6f015..aff63c3 100644
--- a/drivers/net/wireless/iwlwifi/iwl-csr.h
+++ b/drivers/net/wireless/iwlwifi/iwl-csr.h
@@ -129,6 +129,8 @@
 #define CSR_UCODE_DRV_GP1_CLR   (CSR_BASE+0x05c)
 #define CSR_UCODE_DRV_GP2       (CSR_BASE+0x060)
 
+#define CSR_MBOX_SET_REG	(CSR_BASE + 0x88)
+
 #define CSR_LED_REG             (CSR_BASE+0x094)
 #define CSR_DRAM_INT_TBL_REG	(CSR_BASE+0x0A0)
 #define CSR_MAC_SHADOW_REG_CTRL	(CSR_BASE+0x0A8) /* 6000 and up */
@@ -184,6 +186,8 @@
 #define CSR_HW_IF_CONFIG_REG_PREPARE		  (0x08000000) /* WAKE_ME */
 #define CSR_HW_IF_CONFIG_REG_PERSIST_MODE	  (0x40000000) /* PERSISTENCE */
 
+#define CSR_MBOX_SET_REG_OS_ALIVE		BIT(5)
+
 #define CSR_INT_PERIODIC_DIS			(0x00) /* disable periodic int*/
 #define CSR_INT_PERIODIC_ENA			(0xFF) /* 255*32 usec ~ 8 msec*/
 
@@ -305,23 +309,24 @@
 };
 
 
-#define CSR_HW_REV_TYPE_MSK            (0x000FFF0)
-#define CSR_HW_REV_TYPE_5300           (0x0000020)
-#define CSR_HW_REV_TYPE_5350           (0x0000030)
-#define CSR_HW_REV_TYPE_5100           (0x0000050)
-#define CSR_HW_REV_TYPE_5150           (0x0000040)
-#define CSR_HW_REV_TYPE_1000           (0x0000060)
-#define CSR_HW_REV_TYPE_6x00           (0x0000070)
-#define CSR_HW_REV_TYPE_6x50           (0x0000080)
-#define CSR_HW_REV_TYPE_6150           (0x0000084)
-#define CSR_HW_REV_TYPE_6x05	       (0x00000B0)
-#define CSR_HW_REV_TYPE_6x30	       CSR_HW_REV_TYPE_6x05
-#define CSR_HW_REV_TYPE_6x35	       CSR_HW_REV_TYPE_6x05
-#define CSR_HW_REV_TYPE_2x30	       (0x00000C0)
-#define CSR_HW_REV_TYPE_2x00	       (0x0000100)
-#define CSR_HW_REV_TYPE_105	       (0x0000110)
-#define CSR_HW_REV_TYPE_135	       (0x0000120)
-#define CSR_HW_REV_TYPE_NONE           (0x00001F0)
+#define CSR_HW_REV_TYPE_MSK		(0x000FFF0)
+#define CSR_HW_REV_TYPE_5300		(0x0000020)
+#define CSR_HW_REV_TYPE_5350		(0x0000030)
+#define CSR_HW_REV_TYPE_5100		(0x0000050)
+#define CSR_HW_REV_TYPE_5150		(0x0000040)
+#define CSR_HW_REV_TYPE_1000		(0x0000060)
+#define CSR_HW_REV_TYPE_6x00		(0x0000070)
+#define CSR_HW_REV_TYPE_6x50		(0x0000080)
+#define CSR_HW_REV_TYPE_6150		(0x0000084)
+#define CSR_HW_REV_TYPE_6x05		(0x00000B0)
+#define CSR_HW_REV_TYPE_6x30		CSR_HW_REV_TYPE_6x05
+#define CSR_HW_REV_TYPE_6x35		CSR_HW_REV_TYPE_6x05
+#define CSR_HW_REV_TYPE_2x30		(0x00000C0)
+#define CSR_HW_REV_TYPE_2x00		(0x0000100)
+#define CSR_HW_REV_TYPE_105		(0x0000110)
+#define CSR_HW_REV_TYPE_135		(0x0000120)
+#define CSR_HW_REV_TYPE_7265D		(0x0000210)
+#define CSR_HW_REV_TYPE_NONE		(0x00001F0)
 
 /* EEPROM REG */
 #define CSR_EEPROM_REG_READ_VALID_MSK	(0x00000001)
diff --git a/drivers/net/wireless/iwlwifi/iwl-debug.h b/drivers/net/wireless/iwlwifi/iwl-debug.h
index 0a70bcd..6842545 100644
--- a/drivers/net/wireless/iwlwifi/iwl-debug.h
+++ b/drivers/net/wireless/iwlwifi/iwl-debug.h
@@ -143,7 +143,7 @@
 #define IWL_DL_INFO		0x00000001
 #define IWL_DL_MAC80211		0x00000002
 #define IWL_DL_HCMD		0x00000004
-#define IWL_DL_STATE		0x00000008
+#define IWL_DL_TDLS		0x00000008
 /* 0x000000F0 - 0x00000010 */
 #define IWL_DL_QUOTA		0x00000010
 #define IWL_DL_TE		0x00000020
@@ -180,6 +180,7 @@
 #define IWL_DL_TX_QUEUES	0x80000000
 
 #define IWL_DEBUG_INFO(p, f, a...)	IWL_DEBUG(p, IWL_DL_INFO, f, ## a)
+#define IWL_DEBUG_TDLS(p, f, a...)	IWL_DEBUG(p, IWL_DL_TDLS, f, ## a)
 #define IWL_DEBUG_MAC80211(p, f, a...)	IWL_DEBUG(p, IWL_DL_MAC80211, f, ## a)
 #define IWL_DEBUG_EXTERNAL(p, f, a...)	IWL_DEBUG(p, IWL_DL_EXTERNAL, f, ## a)
 #define IWL_DEBUG_TEMP(p, f, a...)	IWL_DEBUG(p, IWL_DL_TEMP, f, ## a)
diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c
index d9fa8e0..38de151 100644
--- a/drivers/net/wireless/iwlwifi/iwl-drv.c
+++ b/drivers/net/wireless/iwlwifi/iwl-drv.c
@@ -78,9 +78,6 @@
 #include "iwl-config.h"
 #include "iwl-modparams.h"
 
-/* private includes */
-#include "iwl-fw-file.h"
-
 /******************************************************************************
  *
  * module boiler plate
@@ -187,6 +184,11 @@
 static void iwl_dealloc_ucode(struct iwl_drv *drv)
 {
 	int i;
+
+	kfree(drv->fw.dbg_dest_tlv);
+	for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_conf_tlv); i++)
+		kfree(drv->fw.dbg_conf_tlv[i]);
+
 	for (i = 0; i < IWL_UCODE_TYPE_MAX; i++)
 		iwl_free_fw_img(drv, drv->fw.img + i);
 }
@@ -248,6 +250,9 @@
 	/*
 	 * Starting 8000B - FW name format has changed. This overwrites the
 	 * previous name and uses the new format.
+	 *
+	 * TODO:
+	 * Once there is only one supported step for 8000 family - delete this!
 	 */
 	if (drv->trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) {
 		char rev_step[2] = {
@@ -258,6 +263,13 @@
 		if (CSR_HW_REV_STEP(drv->trans->hw_rev) == SILICON_A_STEP)
 			rev_step[0] = 0;
 
+		/*
+		 * If hw_rev wasn't set yet - default as B-step. If it IS A-step
+		 * we'll reload that FW later instead.
+		 */
+		if (drv->trans->hw_rev == 0)
+			rev_step[0] = 'B';
+
 		snprintf(drv->firmware_name, sizeof(drv->firmware_name),
 			 "%s%s-%s.ucode", name_pre, rev_step, tag);
 	}
@@ -301,6 +313,11 @@
 
 	u32 init_evtlog_ptr, init_evtlog_size, init_errlog_ptr;
 	u32 inst_evtlog_ptr, inst_evtlog_size, inst_errlog_ptr;
+
+	/* FW debug data parsed for driver usage */
+	struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv;
+	struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_MAX];
+	size_t dbg_conf_tlv_len[FW_DBG_MAX];
 };
 
 /*
@@ -574,6 +591,8 @@
 	char buildstr[25];
 	u32 build;
 	int num_of_cpus;
+	bool usniffer_images = false;
+	bool usniffer_req = false;
 
 	if (len < sizeof(*ucode)) {
 		IWL_ERR(drv, "uCode has invalid length: %zd\n", len);
@@ -846,12 +865,79 @@
 			capa->n_scan_channels =
 				le32_to_cpup((__le32 *)tlv_data);
 			break;
+		case IWL_UCODE_TLV_FW_DBG_DEST: {
+			struct iwl_fw_dbg_dest_tlv *dest = (void *)tlv_data;
+
+			if (pieces->dbg_dest_tlv) {
+				IWL_ERR(drv,
+					"dbg destination ignored, already exists\n");
+				break;
+			}
+
+			pieces->dbg_dest_tlv = dest;
+			IWL_INFO(drv, "Found debug destination: %s\n",
+				 get_fw_dbg_mode_string(dest->monitor_mode));
+
+			drv->fw.dbg_dest_reg_num =
+				tlv_len - offsetof(struct iwl_fw_dbg_dest_tlv,
+						   reg_ops);
+			drv->fw.dbg_dest_reg_num /=
+				sizeof(drv->fw.dbg_dest_tlv->reg_ops[0]);
+
+			break;
+			}
+		case IWL_UCODE_TLV_FW_DBG_CONF: {
+			struct iwl_fw_dbg_conf_tlv *conf = (void *)tlv_data;
+
+			if (!pieces->dbg_dest_tlv) {
+				IWL_ERR(drv,
+					"Ignore dbg config %d - no destination configured\n",
+					conf->id);
+				break;
+			}
+
+			if (conf->id >= ARRAY_SIZE(drv->fw.dbg_conf_tlv)) {
+				IWL_ERR(drv,
+					"Skip unknown configuration: %d\n",
+					conf->id);
+				break;
+			}
+
+			if (pieces->dbg_conf_tlv[conf->id]) {
+				IWL_ERR(drv,
+					"Ignore duplicate dbg config %d\n",
+					conf->id);
+				break;
+			}
+
+			if (conf->usniffer)
+				usniffer_req = true;
+
+			IWL_INFO(drv, "Found debug configuration: %d\n",
+				 conf->id);
+
+			pieces->dbg_conf_tlv[conf->id] = conf;
+			pieces->dbg_conf_tlv_len[conf->id] = tlv_len;
+			break;
+			}
+		case IWL_UCODE_TLV_SEC_RT_USNIFFER:
+			usniffer_images = true;
+			iwl_store_ucode_sec(pieces, tlv_data,
+					    IWL_UCODE_REGULAR_USNIFFER,
+					    tlv_len);
+			break;
 		default:
 			IWL_DEBUG_INFO(drv, "unknown TLV: %d\n", tlv_type);
 			break;
 		}
 	}
 
+	if (usniffer_req && !usniffer_images) {
+		IWL_ERR(drv,
+			"user selected to work with usniffer but usniffer image isn't available in ucode package\n");
+		return -EINVAL;
+	}
+
 	if (len) {
 		IWL_ERR(drv, "invalid TLV after parsing: %zd\n", len);
 		iwl_print_hex_dump(drv, IWL_DL_FW, (u8 *)data, len);
@@ -989,13 +1075,14 @@
 	struct iwl_ucode_header *ucode;
 	struct iwlwifi_opmode_table *op;
 	int err;
-	struct iwl_firmware_pieces pieces;
+	struct iwl_firmware_pieces *pieces;
 	const unsigned int api_max = drv->cfg->ucode_api_max;
 	unsigned int api_ok = drv->cfg->ucode_api_ok;
 	const unsigned int api_min = drv->cfg->ucode_api_min;
 	u32 api_ver;
 	int i;
 	bool load_module = false;
+	u32 hw_rev = drv->trans->hw_rev;
 
 	fw->ucode_capa.max_probe_length = IWL_DEFAULT_MAX_PROBE_LENGTH;
 	fw->ucode_capa.standard_phy_calibration_size =
@@ -1005,7 +1092,9 @@
 	if (!api_ok)
 		api_ok = api_max;
 
-	memset(&pieces, 0, sizeof(pieces));
+	pieces = kzalloc(sizeof(*pieces), GFP_KERNEL);
+	if (!pieces)
+		return;
 
 	if (!ucode_raw) {
 		if (drv->fw_index <= api_ok)
@@ -1028,10 +1117,10 @@
 	ucode = (struct iwl_ucode_header *)ucode_raw->data;
 
 	if (ucode->ver)
-		err = iwl_parse_v1_v2_firmware(drv, ucode_raw, &pieces);
+		err = iwl_parse_v1_v2_firmware(drv, ucode_raw, pieces);
 	else
-		err = iwl_parse_tlv_firmware(drv, ucode_raw, &pieces,
-					   &fw->ucode_capa);
+		err = iwl_parse_tlv_firmware(drv, ucode_raw, pieces,
+					     &fw->ucode_capa);
 
 	if (err)
 		goto try_again;
@@ -1071,7 +1160,7 @@
 	 * In mvm uCode there is no difference between data and instructions
 	 * sections.
 	 */
-	if (!fw->mvm_fw && validate_sec_sizes(drv, &pieces, drv->cfg))
+	if (!fw->mvm_fw && validate_sec_sizes(drv, pieces, drv->cfg))
 		goto try_again;
 
 	/* Allocate ucode buffers for card's bus-master loading ... */
@@ -1080,9 +1169,33 @@
 	 * 1) unmodified from disk
 	 * 2) backup cache for save/restore during power-downs */
 	for (i = 0; i < IWL_UCODE_TYPE_MAX; i++)
-		if (iwl_alloc_ucode(drv, &pieces, i))
+		if (iwl_alloc_ucode(drv, pieces, i))
 			goto out_free_fw;
 
+	if (pieces->dbg_dest_tlv) {
+		drv->fw.dbg_dest_tlv =
+			kmemdup(pieces->dbg_dest_tlv,
+				sizeof(*pieces->dbg_dest_tlv) +
+				sizeof(pieces->dbg_dest_tlv->reg_ops[0]) *
+				drv->fw.dbg_dest_reg_num, GFP_KERNEL);
+
+		if (!drv->fw.dbg_dest_tlv)
+			goto out_free_fw;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_conf_tlv); i++) {
+		if (pieces->dbg_conf_tlv[i]) {
+			drv->fw.dbg_conf_tlv_len[i] =
+				pieces->dbg_conf_tlv_len[i];
+			drv->fw.dbg_conf_tlv[i] =
+				kmemdup(pieces->dbg_conf_tlv[i],
+					drv->fw.dbg_conf_tlv_len[i],
+					GFP_KERNEL);
+			if (!drv->fw.dbg_conf_tlv[i])
+				goto out_free_fw;
+		}
+	}
+
 	/* Now that we can no longer fail, copy information */
 
 	/*
@@ -1090,20 +1203,20 @@
 	 * for each event, which is of mode 1 (including timestamp) for all
 	 * new microcodes that include this information.
 	 */
-	fw->init_evtlog_ptr = pieces.init_evtlog_ptr;
-	if (pieces.init_evtlog_size)
-		fw->init_evtlog_size = (pieces.init_evtlog_size - 16)/12;
+	fw->init_evtlog_ptr = pieces->init_evtlog_ptr;
+	if (pieces->init_evtlog_size)
+		fw->init_evtlog_size = (pieces->init_evtlog_size - 16)/12;
 	else
 		fw->init_evtlog_size =
 			drv->cfg->base_params->max_event_log_size;
-	fw->init_errlog_ptr = pieces.init_errlog_ptr;
-	fw->inst_evtlog_ptr = pieces.inst_evtlog_ptr;
-	if (pieces.inst_evtlog_size)
-		fw->inst_evtlog_size = (pieces.inst_evtlog_size - 16)/12;
+	fw->init_errlog_ptr = pieces->init_errlog_ptr;
+	fw->inst_evtlog_ptr = pieces->inst_evtlog_ptr;
+	if (pieces->inst_evtlog_size)
+		fw->inst_evtlog_size = (pieces->inst_evtlog_size - 16)/12;
 	else
 		fw->inst_evtlog_size =
 			drv->cfg->base_params->max_event_log_size;
-	fw->inst_errlog_ptr = pieces.inst_errlog_ptr;
+	fw->inst_errlog_ptr = pieces->inst_errlog_ptr;
 
 	/*
 	 * figure out the offset of chain noise reset and gain commands
@@ -1162,10 +1275,55 @@
 				op->name, err);
 #endif
 	}
+
+	/*
+	 * We may have loaded the wrong FW file in 8000 HW family if it is an
+	 * A-step card, and if drv->trans->hw_rev wasn't properly read when
+	 * the FW file had been loaded. (This might happen in SDIO.) In such a
+	 * case - unload and reload the correct file.
+	 *
+	 * TODO:
+	 * Once there is only one supported step for 8000 family - delete this!
+	 */
+	if (drv->trans->cfg->device_family == IWL_DEVICE_FAMILY_8000 &&
+	    CSR_HW_REV_STEP(drv->trans->hw_rev) == SILICON_A_STEP &&
+	    drv->trans->hw_rev != hw_rev) {
+		char firmware_name[32];
+
+		/* Free previous FW resources */
+		if (drv->op_mode)
+			_iwl_op_mode_stop(drv);
+		iwl_dealloc_ucode(drv);
+
+		/* Build name of correct-step FW */
+		snprintf(firmware_name, sizeof(firmware_name),
+			 strrchr(drv->firmware_name, '-'));
+		snprintf(drv->firmware_name, sizeof(drv->firmware_name),
+			 "%s%s", drv->cfg->fw_name_pre, firmware_name);
+
+		/* Clear data before loading correct FW */
+		list_del(&drv->list);
+
+		/* Request correct FW file this time */
+		IWL_DEBUG_INFO(drv, "attempting to load A-step FW %s\n",
+			       drv->firmware_name);
+		err = request_firmware(&ucode_raw, drv->firmware_name,
+				       drv->trans->dev);
+		if (err) {
+			IWL_ERR(drv, "Failed swapping FW!\n");
+			goto out_unbind;
+		}
+
+		/* Redo callback function - this time with right FW */
+		iwl_req_fw_callback(ucode_raw, context);
+	}
+
+	kfree(pieces);
 	return;
 
  try_again:
 	/* try next, if any */
+	kfree(pieces);
 	release_firmware(ucode_raw);
 	if (iwl_request_firmware(drv, false))
 		goto out_unbind;
@@ -1176,6 +1334,7 @@
 	iwl_dealloc_ucode(drv);
 	release_firmware(ucode_raw);
  out_unbind:
+	kfree(pieces);
 	complete(&drv->request_firmware_complete);
 	device_release_driver(drv->trans->dev);
 }
diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c
index 74b796d..41ff85d 100644
--- a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c
+++ b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c
@@ -764,7 +764,7 @@
 	if (iwlwifi_mod_params.amsdu_size_8K)
 		ht_info->cap |= IEEE80211_HT_CAP_MAX_AMSDU;
 
-	ht_info->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
+	ht_info->ampdu_factor = cfg->max_ht_ampdu_exponent;
 	ht_info->ampdu_density = IEEE80211_HT_MPDU_DENSITY_4;
 
 	ht_info->mcs.rx_mask[0] = 0xFF;
diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h b/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h
index e30a41d..20a8a64 100644
--- a/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h
+++ b/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h
@@ -81,6 +81,7 @@
  * @IWL_FW_ERROR_DUMP_FW_MONITOR: firmware monitor
  * @IWL_FW_ERROR_DUMP_PRPH: range of periphery registers - there can be several
  *	sections like this in a single file.
+ * @IWL_FW_ERROR_DUMP_FH_REGS: range of FH registers
  */
 enum iwl_fw_error_dump_type {
 	IWL_FW_ERROR_DUMP_SRAM = 0,
@@ -90,6 +91,8 @@
 	IWL_FW_ERROR_DUMP_DEV_FW_INFO = 4,
 	IWL_FW_ERROR_DUMP_FW_MONITOR = 5,
 	IWL_FW_ERROR_DUMP_PRPH = 6,
+	IWL_FW_ERROR_DUMP_TXF = 7,
+	IWL_FW_ERROR_DUMP_FH_REGS = 8,
 
 	IWL_FW_ERROR_DUMP_MAX,
 };
diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/iwlwifi/iwl-fw-file.h
index 401f7be..f2a047f 100644
--- a/drivers/net/wireless/iwlwifi/iwl-fw-file.h
+++ b/drivers/net/wireless/iwlwifi/iwl-fw-file.h
@@ -131,6 +131,9 @@
 	IWL_UCODE_TLV_API_CHANGES_SET	= 29,
 	IWL_UCODE_TLV_ENABLED_CAPABILITIES	= 30,
 	IWL_UCODE_TLV_N_SCAN_CHANNELS		= 31,
+	IWL_UCODE_TLV_SEC_RT_USNIFFER	= 34,
+	IWL_UCODE_TLV_FW_DBG_DEST	= 38,
+	IWL_UCODE_TLV_FW_DBG_CONF	= 39,
 };
 
 struct iwl_ucode_tlv {
@@ -179,4 +182,309 @@
 	__le32 api_capa;
 } __packed;
 
+/**
+ * enum iwl_ucode_tlv_flag - ucode API flags
+ * @IWL_UCODE_TLV_FLAGS_PAN: This is PAN capable microcode; this previously
+ *	was a separate TLV but moved here to save space.
+ * @IWL_UCODE_TLV_FLAGS_NEWSCAN: new uCode scan behaviour on hidden SSID,
+ *	treats good CRC threshold as a boolean
+ * @IWL_UCODE_TLV_FLAGS_MFP: This uCode image supports MFP (802.11w).
+ * @IWL_UCODE_TLV_FLAGS_P2P: This uCode image supports P2P.
+ * @IWL_UCODE_TLV_FLAGS_DW_BC_TABLE: The SCD byte count table is in DWORDS
+ * @IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT: This uCode image supports uAPSD
+ * @IWL_UCODE_TLV_FLAGS_SHORT_BL: 16 entries of black list instead of 64 in scan
+ *	offload profile config command.
+ * @IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS: D3 image supports up to six
+ *	(rather than two) IPv6 addresses
+ * @IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID: not sending a probe with the SSID element
+ *	from the probe request template.
+ * @IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL: new NS offload (small version)
+ * @IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE: new NS offload (large version)
+ * @IWL_UCODE_TLV_FLAGS_P2P_PM: P2P client supports PM as a stand alone MAC
+ * @IWL_UCODE_TLV_FLAGS_P2P_BSS_PS_DCM: support power save on BSS station and
+ *	P2P client interfaces simultaneously if they are in different bindings.
+ * @IWL_UCODE_TLV_FLAGS_P2P_BSS_PS_SCM: support power save on BSS station and
+ *	P2P client interfaces simultaneously if they are in same bindings.
+ * @IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT: General support for uAPSD
+ * @IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD: P2P client supports uAPSD power save
+ * @IWL_UCODE_TLV_FLAGS_BCAST_FILTERING: uCode supports broadcast filtering.
+ * @IWL_UCODE_TLV_FLAGS_GO_UAPSD: AP/GO interfaces support uAPSD clients
+ * @IWL_UCODE_TLV_FLAGS_EBS_SUPPORT: this uCode image supports EBS.
+ */
+enum iwl_ucode_tlv_flag {
+	IWL_UCODE_TLV_FLAGS_PAN			= BIT(0),
+	IWL_UCODE_TLV_FLAGS_NEWSCAN		= BIT(1),
+	IWL_UCODE_TLV_FLAGS_MFP			= BIT(2),
+	IWL_UCODE_TLV_FLAGS_P2P			= BIT(3),
+	IWL_UCODE_TLV_FLAGS_DW_BC_TABLE		= BIT(4),
+	IWL_UCODE_TLV_FLAGS_SHORT_BL		= BIT(7),
+	IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS	= BIT(10),
+	IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID	= BIT(12),
+	IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL	= BIT(15),
+	IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE	= BIT(16),
+	IWL_UCODE_TLV_FLAGS_P2P_PM		= BIT(21),
+	IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_DCM	= BIT(22),
+	IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_SCM	= BIT(23),
+	IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT	= BIT(24),
+	IWL_UCODE_TLV_FLAGS_EBS_SUPPORT		= BIT(25),
+	IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD	= BIT(26),
+	IWL_UCODE_TLV_FLAGS_BCAST_FILTERING	= BIT(29),
+	IWL_UCODE_TLV_FLAGS_GO_UAPSD		= BIT(30),
+};
+
+/**
+ * enum iwl_ucode_tlv_api - ucode api
+ * @IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID: wowlan config includes tid field.
+ * @IWL_UCODE_TLV_CAPA_EXTENDED_BEACON: Support Extended beacon notification
+ * @IWL_UCODE_TLV_API_BT_COEX_SPLIT: new API for BT Coex
+ * @IWL_UCODE_TLV_API_CSA_FLOW: ucode can do unbind-bind flow for CSA.
+ * @IWL_UCODE_TLV_API_DISABLE_STA_TX: ucode supports tx_disable bit.
+ * @IWL_UCODE_TLV_API_LMAC_SCAN: This ucode uses LMAC unified scan API.
+ * @IWL_UCODE_TLV_API_SF_NO_DUMMY_NOTIF: ucode supports disabling dummy notif.
+ * @IWL_UCODE_TLV_API_FRAGMENTED_SCAN: This ucode supports active dwell time
+ *	longer than the passive one, which is essential for fragmented scan.
+ */
+enum iwl_ucode_tlv_api {
+	IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID	= BIT(0),
+	IWL_UCODE_TLV_CAPA_EXTENDED_BEACON	= BIT(1),
+	IWL_UCODE_TLV_API_BT_COEX_SPLIT         = BIT(3),
+	IWL_UCODE_TLV_API_CSA_FLOW		= BIT(4),
+	IWL_UCODE_TLV_API_DISABLE_STA_TX	= BIT(5),
+	IWL_UCODE_TLV_API_LMAC_SCAN		= BIT(6),
+	IWL_UCODE_TLV_API_SF_NO_DUMMY_NOTIF	= BIT(7),
+	IWL_UCODE_TLV_API_FRAGMENTED_SCAN	= BIT(8),
+};
+
+/**
+ * enum iwl_ucode_tlv_capa - ucode capabilities
+ * @IWL_UCODE_TLV_CAPA_D0I3_SUPPORT: supports D0i3
+ * @IWL_UCODE_TLV_CAPA_LAR_SUPPORT: supports Location Aware Regulatory
+ * @IWL_UCODE_TLV_CAPA_UMAC_SCAN: supports UMAC scan.
+ * @IWL_UCODE_TLV_CAPA_TDLS_SUPPORT: support basic TDLS functionality
+ * @IWL_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT: supports insertion of current
+ *	tx power value into TPC Report action frame and Link Measurement Report
+ *	action frame
+ * @IWL_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT: supports updating current
+ *	channel in DS parameter set element in probe requests.
+ * @IWL_UCODE_TLV_CAPA_WFA_TPC_REP_IE_SUPPORT: supports adding TPC Report IE in
+ *	probe requests.
+ * @IWL_UCODE_TLV_CAPA_QUIET_PERIOD_SUPPORT: supports Quiet Period requests
+ * @IWL_UCODE_TLV_CAPA_DQA_SUPPORT: supports dynamic queue allocation (DQA),
+ *	which also implies support for the scheduler configuration command
+ * @IWL_UCODE_TLV_CAPA_TDLS_CHANNEL_SWITCH: supports TDLS channel switching
+ * @IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT: supports Hot Spot Command
+ */
+enum iwl_ucode_tlv_capa {
+	IWL_UCODE_TLV_CAPA_D0I3_SUPPORT			= BIT(0),
+	IWL_UCODE_TLV_CAPA_LAR_SUPPORT			= BIT(1),
+	IWL_UCODE_TLV_CAPA_UMAC_SCAN			= BIT(2),
+	IWL_UCODE_TLV_CAPA_TDLS_SUPPORT			= BIT(6),
+	IWL_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT	= BIT(8),
+	IWL_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT	= BIT(9),
+	IWL_UCODE_TLV_CAPA_WFA_TPC_REP_IE_SUPPORT	= BIT(10),
+	IWL_UCODE_TLV_CAPA_QUIET_PERIOD_SUPPORT		= BIT(11),
+	IWL_UCODE_TLV_CAPA_DQA_SUPPORT			= BIT(12),
+	IWL_UCODE_TLV_CAPA_TDLS_CHANNEL_SWITCH		= BIT(13),
+	IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT		= BIT(18),
+};
+
+/* The default calibrate table size if not specified by firmware file */
+#define IWL_DEFAULT_STANDARD_PHY_CALIBRATE_TBL_SIZE	18
+#define IWL_MAX_STANDARD_PHY_CALIBRATE_TBL_SIZE		19
+#define IWL_MAX_PHY_CALIBRATE_TBL_SIZE			253
+
+/* The default max probe length if not specified by the firmware file */
+#define IWL_DEFAULT_MAX_PROBE_LENGTH	200
+
+/*
+ * For 16.0 uCode and above, there is no differentiation between sections,
+ * just an offset to the HW address.
+ */
+#define IWL_UCODE_SECTION_MAX 12
+#define IWL_API_ARRAY_SIZE	1
+#define IWL_CAPABILITIES_ARRAY_SIZE	1
+#define CPU1_CPU2_SEPARATOR_SECTION	0xFFFFCCCC
+
+/* uCode version contains 4 values: Major/Minor/API/Serial */
+#define IWL_UCODE_MAJOR(ver)	(((ver) & 0xFF000000) >> 24)
+#define IWL_UCODE_MINOR(ver)	(((ver) & 0x00FF0000) >> 16)
+#define IWL_UCODE_API(ver)	(((ver) & 0x0000FF00) >> 8)
+#define IWL_UCODE_SERIAL(ver)	((ver) & 0x000000FF)
+
+/*
+ * Calibration control struct.
+ * Sent as part of the phy configuration command.
+ * @flow_trigger: bitmap for which calibrations to perform according to
+ *		flow triggers.
+ * @event_trigger: bitmap for which calibrations to perform according to
+ *		event triggers.
+ */
+struct iwl_tlv_calib_ctrl {
+	__le32 flow_trigger;
+	__le32 event_trigger;
+} __packed;
+
+enum iwl_fw_phy_cfg {
+	FW_PHY_CFG_RADIO_TYPE_POS = 0,
+	FW_PHY_CFG_RADIO_TYPE = 0x3 << FW_PHY_CFG_RADIO_TYPE_POS,
+	FW_PHY_CFG_RADIO_STEP_POS = 2,
+	FW_PHY_CFG_RADIO_STEP = 0x3 << FW_PHY_CFG_RADIO_STEP_POS,
+	FW_PHY_CFG_RADIO_DASH_POS = 4,
+	FW_PHY_CFG_RADIO_DASH = 0x3 << FW_PHY_CFG_RADIO_DASH_POS,
+	FW_PHY_CFG_TX_CHAIN_POS = 16,
+	FW_PHY_CFG_TX_CHAIN = 0xf << FW_PHY_CFG_TX_CHAIN_POS,
+	FW_PHY_CFG_RX_CHAIN_POS = 20,
+	FW_PHY_CFG_RX_CHAIN = 0xf << FW_PHY_CFG_RX_CHAIN_POS,
+};
+
+#define IWL_UCODE_MAX_CS		1
+
+/**
+ * struct iwl_fw_cipher_scheme - a cipher scheme supported by FW.
+ * @cipher: a cipher suite selector
+ * @flags: cipher scheme flags (currently reserved for a future use)
+ * @hdr_len: a size of MPDU security header
+ * @pn_len: a size of PN
+ * @pn_off: an offset of pn from the beginning of the security header
+ * @key_idx_off: an offset of key index byte in the security header
+ * @key_idx_mask: a bit mask of key_idx bits
+ * @key_idx_shift: bit shift needed to get key_idx
+ * @mic_len: mic length in bytes
+ * @hw_cipher: a HW cipher index used in host commands
+ */
+struct iwl_fw_cipher_scheme {
+	__le32 cipher;
+	u8 flags;
+	u8 hdr_len;
+	u8 pn_len;
+	u8 pn_off;
+	u8 key_idx_off;
+	u8 key_idx_mask;
+	u8 key_idx_shift;
+	u8 mic_len;
+	u8 hw_cipher;
+} __packed;
+
+enum iwl_fw_dbg_reg_operator {
+	CSR_ASSIGN,
+	CSR_SETBIT,
+	CSR_CLEARBIT,
+
+	PRPH_ASSIGN,
+	PRPH_SETBIT,
+	PRPH_CLEARBIT,
+};
+
+/**
+ * struct iwl_fw_dbg_reg_op - an operation on a register
+ *
+ * @op: %enum iwl_fw_dbg_reg_operator
+ * @addr: offset of the register
+ * @val: value
+ */
+struct iwl_fw_dbg_reg_op {
+	u8 op;
+	u8 reserved[3];
+	__le32 addr;
+	__le32 val;
+} __packed;
+
+/**
+ * enum iwl_fw_dbg_monitor_mode - available monitor recording modes
+ *
+ * @SMEM_MODE: monitor stores the data in SMEM
+ * @EXTERNAL_MODE: monitor stores the data in allocated DRAM
+ * @MARBH_MODE: monitor stores the data in MARBH buffer
+ */
+enum iwl_fw_dbg_monitor_mode {
+	SMEM_MODE = 0,
+	EXTERNAL_MODE = 1,
+	MARBH_MODE = 2,
+};
+
+/**
+ * struct iwl_fw_dbg_dest_tlv - configures the destination of the debug data
+ *
+ * @version: version of the TLV - currently 0
+ * @monitor_mode: %enum iwl_fw_dbg_monitor_mode
+ * @base_reg: addr of the base addr register (PRPH)
+ * @end_reg:  addr of the end addr register (PRPH)
+ * @write_ptr_reg: the addr of the reg of the write pointer
+ * @wrap_count: the addr of the reg of the wrap_count
+ * @base_shift: shift right of the base addr reg
+ * @end_shift: shift right of the end addr reg
+ * @reg_ops: array of registers operations
+ *
+ * This parses IWL_UCODE_TLV_FW_DBG_DEST
+ */
+struct iwl_fw_dbg_dest_tlv {
+	u8 version;
+	u8 monitor_mode;
+	u8 reserved[2];
+	__le32 base_reg;
+	__le32 end_reg;
+	__le32 write_ptr_reg;
+	__le32 wrap_count;
+	u8 base_shift;
+	u8 end_shift;
+	struct iwl_fw_dbg_reg_op reg_ops[0];
+} __packed;
+
+struct iwl_fw_dbg_conf_hcmd {
+	u8 id;
+	u8 reserved;
+	__le16 len;
+	u8 data[0];
+} __packed;
+
+/**
+ * struct iwl_fw_dbg_trigger - a TLV that describes a debug configuration
+ *
+ * @enabled: is this trigger enabled
+ * @reserved:
+ * @len: length, in bytes, of the %trigger field
+ * @trigger: pointer to a trigger struct
+ */
+struct iwl_fw_dbg_trigger {
+	u8 enabled;
+	u8 reserved;
+	u8 len;
+	u8 trigger[0];
+} __packed;
+
+/**
+ * enum iwl_fw_dbg_conf - configurations available
+ *
+ * @FW_DBG_CUSTOM: take this configuration from alive
+ *	Note that the trigger is NO-OP for this configuration
+ */
+enum iwl_fw_dbg_conf {
+	FW_DBG_CUSTOM = 0,
+
+	/* must be last */
+	FW_DBG_MAX,
+	FW_DBG_INVALID = 0xff,
+};
+
+/**
+ * struct iwl_fw_dbg_conf_tlv - a TLV that describes a debug configuration
+ *
+ * @id: %enum iwl_fw_dbg_conf
+ * @usniffer: should the uSniffer image be used
+ * @num_of_hcmds: how many HCMDs to send are present here
+ * @hcmd: a variable length host command to be sent to apply the configuration.
+ *	If there is more than one HCMD to send, they will appear one after the
+ *	other and be sent in the order that they appear in.
+ * This parses IWL_UCODE_TLV_FW_DBG_CONF
+ */
+struct iwl_fw_dbg_conf_tlv {
+	u8 id;
+	u8 usniffer;
+	u8 reserved;
+	u8 num_of_hcmds;
+	struct iwl_fw_dbg_conf_hcmd hcmd;
+
+	/* struct iwl_fw_dbg_trigger sits after all variable length hcmds */
+} __packed;
+
 #endif  /* __iwl_fw_file_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-fw.h b/drivers/net/wireless/iwlwifi/iwl-fw.h
index 6f7ae5f..e6dc3b8 100644
--- a/drivers/net/wireless/iwlwifi/iwl-fw.h
+++ b/drivers/net/wireless/iwlwifi/iwl-fw.h
@@ -70,110 +70,6 @@
 #include "iwl-fw-file.h"
 
 /**
- * enum iwl_ucode_tlv_flag - ucode API flags
- * @IWL_UCODE_TLV_FLAGS_PAN: This is PAN capable microcode; this previously
- *	was a separate TLV but moved here to save space.
- * @IWL_UCODE_TLV_FLAGS_NEWSCAN: new uCode scan behaviour on hidden SSID,
- *	treats good CRC threshold as a boolean
- * @IWL_UCODE_TLV_FLAGS_MFP: This uCode image supports MFP (802.11w).
- * @IWL_UCODE_TLV_FLAGS_P2P: This uCode image supports P2P.
- * @IWL_UCODE_TLV_FLAGS_DW_BC_TABLE: The SCD byte count table is in DWORDS
- * @IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT: This uCode image supports uAPSD
- * @IWL_UCODE_TLV_FLAGS_SHORT_BL: 16 entries of black list instead of 64 in scan
- *	offload profile config command.
- * @IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS: D3 image supports up to six
- *	(rather than two) IPv6 addresses
- * @IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID: not sending a probe with the SSID element
- *	from the probe request template.
- * @IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL: new NS offload (small version)
- * @IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE: new NS offload (large version)
- * @IWL_UCODE_TLV_FLAGS_P2P_PM: P2P client supports PM as a stand alone MAC
- * @IWL_UCODE_TLV_FLAGS_P2P_BSS_PS_DCM: support power save on BSS station and
- *	P2P client interfaces simultaneously if they are in different bindings.
- * @IWL_UCODE_TLV_FLAGS_P2P_BSS_PS_SCM: support power save on BSS station and
- *	P2P client interfaces simultaneously if they are in same bindings.
- * @IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT: General support for uAPSD
- * @IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD: P2P client supports uAPSD power save
- * @IWL_UCODE_TLV_FLAGS_BCAST_FILTERING: uCode supports broadcast filtering.
- * @IWL_UCODE_TLV_FLAGS_GO_UAPSD: AP/GO interfaces support uAPSD clients
- * @IWL_UCODE_TLV_FLAGS_EBS_SUPPORT: this uCode image supports EBS.
- */
-enum iwl_ucode_tlv_flag {
-	IWL_UCODE_TLV_FLAGS_PAN			= BIT(0),
-	IWL_UCODE_TLV_FLAGS_NEWSCAN		= BIT(1),
-	IWL_UCODE_TLV_FLAGS_MFP			= BIT(2),
-	IWL_UCODE_TLV_FLAGS_P2P			= BIT(3),
-	IWL_UCODE_TLV_FLAGS_DW_BC_TABLE		= BIT(4),
-	IWL_UCODE_TLV_FLAGS_SHORT_BL		= BIT(7),
-	IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS	= BIT(10),
-	IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID	= BIT(12),
-	IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL	= BIT(15),
-	IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE	= BIT(16),
-	IWL_UCODE_TLV_FLAGS_P2P_PM		= BIT(21),
-	IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_DCM	= BIT(22),
-	IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_SCM	= BIT(23),
-	IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT	= BIT(24),
-	IWL_UCODE_TLV_FLAGS_EBS_SUPPORT		= BIT(25),
-	IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD	= BIT(26),
-	IWL_UCODE_TLV_FLAGS_BCAST_FILTERING	= BIT(29),
-	IWL_UCODE_TLV_FLAGS_GO_UAPSD		= BIT(30),
-};
-
-/**
- * enum iwl_ucode_tlv_api - ucode api
- * @IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID: wowlan config includes tid field.
- * @IWL_UCODE_TLV_CAPA_EXTENDED_BEACON: Support Extended beacon notification
- * @IWL_UCODE_TLV_API_BT_COEX_SPLIT: new API for BT Coex
- * @IWL_UCODE_TLV_API_CSA_FLOW: ucode can do unbind-bind flow for CSA.
- * @IWL_UCODE_TLV_API_DISABLE_STA_TX: ucode supports tx_disable bit.
- * @IWL_UCODE_TLV_API_LMAC_SCAN: This ucode uses LMAC unified scan API.
- * @IWL_UCODE_TLV_API_SF_NO_DUMMY_NOTIF: ucode supports disabling dummy notif.
- * @IWL_UCODE_TLV_API_FRAGMENTED_SCAN: This ucode supports active dwell time
- *	longer than the passive one, which is essential for fragmented scan.
- */
-enum iwl_ucode_tlv_api {
-	IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID	= BIT(0),
-	IWL_UCODE_TLV_CAPA_EXTENDED_BEACON	= BIT(1),
-	IWL_UCODE_TLV_API_BT_COEX_SPLIT         = BIT(3),
-	IWL_UCODE_TLV_API_CSA_FLOW		= BIT(4),
-	IWL_UCODE_TLV_API_DISABLE_STA_TX	= BIT(5),
-	IWL_UCODE_TLV_API_LMAC_SCAN		= BIT(6),
-	IWL_UCODE_TLV_API_SF_NO_DUMMY_NOTIF	= BIT(7),
-	IWL_UCODE_TLV_API_FRAGMENTED_SCAN	= BIT(8),
-};
-
-/**
- * enum iwl_ucode_tlv_capa - ucode capabilities
- * @IWL_UCODE_TLV_CAPA_D0I3_SUPPORT: supports D0i3
- * @IWL_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT: supports insertion of current
- *	tx power value into TPC Report action frame and Link Measurement Report
- *	action frame
- * @IWL_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT: supports adding DS params
- *	element in probe requests.
- * @IWL_UCODE_TLV_CAPA_WFA_TPC_REP_IE_SUPPORT: supports adding TPC Report IE in
- *	probe requests.
- * @IWL_UCODE_TLV_CAPA_QUIET_PERIOD_SUPPORT: supports Quiet Period requests
- * @IWL_UCODE_TLV_CAPA_DQA_SUPPORT: supports dynamic queue allocation (DQA),
- *	which also implies support for the scheduler configuration command
- */
-enum iwl_ucode_tlv_capa {
-	IWL_UCODE_TLV_CAPA_D0I3_SUPPORT			= BIT(0),
-	IWL_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT	= BIT(8),
-	IWL_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT	= BIT(9),
-	IWL_UCODE_TLV_CAPA_WFA_TPC_REP_IE_SUPPORT	= BIT(10),
-	IWL_UCODE_TLV_CAPA_QUIET_PERIOD_SUPPORT		= BIT(11),
-	IWL_UCODE_TLV_CAPA_DQA_SUPPORT			= BIT(12),
-};
-
-/* The default calibrate table size if not specified by firmware file */
-#define IWL_DEFAULT_STANDARD_PHY_CALIBRATE_TBL_SIZE	18
-#define IWL_MAX_STANDARD_PHY_CALIBRATE_TBL_SIZE		19
-#define IWL_MAX_PHY_CALIBRATE_TBL_SIZE			253
-
-/* The default max probe length if not specified by the firmware file */
-#define IWL_DEFAULT_MAX_PROBE_LENGTH	200
-
-/**
  * enum iwl_ucode_type
  *
  * The type of ucode.
@@ -181,11 +77,13 @@
  * @IWL_UCODE_REGULAR: Normal runtime ucode
  * @IWL_UCODE_INIT: Initial ucode
  * @IWL_UCODE_WOWLAN: Wake on Wireless enabled ucode
+ * @IWL_UCODE_REGULAR_USNIFFER: Normal runtime ucode when using usniffer image
  */
 enum iwl_ucode_type {
 	IWL_UCODE_REGULAR,
 	IWL_UCODE_INIT,
 	IWL_UCODE_WOWLAN,
+	IWL_UCODE_REGULAR_USNIFFER,
 	IWL_UCODE_TYPE_MAX,
 };
 
@@ -200,14 +98,6 @@
 	IWL_UCODE_SECTION_DATA,
 	IWL_UCODE_SECTION_INST,
 };
-/*
- * For 16.0 uCode and above, there is no differentiation between sections,
- * just an offset to the HW address.
- */
-#define IWL_UCODE_SECTION_MAX 12
-#define IWL_API_ARRAY_SIZE	1
-#define IWL_CAPABILITIES_ARRAY_SIZE	1
-#define CPU1_CPU2_SEPARATOR_SECTION	0xFFFFCCCC
 
 struct iwl_ucode_capabilities {
 	u32 max_probe_length;
@@ -235,66 +125,6 @@
 	u32 size;
 };
 
-/* uCode version contains 4 values: Major/Minor/API/Serial */
-#define IWL_UCODE_MAJOR(ver)	(((ver) & 0xFF000000) >> 24)
-#define IWL_UCODE_MINOR(ver)	(((ver) & 0x00FF0000) >> 16)
-#define IWL_UCODE_API(ver)	(((ver) & 0x0000FF00) >> 8)
-#define IWL_UCODE_SERIAL(ver)	((ver) & 0x000000FF)
-
-/*
- * Calibration control struct.
- * Sent as part of the phy configuration command.
- * @flow_trigger: bitmap for which calibrations to perform according to
- *		flow triggers.
- * @event_trigger: bitmap for which calibrations to perform according to
- *		event triggers.
- */
-struct iwl_tlv_calib_ctrl {
-	__le32 flow_trigger;
-	__le32 event_trigger;
-} __packed;
-
-enum iwl_fw_phy_cfg {
-	FW_PHY_CFG_RADIO_TYPE_POS = 0,
-	FW_PHY_CFG_RADIO_TYPE = 0x3 << FW_PHY_CFG_RADIO_TYPE_POS,
-	FW_PHY_CFG_RADIO_STEP_POS = 2,
-	FW_PHY_CFG_RADIO_STEP = 0x3 << FW_PHY_CFG_RADIO_STEP_POS,
-	FW_PHY_CFG_RADIO_DASH_POS = 4,
-	FW_PHY_CFG_RADIO_DASH = 0x3 << FW_PHY_CFG_RADIO_DASH_POS,
-	FW_PHY_CFG_TX_CHAIN_POS = 16,
-	FW_PHY_CFG_TX_CHAIN = 0xf << FW_PHY_CFG_TX_CHAIN_POS,
-	FW_PHY_CFG_RX_CHAIN_POS = 20,
-	FW_PHY_CFG_RX_CHAIN = 0xf << FW_PHY_CFG_RX_CHAIN_POS,
-};
-
-#define IWL_UCODE_MAX_CS		1
-
-/**
- * struct iwl_fw_cipher_scheme - a cipher scheme supported by FW.
- * @cipher: a cipher suite selector
- * @flags: cipher scheme flags (currently reserved for a future use)
- * @hdr_len: a size of MPDU security header
- * @pn_len: a size of PN
- * @pn_off: an offset of pn from the beginning of the security header
- * @key_idx_off: an offset of key index byte in the security header
- * @key_idx_mask: a bit mask of key_idx bits
- * @key_idx_shift: bit shift needed to get key_idx
- * @mic_len: mic length in bytes
- * @hw_cipher: a HW cipher index used in host commands
- */
-struct iwl_fw_cipher_scheme {
-	__le32 cipher;
-	u8 flags;
-	u8 hdr_len;
-	u8 pn_len;
-	u8 pn_off;
-	u8 key_idx_off;
-	u8 key_idx_mask;
-	u8 key_idx_shift;
-	u8 mic_len;
-	u8 hw_cipher;
-} __packed;
-
 /**
  * struct iwl_fw_cscheme_list - a cipher scheme list
  * @size: a number of entries
@@ -321,6 +151,11 @@
  * @inst_errlog_ptr: error log offfset for runtime ucode.
  * @mvm_fw: indicates this is MVM firmware
  * @cipher_scheme: optional external cipher scheme.
+ * @human_readable: human readable version
+ * @dbg_dest_tlv: points to the destination TLV for debug
+ * @dbg_conf_tlv: array of pointers to configuration TLVs for debug
+ * @dbg_conf_tlv_len: lengths of the @dbg_conf_tlv entries
+ * @dbg_dest_reg_num: num of reg_ops in %dbg_dest_tlv
  */
 struct iwl_fw {
 	u32 ucode_ver;
@@ -345,6 +180,68 @@
 
 	struct ieee80211_cipher_scheme cs[IWL_UCODE_MAX_CS];
 	u8 human_readable[FW_VER_HUMAN_READABLE_SZ];
+
+	struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv;
+	struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_MAX];
+	size_t dbg_conf_tlv_len[FW_DBG_MAX];
+
+	u8 dbg_dest_reg_num;
 };
 
+static inline const char *get_fw_dbg_mode_string(int mode)
+{
+	switch (mode) {
+	case SMEM_MODE:
+		return "SMEM";
+	case EXTERNAL_MODE:
+		return "EXTERNAL_DRAM";
+	case MARBH_MODE:
+		return "MARBH";
+	default:
+		return "UNKNOWN";
+	}
+}
+
+static inline const struct iwl_fw_dbg_trigger *
+iwl_fw_dbg_conf_get_trigger(const struct iwl_fw *fw, u8 id)
+{
+	const struct iwl_fw_dbg_conf_tlv *conf_tlv = fw->dbg_conf_tlv[id];
+	u8 *ptr;
+	int i;
+
+	if (!conf_tlv)
+		return NULL;
+
+	ptr = (void *)&conf_tlv->hcmd;
+	for (i = 0; i < conf_tlv->num_of_hcmds; i++) {
+		ptr += sizeof(conf_tlv->hcmd);
+		ptr += le16_to_cpu(conf_tlv->hcmd.len);
+	}
+
+	return (const struct iwl_fw_dbg_trigger *)ptr;
+}
+
+static inline bool
+iwl_fw_dbg_conf_enabled(const struct iwl_fw *fw, u8 id)
+{
+	const struct iwl_fw_dbg_trigger *trigger =
+		iwl_fw_dbg_conf_get_trigger(fw, id);
+
+	if (!trigger)
+		return false;
+
+	return trigger->enabled;
+}
+
+static inline bool
+iwl_fw_dbg_conf_usniffer(const struct iwl_fw *fw, u8 id)
+{
+	const struct iwl_fw_dbg_conf_tlv *conf_tlv = fw->dbg_conf_tlv[id];
+
+	if (!conf_tlv)
+		return false;
+
+	return conf_tlv->usniffer;
+}
+
 #endif  /* __iwl_fw_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c
index c302e74..06e02fc 100644
--- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c
+++ b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c
@@ -325,6 +325,8 @@
 {
 	int num_rx_ants = num_of_ant(rx_chains);
 	int num_tx_ants = num_of_ant(tx_chains);
+	unsigned int max_ampdu_exponent = (cfg->max_vht_ampdu_exponent ?:
+					   IEEE80211_VHT_MAX_AMPDU_1024K);
 
 	vht_cap->vht_supported = true;
 
@@ -332,7 +334,8 @@
 		       IEEE80211_VHT_CAP_RXSTBC_1 |
 		       IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
 		       3 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT |
-		       7 << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT;
+		       max_ampdu_exponent <<
+		       IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT;
 
 	if (cfg->ht_params->ldpc)
 		vht_cap->cap |= IEEE80211_VHT_CAP_RXLDPC;
diff --git a/drivers/net/wireless/iwlwifi/iwl-op-mode.h b/drivers/net/wireless/iwlwifi/iwl-op-mode.h
index b6d666e..17de6d4 100644
--- a/drivers/net/wireless/iwlwifi/iwl-op-mode.h
+++ b/drivers/net/wireless/iwlwifi/iwl-op-mode.h
@@ -138,7 +138,8 @@
  * @nic_config: configure NIC, called before firmware is started.
  *	May sleep
  * @wimax_active: invoked when WiMax becomes active. May sleep
- * @enter_d0i3: configure the fw to enter d0i3. May sleep.
+ * @enter_d0i3: configure the fw to enter d0i3. return 1 to indicate d0i3
+ *	entrance is aborted (e.g. due to held reference). May sleep.
  * @exit_d0i3: configure the fw to exit d0i3. May sleep.
  */
 struct iwl_op_mode_ops {
diff --git a/drivers/net/wireless/iwlwifi/iwl-prph.h b/drivers/net/wireless/iwlwifi/iwl-prph.h
index 1560f45..2df51ea 100644
--- a/drivers/net/wireless/iwlwifi/iwl-prph.h
+++ b/drivers/net/wireless/iwlwifi/iwl-prph.h
@@ -322,6 +322,7 @@
 	LMPM_SECURE_BOOT_CONFIG_INSPECTOR_NOT_REQ	= 0x00000002,
 };
 
+#define LMPM_SECURE_BOOT_CPU1_STATUS_ADDR_B0	(0xA01E30)
 #define LMPM_SECURE_BOOT_CPU1_STATUS_ADDR	(0x1E30)
 #define LMPM_SECURE_BOOT_CPU2_STATUS_ADDR	(0x1E34)
 enum secure_boot_status_reg {
@@ -333,6 +334,7 @@
 	LMPM_SECURE_BOOT_STATUS_SUCCESS			= 0x00000003,
 };
 
+#define FH_UCODE_LOAD_STATUS		(0x1AF0)
 #define CSR_UCODE_LOAD_STATUS_ADDR	(0x1E70)
 enum secure_load_status_reg {
 	LMPM_CPU_UCODE_LOADING_STARTED			= 0x00000001,
@@ -352,7 +354,7 @@
 #define LMPM_SECURE_CPU1_HDR_MEM_SPACE		(0x420000)
 #define LMPM_SECURE_CPU2_HDR_MEM_SPACE		(0x420400)
 
-#define LMPM_SECURE_TIME_OUT	(100)
+#define LMPM_SECURE_TIME_OUT	(100) /* 10 micro */
 
 /* Rx FIFO */
 #define RXF_SIZE_ADDR			(0xa00c88)
@@ -368,4 +370,10 @@
 #define MON_BUFF_WRPTR			(0xa03c44)
 #define MON_BUFF_CYCLE_CNT		(0xa03c48)
 
+/* FW chicken bits */
+#define LMPM_CHICK			0xA01FF8
+enum {
+	LMPM_CHICK_EXTENDED_ADDR_SPACE = BIT(0),
+};
+
 #endif				/* __iwl_prph_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h
index 0768f83e..028408a 100644
--- a/drivers/net/wireless/iwlwifi/iwl-trans.h
+++ b/drivers/net/wireless/iwlwifi/iwl-trans.h
@@ -534,6 +534,8 @@
 			      u32 value);
 	void (*ref)(struct iwl_trans *trans);
 	void (*unref)(struct iwl_trans *trans);
+	void (*suspend)(struct iwl_trans *trans);
+	void (*resume)(struct iwl_trans *trans);
 
 	struct iwl_trans_dump_data *(*dump_data)(struct iwl_trans *trans);
 };
@@ -572,6 +574,9 @@
  * @rx_mpdu_cmd_hdr_size: used for tracing, amount of data before the
  *	start of the 802.11 header in the @rx_mpdu_cmd
  * @dflt_pwr_limit: default power limit fetched from the platform (ACPI)
+ * @dbg_dest_tlv: points to the destination TLV for debug
+ * @dbg_conf_tlv: array of pointers to configuration TLVs for debug
+ * @dbg_dest_reg_num: num of reg_ops in %dbg_dest_tlv
  */
 struct iwl_trans {
 	const struct iwl_trans_ops *ops;
@@ -603,6 +608,10 @@
 
 	u64 dflt_pwr_limit;
 
+	const struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv;
+	const struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_MAX];
+	u8 dbg_dest_reg_num;
+
 	/* pointer to trans specific struct */
 	/*Ensure that this pointer will always be aligned to sizeof pointer */
 	char trans_specific[0] __aligned(sizeof(void *));
@@ -702,6 +711,18 @@
 		trans->ops->unref(trans);
 }
 
+static inline void iwl_trans_suspend(struct iwl_trans *trans)
+{
+	if (trans->ops->suspend)
+		trans->ops->suspend(trans);
+}
+
+static inline void iwl_trans_resume(struct iwl_trans *trans)
+{
+	if (trans->ops->resume)
+		trans->ops->resume(trans);
+}
+
 static inline struct iwl_trans_dump_data *
 iwl_trans_dump_data(struct iwl_trans *trans)
 {
diff --git a/drivers/net/wireless/iwlwifi/mvm/coex.c b/drivers/net/wireless/iwlwifi/mvm/coex.c
index 508c813..a3bfda4 100644
--- a/drivers/net/wireless/iwlwifi/mvm/coex.c
+++ b/drivers/net/wireless/iwlwifi/mvm/coex.c
@@ -1137,6 +1137,22 @@
 	return lut_type != BT_COEX_LOOSE_LUT;
 }
 
+bool iwl_mvm_bt_coex_is_ant_avail(struct iwl_mvm *mvm, u8 ant)
+{
+	/* there is no other antenna, shared antenna is always available */
+	if (mvm->cfg->bt_shared_single_ant)
+		return true;
+
+	if (ant & mvm->cfg->non_shared_ant)
+		return true;
+
+	if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT))
+		return iwl_mvm_bt_coex_is_shared_ant_avail_old(mvm);
+
+	return le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) <
+		BT_HIGH_TRAFFIC;
+}
+
 bool iwl_mvm_bt_coex_is_shared_ant_avail(struct iwl_mvm *mvm)
 {
 	/* there is no other antenna, shared antenna is always available */
diff --git a/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c b/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c
index b571e1b..b3210cf 100644
--- a/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c
+++ b/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c
@@ -612,7 +612,9 @@
 					    BT_VALID_ANT_ISOLATION_THRS |
 					    BT_VALID_TXTX_DELTA_FREQ_THRS |
 					    BT_VALID_TXRX_MAX_FREQ_0 |
-					    BT_VALID_SYNC_TO_SCO);
+					    BT_VALID_SYNC_TO_SCO |
+					    BT_VALID_TTC |
+					    BT_VALID_RRC);
 
 	if (IWL_MVM_BT_COEX_SYNC2SCO)
 		bt_cmd->flags |= cpu_to_le32(BT_COEX_SYNC2SCO);
@@ -628,6 +630,12 @@
 		bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_MULTI_PRIO_LUT);
 	}
 
+	if (IWL_MVM_BT_COEX_TTC)
+		bt_cmd->flags |= cpu_to_le32(BT_COEX_TTC);
+
+	if (IWL_MVM_BT_COEX_RRC)
+		bt_cmd->flags |= cpu_to_le32(BT_COEX_RRC);
+
 	if (mvm->cfg->bt_shared_single_ant)
 		memcpy(&bt_cmd->decision_lut, iwl_single_shared_ant,
 		       sizeof(iwl_single_shared_ant));
@@ -824,6 +832,9 @@
 	if (!vif->bss_conf.assoc)
 		smps_mode = IEEE80211_SMPS_AUTOMATIC;
 
+	if (data->notif->rrc_enabled & BIT(mvmvif->phy_ctxt->id))
+		smps_mode = IEEE80211_SMPS_AUTOMATIC;
+
 	IWL_DEBUG_COEX(data->mvm,
 		       "mac %d: bt_status %d bt_activity_grading %d smps_req %d\n",
 		       mvmvif->id, data->notif->bt_status, bt_activity_grading,
@@ -1156,6 +1167,12 @@
 	return lut_type != BT_COEX_LOOSE_LUT;
 }
 
+bool iwl_mvm_bt_coex_is_ant_avail_old(struct iwl_mvm *mvm, u8 ant)
+{
+	u32 ag = le32_to_cpu(mvm->last_bt_notif_old.bt_activity_grading);
+	return ag < BT_HIGH_TRAFFIC;
+}
+
 bool iwl_mvm_bt_coex_is_shared_ant_avail_old(struct iwl_mvm *mvm)
 {
 	u32 ag = le32_to_cpu(mvm->last_bt_notif_old.bt_activity_grading);
diff --git a/drivers/net/wireless/iwlwifi/mvm/constants.h b/drivers/net/wireless/iwlwifi/mvm/constants.h
index 5c1ea80..3bd9347 100644
--- a/drivers/net/wireless/iwlwifi/mvm/constants.h
+++ b/drivers/net/wireless/iwlwifi/mvm/constants.h
@@ -92,8 +92,10 @@
 #define IWL_MVM_BT_COEX_SYNC2SCO		1
 #define IWL_MVM_BT_COEX_CORUNNING		0
 #define IWL_MVM_BT_COEX_MPLUT			1
-#define IWL_MVM_BT_COEX_MPLUT_REG0		0x2e402280
-#define IWL_MVM_BT_COEX_MPLUT_REG1		0x7711a751
+#define IWL_MVM_BT_COEX_RRC			1
+#define IWL_MVM_BT_COEX_TTC			1
+#define IWL_MVM_BT_COEX_MPLUT_REG0		0x28412201
+#define IWL_MVM_BT_COEX_MPLUT_REG1		0x11118451
 #define IWL_MVM_BT_COEX_ANTENNA_COUPLING_THRS	30
 #define IWL_MVM_FW_MCAST_FILTER_PASS_ALL	0
 #define IWL_MVM_FW_BCAST_FILTER_PASS_ALL	0
diff --git a/drivers/net/wireless/iwlwifi/mvm/d3.c b/drivers/net/wireless/iwlwifi/mvm/d3.c
index 3bbb511..744de26 100644
--- a/drivers/net/wireless/iwlwifi/mvm/d3.c
+++ b/drivers/net/wireless/iwlwifi/mvm/d3.c
@@ -786,32 +786,18 @@
 }
 
 static int
-iwl_mvm_send_wowlan_config_cmd(struct iwl_mvm *mvm,
-			       const struct iwl_wowlan_config_cmd_v3 *cmd)
-{
-	/* start only with the v2 part of the command */
-	u16 cmd_len = sizeof(cmd->common);
-
-	if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID)
-		cmd_len = sizeof(*cmd);
-
-	return iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, 0,
-				    cmd_len, cmd);
-}
-
-static int
 iwl_mvm_get_wowlan_config(struct iwl_mvm *mvm,
 			  struct cfg80211_wowlan *wowlan,
-			  struct iwl_wowlan_config_cmd_v3 *wowlan_config_cmd,
+			  struct iwl_wowlan_config_cmd *wowlan_config_cmd,
 			  struct ieee80211_vif *vif, struct iwl_mvm_vif *mvmvif,
 			  struct ieee80211_sta *ap_sta)
 {
 	int ret;
 	struct iwl_mvm_sta *mvm_ap_sta = (struct iwl_mvm_sta *)ap_sta->drv_priv;
 
-	/* TODO: wowlan_config_cmd->common.wowlan_ba_teardown_tids */
+	/* TODO: wowlan_config_cmd->wowlan_ba_teardown_tids */
 
-	wowlan_config_cmd->common.is_11n_connection =
+	wowlan_config_cmd->is_11n_connection =
 					ap_sta->ht_cap.ht_supported;
 
 	/* Query the last used seqno and set it */
@@ -819,32 +805,32 @@
 	if (ret < 0)
 		return ret;
 
-	wowlan_config_cmd->common.non_qos_seq = cpu_to_le16(ret);
+	wowlan_config_cmd->non_qos_seq = cpu_to_le16(ret);
 
-	iwl_mvm_set_wowlan_qos_seq(mvm_ap_sta, &wowlan_config_cmd->common);
+	iwl_mvm_set_wowlan_qos_seq(mvm_ap_sta, wowlan_config_cmd);
 
 	if (wowlan->disconnect)
-		wowlan_config_cmd->common.wakeup_filter |=
+		wowlan_config_cmd->wakeup_filter |=
 			cpu_to_le32(IWL_WOWLAN_WAKEUP_BEACON_MISS |
 				    IWL_WOWLAN_WAKEUP_LINK_CHANGE);
 	if (wowlan->magic_pkt)
-		wowlan_config_cmd->common.wakeup_filter |=
+		wowlan_config_cmd->wakeup_filter |=
 			cpu_to_le32(IWL_WOWLAN_WAKEUP_MAGIC_PACKET);
 	if (wowlan->gtk_rekey_failure)
-		wowlan_config_cmd->common.wakeup_filter |=
+		wowlan_config_cmd->wakeup_filter |=
 			cpu_to_le32(IWL_WOWLAN_WAKEUP_GTK_REKEY_FAIL);
 	if (wowlan->eap_identity_req)
-		wowlan_config_cmd->common.wakeup_filter |=
+		wowlan_config_cmd->wakeup_filter |=
 			cpu_to_le32(IWL_WOWLAN_WAKEUP_EAP_IDENT_REQ);
 	if (wowlan->four_way_handshake)
-		wowlan_config_cmd->common.wakeup_filter |=
+		wowlan_config_cmd->wakeup_filter |=
 			cpu_to_le32(IWL_WOWLAN_WAKEUP_4WAY_HANDSHAKE);
 	if (wowlan->n_patterns)
-		wowlan_config_cmd->common.wakeup_filter |=
+		wowlan_config_cmd->wakeup_filter |=
 			cpu_to_le32(IWL_WOWLAN_WAKEUP_PATTERN_MATCH);
 
 	if (wowlan->rfkill_release)
-		wowlan_config_cmd->common.wakeup_filter |=
+		wowlan_config_cmd->wakeup_filter |=
 			cpu_to_le32(IWL_WOWLAN_WAKEUP_RF_KILL_DEASSERT);
 
 	if (wowlan->tcp) {
@@ -852,7 +838,7 @@
 		 * Set the "link change" (really "link lost") flag as well
 		 * since that implies losing the TCP connection.
 		 */
-		wowlan_config_cmd->common.wakeup_filter |=
+		wowlan_config_cmd->wakeup_filter |=
 			cpu_to_le32(IWL_WOWLAN_WAKEUP_REMOTE_LINK_LOSS |
 				    IWL_WOWLAN_WAKEUP_REMOTE_SIGNATURE_TABLE |
 				    IWL_WOWLAN_WAKEUP_REMOTE_WAKEUP_PACKET |
@@ -865,7 +851,7 @@
 static int
 iwl_mvm_wowlan_config(struct iwl_mvm *mvm,
 		      struct cfg80211_wowlan *wowlan,
-		      struct iwl_wowlan_config_cmd_v3 *wowlan_config_cmd,
+		      struct iwl_wowlan_config_cmd *wowlan_config_cmd,
 		      struct ieee80211_vif *vif, struct iwl_mvm_vif *mvmvif,
 		      struct ieee80211_sta *ap_sta)
 {
@@ -878,6 +864,10 @@
 	};
 	int ret;
 
+	ret = iwl_mvm_switch_to_d3(mvm);
+	if (ret)
+		return ret;
+
 	ret = iwl_mvm_d3_reprogram(mvm, vif, ap_sta);
 	if (ret)
 		return ret;
@@ -943,7 +933,9 @@
 		}
 	}
 
-	ret = iwl_mvm_send_wowlan_config_cmd(mvm, wowlan_config_cmd);
+	ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, 0,
+				   sizeof(*wowlan_config_cmd),
+				   wowlan_config_cmd);
 	if (ret)
 		goto out;
 
@@ -962,6 +954,68 @@
 	return ret;
 }
 
+static int
+iwl_mvm_netdetect_config(struct iwl_mvm *mvm,
+			 struct cfg80211_wowlan *wowlan,
+			 struct cfg80211_sched_scan_request *nd_config,
+			 struct ieee80211_vif *vif)
+{
+	struct iwl_wowlan_config_cmd wowlan_config_cmd = {};
+	int ret;
+
+	ret = iwl_mvm_switch_to_d3(mvm);
+	if (ret)
+		return ret;
+
+	/* rfkill release can be either for wowlan or netdetect */
+	if (wowlan->rfkill_release)
+		wowlan_config_cmd.wakeup_filter |=
+			cpu_to_le32(IWL_WOWLAN_WAKEUP_RF_KILL_DEASSERT);
+
+	ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, 0,
+				   sizeof(wowlan_config_cmd),
+				   &wowlan_config_cmd);
+	if (ret)
+		return ret;
+
+	ret = iwl_mvm_scan_offload_start(mvm, vif, nd_config, &mvm->nd_ies);
+	if (ret)
+		return ret;
+
+	if (WARN_ON(mvm->nd_match_sets || mvm->nd_channels))
+		return -EBUSY;
+
+	/* save the sched scan matchsets... */
+	if (nd_config->n_match_sets) {
+		mvm->nd_match_sets = kmemdup(nd_config->match_sets,
+					     sizeof(*nd_config->match_sets) *
+					     nd_config->n_match_sets,
+					     GFP_KERNEL);
+		if (mvm->nd_match_sets)
+			mvm->n_nd_match_sets = nd_config->n_match_sets;
+	}
+
+	/* ...and the sched scan channels for later reporting */
+	mvm->nd_channels = kmemdup(nd_config->channels,
+				   sizeof(*nd_config->channels) *
+				   nd_config->n_channels,
+				   GFP_KERNEL);
+	if (mvm->nd_channels)
+		mvm->n_nd_channels = nd_config->n_channels;
+
+	return 0;
+}
+
+static void iwl_mvm_free_nd(struct iwl_mvm *mvm)
+{
+	kfree(mvm->nd_match_sets);
+	mvm->nd_match_sets = NULL;
+	mvm->n_nd_match_sets = 0;
+	kfree(mvm->nd_channels);
+	mvm->nd_channels = NULL;
+	mvm->n_nd_channels = 0;
+}
+
 static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
 			     struct cfg80211_wowlan *wowlan,
 			     bool test)
@@ -970,7 +1024,6 @@
 	struct ieee80211_vif *vif = NULL;
 	struct iwl_mvm_vif *mvmvif = NULL;
 	struct ieee80211_sta *ap_sta = NULL;
-	struct iwl_wowlan_config_cmd_v3 wowlan_config_cmd = {};
 	struct iwl_d3_manager_config d3_cfg_cmd_data = {
 		/*
 		 * Program the minimum sleep time to 10 seconds, as many
@@ -1007,8 +1060,22 @@
 
 	mvmvif = iwl_mvm_vif_from_mac80211(vif);
 
-	/* if we're associated, this is wowlan */
-	if (mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) {
+	if (mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT) {
+		/* if we're not associated, this must be netdetect */
+		if (!wowlan->nd_config && !mvm->nd_config) {
+			ret = 1;
+			goto out_noreset;
+		}
+
+		ret = iwl_mvm_netdetect_config(
+			mvm, wowlan, wowlan->nd_config ?: mvm->nd_config, vif);
+		if (ret)
+			goto out;
+
+		mvm->net_detect = true;
+	} else {
+		struct iwl_wowlan_config_cmd wowlan_config_cmd = {};
+
 		ap_sta = rcu_dereference_protected(
 			mvm->fw_id_to_mac_id[mvmvif->ap_sta_id],
 			lockdep_is_held(&mvm->mutex));
@@ -1021,27 +1088,12 @@
 						vif, mvmvif, ap_sta);
 		if (ret)
 			goto out_noreset;
-
-		ret = iwl_mvm_switch_to_d3(mvm);
-		if (ret)
-			goto out;
-
 		ret = iwl_mvm_wowlan_config(mvm, wowlan, &wowlan_config_cmd,
 					    vif, mvmvif, ap_sta);
 		if (ret)
 			goto out;
-	} else if (mvm->nd_config) {
-		ret = iwl_mvm_switch_to_d3(mvm);
-		if (ret)
-			goto out;
 
-		ret = iwl_mvm_scan_offload_start(mvm, vif, mvm->nd_config,
-						 mvm->nd_ies);
-		if (ret)
-			goto out;
-	} else {
-		ret = 1;
-		goto out_noreset;
+		mvm->net_detect = false;
 	}
 
 	ret = iwl_mvm_power_update_device(mvm);
@@ -1075,8 +1127,10 @@
 
 	iwl_trans_d3_suspend(mvm->trans, test);
  out:
-	if (ret < 0)
+	if (ret < 0) {
 		ieee80211_restart_hw(mvm->hw);
+		iwl_mvm_free_nd(mvm);
+	}
  out_noreset:
 	mutex_unlock(&mvm->mutex);
 
@@ -1087,6 +1141,7 @@
 {
 	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
 
+	iwl_trans_suspend(mvm->trans);
 	if (iwl_mvm_is_d0i3_supported(mvm)) {
 		mutex_lock(&mvm->d0i3_suspend_mutex);
 		__set_bit(D0I3_DEFER_WAKEUP, &mvm->d0i3_suspend_flags);
@@ -1465,9 +1520,8 @@
 	return true;
 }
 
-/* releases the MVM mutex */
-static bool iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
-					 struct ieee80211_vif *vif)
+static struct iwl_wowlan_status *
+iwl_mvm_get_wakeup_status(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
 {
 	u32 base = mvm->error_event_table;
 	struct error_table_start {
@@ -1479,19 +1533,15 @@
 		.id = WOWLAN_GET_STATUSES,
 		.flags = CMD_WANT_SKB,
 	};
-	struct iwl_wowlan_status_data status;
-	struct iwl_wowlan_status *fw_status;
-	int ret, len, status_size, i;
-	bool keep;
-	struct ieee80211_sta *ap_sta;
-	struct iwl_mvm_sta *mvm_ap_sta;
+	struct iwl_wowlan_status *status, *fw_status;
+	int ret, len, status_size;
 
 	iwl_trans_read_mem_bytes(mvm->trans, base,
 				 &err_info, sizeof(err_info));
 
 	if (err_info.valid) {
-		IWL_INFO(mvm, "error table is valid (%d)\n",
-			 err_info.valid);
+		IWL_INFO(mvm, "error table is valid (%d) with error (%d)\n",
+			 err_info.valid, err_info.error_id);
 		if (err_info.error_id == RF_KILL_INDICATOR_FOR_WOWLAN) {
 			struct cfg80211_wowlan_wakeup wakeup = {
 				.rfkill_release = true,
@@ -1499,7 +1549,7 @@
 			ieee80211_report_wowlan_wakeup(vif, &wakeup,
 						       GFP_KERNEL);
 		}
-		goto out_unlock;
+		return ERR_PTR(-EIO);
 	}
 
 	/* only for tracing for now */
@@ -1510,22 +1560,53 @@
 	ret = iwl_mvm_send_cmd(mvm, &cmd);
 	if (ret) {
 		IWL_ERR(mvm, "failed to query status (%d)\n", ret);
-		goto out_unlock;
+		return ERR_PTR(ret);
 	}
 
 	/* RF-kill already asserted again... */
-	if (!cmd.resp_pkt)
-		goto out_unlock;
+	if (!cmd.resp_pkt) {
+		ret = -ERFKILL;
+		goto out_free_resp;
+	}
 
 	status_size = sizeof(*fw_status);
 
 	len = iwl_rx_packet_payload_len(cmd.resp_pkt);
 	if (len < status_size) {
 		IWL_ERR(mvm, "Invalid WoWLAN status response!\n");
+		ret = -EIO;
 		goto out_free_resp;
 	}
 
-	fw_status = (void *)cmd.resp_pkt->data;
+	status = (void *)cmd.resp_pkt->data;
+	if (len != (status_size +
+		    ALIGN(le32_to_cpu(status->wake_packet_bufsize), 4))) {
+		IWL_ERR(mvm, "Invalid WoWLAN status response!\n");
+		ret = -EIO;
+		goto out_free_resp;
+	}
+
+	fw_status = kmemdup(status, len, GFP_KERNEL);
+
+out_free_resp:
+	iwl_free_resp(&cmd);
+	return ret ? ERR_PTR(ret) : fw_status;
+}
+
+/* releases the MVM mutex */
+static bool iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
+					 struct ieee80211_vif *vif)
+{
+	struct iwl_wowlan_status_data status;
+	struct iwl_wowlan_status *fw_status;
+	int i;
+	bool keep;
+	struct ieee80211_sta *ap_sta;
+	struct iwl_mvm_sta *mvm_ap_sta;
+
+	fw_status = iwl_mvm_get_wakeup_status(mvm, vif);
+	if (IS_ERR_OR_NULL(fw_status))
+		goto out_unlock;
 
 	status.pattern_number = le16_to_cpu(fw_status->pattern_number);
 	for (i = 0; i < 8; i++)
@@ -1538,17 +1619,12 @@
 		le32_to_cpu(fw_status->wake_packet_bufsize);
 	status.wake_packet = fw_status->wake_packet;
 
-	if (len != status_size + ALIGN(status.wake_packet_bufsize, 4)) {
-		IWL_ERR(mvm, "Invalid WoWLAN status response!\n");
-		goto out_free_resp;
-	}
-
 	/* still at hard-coded place 0 for D3 image */
 	ap_sta = rcu_dereference_protected(
 			mvm->fw_id_to_mac_id[0],
 			lockdep_is_held(&mvm->mutex));
 	if (IS_ERR_OR_NULL(ap_sta))
-		goto out_free_resp;
+		goto out_free;
 
 	mvm_ap_sta = (struct iwl_mvm_sta *)ap_sta->drv_priv;
 	for (i = 0; i < IWL_MAX_TID_COUNT; i++) {
@@ -1565,16 +1641,151 @@
 
 	keep = iwl_mvm_setup_connection_keep(mvm, vif, fw_status);
 
-	iwl_free_resp(&cmd);
+	kfree(fw_status);
 	return keep;
 
- out_free_resp:
-	iwl_free_resp(&cmd);
- out_unlock:
+out_free:
+	kfree(fw_status);
+out_unlock:
 	mutex_unlock(&mvm->mutex);
 	return false;
 }
 
+struct iwl_mvm_nd_query_results {
+	u32 matched_profiles;
+	struct iwl_scan_offload_profile_match matches[IWL_SCAN_MAX_PROFILES];
+};
+
+static int
+iwl_mvm_netdetect_query_results(struct iwl_mvm *mvm,
+				struct iwl_mvm_nd_query_results *results)
+{
+	struct iwl_scan_offload_profiles_query *query;
+	struct iwl_host_cmd cmd = {
+		.id = SCAN_OFFLOAD_PROFILES_QUERY_CMD,
+		.flags = CMD_WANT_SKB,
+	};
+	int ret, len;
+
+	ret = iwl_mvm_send_cmd(mvm, &cmd);
+	if (ret) {
+		IWL_ERR(mvm, "failed to query matched profiles (%d)\n", ret);
+		return ret;
+	}
+
+	/* RF-kill already asserted again... */
+	if (!cmd.resp_pkt) {
+		ret = -ERFKILL;
+		goto out_free_resp;
+	}
+
+	len = iwl_rx_packet_payload_len(cmd.resp_pkt);
+	if (len < sizeof(*query)) {
+		IWL_ERR(mvm, "Invalid scan offload profiles query response!\n");
+		ret = -EIO;
+		goto out_free_resp;
+	}
+
+	query = (void *)cmd.resp_pkt->data;
+
+	results->matched_profiles = le32_to_cpu(query->matched_profiles);
+	memcpy(results->matches, query->matches, sizeof(results->matches));
+
+out_free_resp:
+	iwl_free_resp(&cmd);
+	return ret;
+}
+
+static void iwl_mvm_query_netdetect_reasons(struct iwl_mvm *mvm,
+					    struct ieee80211_vif *vif)
+{
+	struct cfg80211_wowlan_nd_info *net_detect = NULL;
+	struct cfg80211_wowlan_wakeup wakeup = {
+		.pattern_idx = -1,
+	};
+	struct cfg80211_wowlan_wakeup *wakeup_report = &wakeup;
+	struct iwl_mvm_nd_query_results query;
+	struct iwl_wowlan_status *fw_status;
+	unsigned long matched_profiles;
+	u32 reasons = 0;
+	int i, j, n_matches, ret;
+
+	fw_status = iwl_mvm_get_wakeup_status(mvm, vif);
+	if (!IS_ERR_OR_NULL(fw_status))
+		reasons = le32_to_cpu(fw_status->wakeup_reasons);
+
+	if (reasons & IWL_WOWLAN_WAKEUP_BY_RFKILL_DEASSERTED)
+		wakeup.rfkill_release = true;
+
+	if (reasons != IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS)
+		goto out;
+
+	ret = iwl_mvm_netdetect_query_results(mvm, &query);
+	if (ret || !query.matched_profiles) {
+		wakeup_report = NULL;
+		goto out;
+	}
+
+	matched_profiles = query.matched_profiles;
+	if (mvm->n_nd_match_sets) {
+		n_matches = hweight_long(matched_profiles);
+	} else {
+		IWL_ERR(mvm, "no net detect match information available\n");
+		n_matches = 0;
+	}
+
+	net_detect = kzalloc(sizeof(*net_detect) +
+			     (n_matches * sizeof(net_detect->matches[0])),
+			     GFP_KERNEL);
+	if (!net_detect || !n_matches)
+		goto out_report_nd;
+
+	for_each_set_bit(i, &matched_profiles, mvm->n_nd_match_sets) {
+		struct iwl_scan_offload_profile_match *fw_match;
+		struct cfg80211_wowlan_nd_match *match;
+		int n_channels = 0;
+
+		fw_match = &query.matches[i];
+
+		for (j = 0; j < SCAN_OFFLOAD_MATCHING_CHANNELS_LEN; j++)
+			n_channels += hweight8(fw_match->matching_channels[j]);
+
+		match = kzalloc(sizeof(*match) +
+				(n_channels * sizeof(*match->channels)),
+				GFP_KERNEL);
+		if (!match)
+			goto out_report_nd;
+
+		net_detect->matches[net_detect->n_matches++] = match;
+
+		match->ssid.ssid_len = mvm->nd_match_sets[i].ssid.ssid_len;
+		memcpy(match->ssid.ssid, mvm->nd_match_sets[i].ssid.ssid,
+		       match->ssid.ssid_len);
+
+		if (mvm->n_nd_channels < n_channels)
+			continue;
+
+		for (j = 0; j < SCAN_OFFLOAD_MATCHING_CHANNELS_LEN * 8; j++)
+			if (fw_match->matching_channels[j / 8] & (BIT(j % 8)))
+				match->channels[match->n_channels++] =
+					mvm->nd_channels[j]->center_freq;
+	}
+
+out_report_nd:
+	wakeup.net_detect = net_detect;
+out:
+	iwl_mvm_free_nd(mvm);
+
+	mutex_unlock(&mvm->mutex);
+	ieee80211_report_wowlan_wakeup(vif, wakeup_report, GFP_KERNEL);
+
+	if (net_detect) {
+		for (i = 0; i < net_detect->n_matches; i++)
+			kfree(net_detect->matches[i]);
+		kfree(net_detect);
+	}
+}
+
 static void iwl_mvm_read_d3_sram(struct iwl_mvm *mvm)
 {
 #ifdef CONFIG_IWLWIFI_DEBUGFS
@@ -1632,11 +1843,15 @@
 	/* query SRAM first in case we want event logging */
 	iwl_mvm_read_d3_sram(mvm);
 
-	keep = iwl_mvm_query_wakeup_reasons(mvm, vif);
+	if (mvm->net_detect) {
+		iwl_mvm_query_netdetect_reasons(mvm, vif);
+	} else {
+		keep = iwl_mvm_query_wakeup_reasons(mvm, vif);
 #ifdef CONFIG_IWLWIFI_DEBUGFS
-	if (keep)
-		mvm->keep_vif = vif;
+		if (keep)
+			mvm->keep_vif = vif;
 #endif
+	}
 	/* has unlocked the mutex, so skip that */
 	goto out;
 
@@ -1651,6 +1866,7 @@
 
 	/* return 1 to reconfigure the device */
 	set_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
+	set_bit(IWL_MVM_STATUS_D3_RECONFIG, &mvm->status);
 	return 1;
 }
 
@@ -1658,18 +1874,10 @@
 {
 	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
 
-	if (iwl_mvm_is_d0i3_supported(mvm)) {
-		bool exit_now;
+	iwl_trans_resume(mvm->trans);
 
-		mutex_lock(&mvm->d0i3_suspend_mutex);
-		__clear_bit(D0I3_DEFER_WAKEUP, &mvm->d0i3_suspend_flags);
-		exit_now = __test_and_clear_bit(D0I3_PENDING_WAKEUP,
-						&mvm->d0i3_suspend_flags);
-		mutex_unlock(&mvm->d0i3_suspend_mutex);
-		if (exit_now)
-			_iwl_mvm_exit_d0i3(mvm);
+	if (iwl_mvm_is_d0i3_supported(mvm))
 		return 0;
-	}
 
 	return __iwl_mvm_resume(mvm, false);
 }
diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c
index 51b7116..33bf915 100644
--- a/drivers/net/wireless/iwlwifi/mvm/debugfs.c
+++ b/drivers/net/wireless/iwlwifi/mvm/debugfs.c
@@ -936,7 +936,11 @@
 	if (scan_rx_ant & ~mvm->fw->valid_rx_ant)
 		return -EINVAL;
 
-	mvm->scan_rx_ant = scan_rx_ant;
+	if (mvm->scan_rx_ant != scan_rx_ant) {
+		mvm->scan_rx_ant = scan_rx_ant;
+		if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN)
+			iwl_mvm_config_scan(mvm);
+	}
 
 	return count;
 }
@@ -1194,14 +1198,8 @@
 		kfree(mvm->nd_config->match_sets);
 		kfree(mvm->nd_config);
 		mvm->nd_config = NULL;
-		kfree(mvm->nd_ies);
-		mvm->nd_ies = NULL;
 	}
 
-	mvm->nd_ies = kzalloc(sizeof(*mvm->nd_ies), GFP_KERNEL);
-	if (!mvm->nd_ies)
-		return -ENOMEM;
-
 	mvm->nd_config = kzalloc(sizeof(*mvm->nd_config) +
 				 (11 * sizeof(struct ieee80211_channel *)),
 				 GFP_KERNEL);
@@ -1258,8 +1256,6 @@
 		kfree(mvm->nd_config->match_sets);
 	kfree(mvm->nd_config);
 	mvm->nd_config = NULL;
-	kfree(mvm->nd_ies);
-	mvm->nd_ies = NULL;
 out:
 	return ret;
 }
@@ -1343,6 +1339,7 @@
 	PRINT_MVM_REF(IWL_MVM_REF_NMI);
 	PRINT_MVM_REF(IWL_MVM_REF_TM_CMD);
 	PRINT_MVM_REF(IWL_MVM_REF_EXIT_WORK);
+	PRINT_MVM_REF(IWL_MVM_REF_PROTECT_CSA);
 
 	return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
 }
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h
index 816883f..f3b1189 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h
@@ -84,6 +84,8 @@
  * @BT_COEX_SYNC2SCO:
  * @BT_COEX_CORUNNING:
  * @BT_COEX_MPLUT:
+ * @BT_COEX_TTC:
+ * @BT_COEX_RRC:
  *
  * The COEX_MODE must be set for each command. Even if it is not changed.
  */
@@ -100,6 +102,8 @@
 	BT_COEX_SYNC2SCO		= BIT(7),
 	BT_COEX_CORUNNING		= BIT(8),
 	BT_COEX_MPLUT			= BIT(9),
+	BT_COEX_TTC			= BIT(20),
+	BT_COEX_RRC			= BIT(21),
 };
 
 /*
@@ -127,6 +131,8 @@
 	BT_VALID_TXTX_DELTA_FREQ_THRS	= BIT(16),
 	BT_VALID_TXRX_MAX_FREQ_0	= BIT(17),
 	BT_VALID_SYNC_TO_SCO		= BIT(18),
+	BT_VALID_TTC			= BIT(20),
+	BT_VALID_RRC			= BIT(21),
 };
 
 /**
@@ -506,7 +512,8 @@
 	u8 bt_agg_traffic_load;
 	u8 bt_ci_compliance;
 	u8 ttc_enabled;
-	__le16 reserved;
+	u8 rrc_enabled;
+	u8 reserved;
 
 	__le32 primary_ch_lut;
 	__le32 secondary_ch_lut;
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h
index e74cdf2..6d3bea5 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h
@@ -241,16 +241,12 @@
 	IWL_WOWLAN_WAKEUP_BCN_FILTERING			= BIT(16),
 }; /* WOWLAN_WAKEUP_FILTER_API_E_VER_4 */
 
-struct iwl_wowlan_config_cmd_v2 {
+struct iwl_wowlan_config_cmd {
 	__le32 wakeup_filter;
 	__le16 non_qos_seq;
 	__le16 qos_seq[8];
 	u8 wowlan_ba_teardown_tids;
 	u8 is_11n_connection;
-} __packed; /* WOWLAN_CONFIG_API_S_VER_2 */
-
-struct iwl_wowlan_config_cmd_v3 {
-	struct iwl_wowlan_config_cmd_v2 common;
 	u8 offloading_tid;
 	u8 reserved[3];
 } __packed; /* WOWLAN_CONFIG_API_S_VER_3 */
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
index 2fd8ad4..4300200 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
@@ -370,7 +370,7 @@
 #define IWL_BF_DEBUG_FLAG_DEFAULT 0
 #define IWL_BF_DEBUG_FLAG_D0I3 0
 
-#define IWL_BF_ESCAPE_TIMER_DEFAULT 50
+#define IWL_BF_ESCAPE_TIMER_DEFAULT 0
 #define IWL_BF_ESCAPE_TIMER_D0I3 0
 #define IWL_BF_ESCAPE_TIMER_MAX 1024
 #define IWL_BF_ESCAPE_TIMER_MIN 0
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h
index 1354c68..1f2acf4 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h
@@ -794,4 +794,301 @@
 	__le32 reserved;
 } __packed;
 
+/* UMAC Scan API */
+
+/**
+ * struct iwl_mvm_umac_cmd_hdr - Command header for UMAC commands
+ * @size:	size of the command (not including header)
+ * @reserved0:	for future use and alignment
+ * @ver:	API version number
+ */
+struct iwl_mvm_umac_cmd_hdr {
+	__le16 size;
+	u8 reserved0;
+	u8 ver;
+} __packed;
+
+#define IWL_MVM_MAX_SIMULTANEOUS_SCANS 8
+
+enum scan_config_flags {
+	SCAN_CONFIG_FLAG_ACTIVATE			= BIT(0),
+	SCAN_CONFIG_FLAG_DEACTIVATE			= BIT(1),
+	SCAN_CONFIG_FLAG_FORBID_CHUB_REQS		= BIT(2),
+	SCAN_CONFIG_FLAG_ALLOW_CHUB_REQS		= BIT(3),
+	SCAN_CONFIG_FLAG_SET_TX_CHAINS			= BIT(8),
+	SCAN_CONFIG_FLAG_SET_RX_CHAINS			= BIT(9),
+	SCAN_CONFIG_FLAG_SET_AUX_STA_ID			= BIT(10),
+	SCAN_CONFIG_FLAG_SET_ALL_TIMES			= BIT(11),
+	SCAN_CONFIG_FLAG_SET_EFFECTIVE_TIMES		= BIT(12),
+	SCAN_CONFIG_FLAG_SET_CHANNEL_FLAGS		= BIT(13),
+	SCAN_CONFIG_FLAG_SET_LEGACY_RATES		= BIT(14),
+	SCAN_CONFIG_FLAG_SET_MAC_ADDR			= BIT(15),
+	SCAN_CONFIG_FLAG_SET_FRAGMENTED			= BIT(16),
+	SCAN_CONFIG_FLAG_CLEAR_FRAGMENTED		= BIT(17),
+	SCAN_CONFIG_FLAG_SET_CAM_MODE			= BIT(18),
+	SCAN_CONFIG_FLAG_CLEAR_CAM_MODE			= BIT(19),
+	SCAN_CONFIG_FLAG_SET_PROMISC_MODE		= BIT(20),
+	SCAN_CONFIG_FLAG_CLEAR_PROMISC_MODE		= BIT(21),
+
+	/* Bits 26-31 are for num of channels in channel_array */
+#define SCAN_CONFIG_N_CHANNELS(n) ((n) << 26)
+};
+
+enum scan_config_rates {
+	/* OFDM basic rates */
+	SCAN_CONFIG_RATE_6M	= BIT(0),
+	SCAN_CONFIG_RATE_9M	= BIT(1),
+	SCAN_CONFIG_RATE_12M	= BIT(2),
+	SCAN_CONFIG_RATE_18M	= BIT(3),
+	SCAN_CONFIG_RATE_24M	= BIT(4),
+	SCAN_CONFIG_RATE_36M	= BIT(5),
+	SCAN_CONFIG_RATE_48M	= BIT(6),
+	SCAN_CONFIG_RATE_54M	= BIT(7),
+	/* CCK basic rates */
+	SCAN_CONFIG_RATE_1M	= BIT(8),
+	SCAN_CONFIG_RATE_2M	= BIT(9),
+	SCAN_CONFIG_RATE_5M	= BIT(10),
+	SCAN_CONFIG_RATE_11M	= BIT(11),
+
+	/* Bits 16-27 are for supported rates */
+#define SCAN_CONFIG_SUPPORTED_RATE(rate)	((rate) << 16)
+};
+
+enum iwl_channel_flags {
+	IWL_CHANNEL_FLAG_EBS				= BIT(0),
+	IWL_CHANNEL_FLAG_ACCURATE_EBS			= BIT(1),
+	IWL_CHANNEL_FLAG_EBS_ADD			= BIT(2),
+	IWL_CHANNEL_FLAG_PRE_SCAN_PASSIVE2ACTIVE	= BIT(3),
+};
+
+/**
+ * struct iwl_scan_config
+ * @hdr: umac command header
+ * @flags:			enum scan_config_flags
+ * @tx_chains:			valid_tx antenna - ANT_* definitions
+ * @rx_chains:			valid_rx antenna - ANT_* definitions
+ * @legacy_rates:		default legacy rates - enum scan_config_rates
+ * @out_of_channel_time:	default max out of serving channel time
+ * @suspend_time:		default max suspend time
+ * @dwell_active:		default dwell time for active scan
+ * @dwell_passive:		default dwell time for passive scan
+ * @dwell_fragmented:		default dwell time for fragmented scan
+ * @reserved:			for future use and alignment
+ * @mac_addr:			default mac address to be used in probes
+ * @bcast_sta_id:		the index of the station in the fw
+ * @channel_flags:		default channel flags - enum iwl_channel_flags
+ *				scan_config_channel_flag
+ * @channel_array:		default supported channels
+ */
+struct iwl_scan_config {
+	struct iwl_mvm_umac_cmd_hdr hdr;
+	__le32 flags;
+	__le32 tx_chains;
+	__le32 rx_chains;
+	__le32 legacy_rates;
+	__le32 out_of_channel_time;
+	__le32 suspend_time;
+	u8 dwell_active;
+	u8 dwell_passive;
+	u8 dwell_fragmented;
+	u8 reserved;
+	u8 mac_addr[ETH_ALEN];
+	u8 bcast_sta_id;
+	u8 channel_flags;
+	u8 channel_array[];
+} __packed; /* SCAN_CONFIG_DB_CMD_API_S */
+
+/**
+ * iwl_umac_scan_flags
+ *@IWL_UMAC_SCAN_FLAG_PREEMPTIVE: scan process triggered by this scan request
+ *	can be preempted by other scan requests with higher priority.
+ *	The low priority scan is aborted.
+ *@IWL_UMAC_SCAN_FLAG_START_NOTIF: notification will be sent to the driver
+ *	when scan starts.
+ */
+enum iwl_umac_scan_flags {
+	IWL_UMAC_SCAN_FLAG_PREEMPTIVE		= BIT(0),
+	IWL_UMAC_SCAN_FLAG_START_NOTIF		= BIT(1),
+};
+
+enum iwl_umac_scan_uid_offsets {
+	IWL_UMAC_SCAN_UID_TYPE_OFFSET		= 0,
+	IWL_UMAC_SCAN_UID_SEQ_OFFSET		= 8,
+};
+
+enum iwl_umac_scan_general_flags {
+	IWL_UMAC_SCAN_GEN_FLAGS_PERIODIC	= BIT(0),
+	IWL_UMAC_SCAN_GEN_FLAGS_OVER_BT		= BIT(1),
+	IWL_UMAC_SCAN_GEN_FLAGS_PASS_ALL	= BIT(2),
+	IWL_UMAC_SCAN_GEN_FLAGS_PASSIVE		= BIT(3),
+	IWL_UMAC_SCAN_GEN_FLAGS_PRE_CONNECT	= BIT(4),
+	IWL_UMAC_SCAN_GEN_FLAGS_ITER_COMPLETE	= BIT(5),
+	IWL_UMAC_SCAN_GEN_FLAGS_MULTIPLE_SSID	= BIT(6),
+	IWL_UMAC_SCAN_GEN_FLAGS_FRAGMENTED	= BIT(7),
+	IWL_UMAC_SCAN_GEN_FLAGS_RRM_ENABLED	= BIT(8),
+	IWL_UMAC_SCAN_GEN_FLAGS_MATCH		= BIT(9)
+};
+
+/**
+ * struct iwl_scan_channel_cfg_umac
+ * @flags:		bitmap - 0-19:	directed scan to i'th ssid.
+ * @channel_num:	channel number 1-13 etc.
+ * @iter_count:		repetition count for the channel.
+ * @iter_interval:	interval between two scan interations on one channel.
+ */
+struct iwl_scan_channel_cfg_umac {
+	__le32 flags;
+	u8 channel_num;
+	u8 iter_count;
+	__le16 iter_interval;
+} __packed; /* SCAN_CHANNEL_CFG_S_VER2 */
+
+/**
+ * struct iwl_scan_umac_schedule
+ * @interval: interval in seconds between scan iterations
+ * @iter_count: num of scan iterations for schedule plan, 0xff for infinite loop
+ * @reserved: for alignment and future use
+ */
+struct iwl_scan_umac_schedule {
+	__le16 interval;
+	u8 iter_count;
+	u8 reserved;
+} __packed; /* SCAN_SCHED_PARAM_API_S_VER_1 */
+
+/**
+ * struct iwl_scan_req_umac_tail - the rest of the UMAC scan request command
+ *      parameters following channels configuration array.
+ * @schedule: two scheduling plans.
+ * @delay: delay in TUs before starting the first scan iteration
+ * @reserved: for future use and alignment
+ * @preq: probe request with IEs blocks
+ * @direct_scan: list of SSIDs for directed active scan
+ */
+struct iwl_scan_req_umac_tail {
+	/* SCAN_PERIODIC_PARAMS_API_S_VER_1 */
+	struct iwl_scan_umac_schedule schedule[2];
+	__le16 delay;
+	__le16 reserved;
+	/* SCAN_PROBE_PARAMS_API_S_VER_1 */
+	struct iwl_scan_probe_req preq;
+	struct iwl_ssid_ie direct_scan[PROBE_OPTION_MAX];
+} __packed;
+
+/**
+ * struct iwl_scan_req_umac
+ * @hdr: umac command header
+ * @flags: &enum iwl_umac_scan_flags
+ * @uid: scan id, &enum iwl_umac_scan_uid_offsets
+ * @ooc_priority: out of channel priority - &enum iwl_scan_priority
+ * @general_flags: &enum iwl_umac_scan_general_flags
+ * @reserved1: for future use and alignment
+ * @active_dwell: dwell time for active scan
+ * @passive_dwell: dwell time for passive scan
+ * @fragmented_dwell: dwell time for fragmented passive scan
+ * @max_out_time: max out of serving channel time
+ * @suspend_time: max suspend time
+ * @scan_priority: scan internal prioritization &enum iwl_scan_priority
+ * @channel_flags: &enum iwl_scan_channel_flags
+ * @n_channels: num of channels in scan request
+ * @reserved2: for future use and alignment
+ * @data: &struct iwl_scan_channel_cfg_umac and
+ *	&struct iwl_scan_req_umac_tail
+ */
+struct iwl_scan_req_umac {
+	struct iwl_mvm_umac_cmd_hdr hdr;
+	__le32 flags;
+	__le32 uid;
+	__le32 ooc_priority;
+	/* SCAN_GENERAL_PARAMS_API_S_VER_1 */
+	__le32 general_flags;
+	u8 reserved1;
+	u8 active_dwell;
+	u8 passive_dwell;
+	u8 fragmented_dwell;
+	__le32 max_out_time;
+	__le32 suspend_time;
+	__le32 scan_priority;
+	/* SCAN_CHANNEL_PARAMS_API_S_VER_1 */
+	u8 channel_flags;
+	u8 n_channels;
+	__le16 reserved2;
+	u8 data[];
+} __packed; /* SCAN_REQUEST_CMD_UMAC_API_S_VER_1 */
+
+/**
+ * struct iwl_umac_scan_abort
+ * @hdr: umac command header
+ * @uid: scan id, &enum iwl_umac_scan_uid_offsets
+ * @flags: reserved
+ */
+struct iwl_umac_scan_abort {
+	struct iwl_mvm_umac_cmd_hdr hdr;
+	__le32 uid;
+	__le32 flags;
+} __packed; /* SCAN_ABORT_CMD_UMAC_API_S_VER_1 */
+
+/**
+ * struct iwl_umac_scan_complete
+ * @uid: scan id, &enum iwl_umac_scan_uid_offsets
+ * @last_schedule: last scheduling line
+ * @last_iter:	last scan iteration number
+ * @scan status: &enum iwl_scan_offload_complete_status
+ * @ebs_status: &enum iwl_scan_ebs_status
+ * @time_from_last_iter: time elapsed from last iteration
+ * @reserved: for future use
+ */
+struct iwl_umac_scan_complete {
+	__le32 uid;
+	u8 last_schedule;
+	u8 last_iter;
+	u8 status;
+	u8 ebs_status;
+	__le32 time_from_last_iter;
+	__le32 reserved;
+} __packed; /* SCAN_COMPLETE_NTF_UMAC_API_S_VER_1 */
+
+#define SCAN_OFFLOAD_MATCHING_CHANNELS_LEN 5
+/**
+ * struct iwl_scan_offload_profile_match - match information
+ * @bssid: matched bssid
+ * @channel: channel where the match occurred
+ * @energy:
+ * @matching_feature:
+ * @matching_channels: bitmap of channels that matched, referencing
+ *	the channels passed in tue scan offload request
+ */
+struct iwl_scan_offload_profile_match {
+	u8 bssid[ETH_ALEN];
+	__le16 reserved;
+	u8 channel;
+	u8 energy;
+	u8 matching_feature;
+	u8 matching_channels[SCAN_OFFLOAD_MATCHING_CHANNELS_LEN];
+} __packed; /* SCAN_OFFLOAD_PROFILE_MATCH_RESULTS_S_VER_1 */
+
+/**
+ * struct iwl_scan_offload_profiles_query - match results query response
+ * @matched_profiles: bitmap of matched profiles, referencing the
+ *	matches passed in the scan offload request
+ * @last_scan_age: age of the last offloaded scan
+ * @n_scans_done: number of offloaded scans done
+ * @gp2_d0u: GP2 when D0U occurred
+ * @gp2_invoked: GP2 when scan offload was invoked
+ * @resume_while_scanning: not used
+ * @self_recovery: obsolete
+ * @reserved: reserved
+ * @matches: array of match information, one for each match
+ */
+struct iwl_scan_offload_profiles_query {
+	__le32 matched_profiles;
+	__le32 last_scan_age;
+	__le32 n_scans_done;
+	__le32 gp2_d0u;
+	__le32 gp2_invoked;
+	u8 resume_while_scanning;
+	u8 self_recovery;
+	__le16 reserved;
+	struct iwl_scan_offload_profile_match matches[IWL_SCAN_MAX_PROFILES];
+} __packed; /* SCAN_OFFLOAD_PROFILES_QUERY_RSP_S_VER_2 */
+
 #endif
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/iwlwifi/mvm/fw-api.h
index c62575d..88af6dd 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api.h
@@ -106,6 +106,12 @@
 	DBG_CFG = 0x9,
 	ANTENNA_COUPLING_NOTIFICATION = 0xa,
 
+	/* UMAC scan commands */
+	SCAN_CFG_CMD = 0xc,
+	SCAN_REQ_UMAC = 0xd,
+	SCAN_ABORT_UMAC = 0xe,
+	SCAN_COMPLETE_UMAC = 0xf,
+
 	/* station table */
 	ADD_STA_KEY = 0x17,
 	ADD_STA = 0x18,
@@ -122,6 +128,11 @@
 	/* global key */
 	WEP_KEY = 0x20,
 
+	/* TDLS */
+	TDLS_CHANNEL_SWITCH_CMD = 0x27,
+	TDLS_CHANNEL_SWITCH_NOTIFICATION = 0xaa,
+	TDLS_CONFIG_CMD = 0xa7,
+
 	/* MAC and Binding commands */
 	MAC_CONTEXT_CMD = 0x28,
 	TIME_EVENT_CMD = 0x29, /* both CMD and response */
@@ -190,6 +201,8 @@
 	/* Power - new power table command */
 	MAC_PM_POWER_TABLE = 0xa9,
 
+	MFUART_LOAD_NOTIFICATION = 0xb1,
+
 	REPLY_RX_PHY_CMD = 0xc0,
 	REPLY_RX_MPDU_CMD = 0xc1,
 	BA_NOTIF = 0xc5,
@@ -236,11 +249,9 @@
 	WOWLAN_TX_POWER_PER_DB = 0xe6,
 
 	/* and for NetDetect */
-	NET_DETECT_CONFIG_CMD = 0x54,
-	NET_DETECT_PROFILES_QUERY_CMD = 0x56,
-	NET_DETECT_PROFILES_CMD = 0x57,
-	NET_DETECT_HOTSPOTS_CMD = 0x58,
-	NET_DETECT_HOTSPOTS_QUERY_CMD = 0x59,
+	SCAN_OFFLOAD_PROFILES_QUERY_CMD = 0x56,
+	SCAN_OFFLOAD_HOTSPOTS_CONFIG_CMD = 0x58,
+	SCAN_OFFLOAD_HOTSPOTS_QUERY_CMD = 0x59,
 
 	REPLY_MAX = 0xff,
 };
@@ -1201,6 +1212,21 @@
 } __packed; /* MISSED_BEACON_NTFY_API_S_VER_3 */
 
 /**
+ * struct iwl_mfuart_load_notif - mfuart image version & status
+ * ( MFUART_LOAD_NOTIFICATION = 0xb1 )
+ * @installed_ver: installed image version
+ * @external_ver: external image version
+ * @status: MFUART loading status
+ * @duration: MFUART loading time
+*/
+struct iwl_mfuart_load_notif {
+	__le32 installed_ver;
+	__le32 external_ver;
+	__le32 status;
+	__le32 duration;
+} __packed; /*MFU_LOADER_NTFY_API_S_VER_1*/
+
+/**
  * struct iwl_set_calib_default_cmd - set default value for calibration.
  * ( SET_CALIB_DEFAULT_CMD = 0x8e )
  * @calib_index: the calibration to set value for
@@ -1589,7 +1615,7 @@
 #define SF_NUM_TIMEOUT_TYPES 2		/* Aging timer and Idle timer */
 
 /* smart FIFO default values */
-#define SF_W_MARK_SISO 4096
+#define SF_W_MARK_SISO 6144
 #define SF_W_MARK_MIMO2 8192
 #define SF_W_MARK_MIMO3 6144
 #define SF_W_MARK_LEGACY 4096
@@ -1711,4 +1737,145 @@
 	u8 flags;
 } __packed;
 
+/***********************************
+ * TDLS API
+ ***********************************/
+
+/* Type of TDLS request */
+enum iwl_tdls_channel_switch_type {
+	TDLS_SEND_CHAN_SW_REQ = 0,
+	TDLS_SEND_CHAN_SW_RESP_AND_MOVE_CH,
+	TDLS_MOVE_CH,
+}; /* TDLS_STA_CHANNEL_SWITCH_CMD_TYPE_API_E_VER_1 */
+
+/**
+ * Switch timing sub-element in a TDLS channel-switch command
+ * @frame_timestamp: GP2 timestamp of channel-switch request/response packet
+ *	received from peer
+ * @max_offchan_duration: What amount of microseconds out of a DTIM is given
+ *	to the TDLS off-channel communication. For instance if the DTIM is
+ *	200TU and the TDLS peer is to be given 25% of the time, the value
+ *	given will be 50TU, or 50 * 1024 if translated into microseconds.
+ * @switch_time: switch time the peer sent in its channel switch timing IE
+ * @switch_timout: switch timeout the peer sent in its channel switch timing IE
+ */
+struct iwl_tdls_channel_switch_timing {
+	__le32 frame_timestamp; /* GP2 time of peer packet Rx */
+	__le32 max_offchan_duration; /* given in micro-seconds */
+	__le32 switch_time; /* given in micro-seconds */
+	__le32 switch_timeout; /* given in micro-seconds */
+} __packed; /* TDLS_STA_CHANNEL_SWITCH_TIMING_DATA_API_S_VER_1 */
+
+#define IWL_TDLS_CH_SW_FRAME_MAX_SIZE 200
+
+/**
+ * TDLS channel switch frame template
+ *
+ * A template representing a TDLS channel-switch request or response frame
+ *
+ * @switch_time_offset: offset to the channel switch timing IE in the template
+ * @tx_cmd: Tx parameters for the frame
+ * @data: frame data
+ */
+struct iwl_tdls_channel_switch_frame {
+	__le32 switch_time_offset;
+	struct iwl_tx_cmd tx_cmd;
+	u8 data[IWL_TDLS_CH_SW_FRAME_MAX_SIZE];
+} __packed; /* TDLS_STA_CHANNEL_SWITCH_FRAME_API_S_VER_1 */
+
+/**
+ * TDLS channel switch command
+ *
+ * The command is sent to initiate a channel switch and also in response to
+ * incoming TDLS channel-switch request/response packets from remote peers.
+ *
+ * @switch_type: see &enum iwl_tdls_channel_switch_type
+ * @peer_sta_id: station id of TDLS peer
+ * @ci: channel we switch to
+ * @timing: timing related data for command
+ * @frame: channel-switch request/response template, depending to switch_type
+ */
+struct iwl_tdls_channel_switch_cmd {
+	u8 switch_type;
+	__le32 peer_sta_id;
+	struct iwl_fw_channel_info ci;
+	struct iwl_tdls_channel_switch_timing timing;
+	struct iwl_tdls_channel_switch_frame frame;
+} __packed; /* TDLS_STA_CHANNEL_SWITCH_CMD_API_S_VER_1 */
+
+/**
+ * TDLS channel switch start notification
+ *
+ * @status: non-zero on success
+ * @offchannel_duration: duration given in microseconds
+ * @sta_id: peer currently performing the channel-switch with
+ */
+struct iwl_tdls_channel_switch_notif {
+	__le32 status;
+	__le32 offchannel_duration;
+	__le32 sta_id;
+} __packed; /* TDLS_STA_CHANNEL_SWITCH_NTFY_API_S_VER_1 */
+
+/**
+ * TDLS station info
+ *
+ * @sta_id: station id of the TDLS peer
+ * @tx_to_peer_tid: TID reserved vs. the peer for FW based Tx
+ * @tx_to_peer_ssn: initial SSN the FW should use for Tx on its TID vs the peer
+ * @is_initiator: 1 if the peer is the TDLS link initiator, 0 otherwise
+ */
+struct iwl_tdls_sta_info {
+	u8 sta_id;
+	u8 tx_to_peer_tid;
+	__le16 tx_to_peer_ssn;
+	__le32 is_initiator;
+} __packed; /* TDLS_STA_INFO_VER_1 */
+
+/**
+ * TDLS basic config command
+ *
+ * @id_and_color: MAC id and color being configured
+ * @tdls_peer_count: amount of currently connected TDLS peers
+ * @tx_to_ap_tid: TID reverved vs. the AP for FW based Tx
+ * @tx_to_ap_ssn: initial SSN the FW should use for Tx on its TID vs. the AP
+ * @sta_info: per-station info. Only the first tdls_peer_count entries are set
+ * @pti_req_data_offset: offset of network-level data for the PTI template
+ * @pti_req_tx_cmd: Tx parameters for PTI request template
+ * @pti_req_template: PTI request template data
+ */
+struct iwl_tdls_config_cmd {
+	__le32 id_and_color; /* mac id and color */
+	u8 tdls_peer_count;
+	u8 tx_to_ap_tid;
+	__le16 tx_to_ap_ssn;
+	struct iwl_tdls_sta_info sta_info[IWL_MVM_TDLS_STA_COUNT];
+
+	__le32 pti_req_data_offset;
+	struct iwl_tx_cmd pti_req_tx_cmd;
+	u8 pti_req_template[0];
+} __packed; /* TDLS_CONFIG_CMD_API_S_VER_1 */
+
+/**
+ * TDLS per-station config information from FW
+ *
+ * @sta_id: station id of the TDLS peer
+ * @tx_to_peer_last_seq: last sequence number used by FW during FW-based Tx to
+ *	the peer
+ */
+struct iwl_tdls_config_sta_info_res {
+	__le16 sta_id;
+	__le16 tx_to_peer_last_seq;
+} __packed; /* TDLS_STA_INFO_RSP_VER_1 */
+
+/**
+ * TDLS config information from FW
+ *
+ * @tx_to_ap_last_seq: last sequence number used by FW during FW-based Tx to AP
+ * @sta_info: per-station TDLS config information
+ */
+struct iwl_tdls_config_res {
+	__le32 tx_to_ap_last_seq;
+	struct iwl_tdls_config_sta_info_res sta_info[IWL_MVM_TDLS_STA_COUNT];
+} __packed; /* TDLS_CONFIG_RSP_API_S_VER_1 */
+
 #endif /* __fw_api_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw.c b/drivers/net/wireless/iwlwifi/mvm/fw.c
index e0d9f19..d0fa6e9 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw.c
+++ b/drivers/net/wireless/iwlwifi/mvm/fw.c
@@ -186,7 +186,12 @@
 	static const u8 alive_cmd[] = { MVM_ALIVE };
 	struct iwl_sf_region st_fwrd_space;
 
-	fw = iwl_get_ucode_image(mvm, ucode_type);
+	if (ucode_type == IWL_UCODE_REGULAR &&
+	    iwl_fw_dbg_conf_usniffer(mvm->fw, FW_DBG_CUSTOM) &&
+	    iwl_fw_dbg_conf_enabled(mvm->fw, FW_DBG_CUSTOM))
+		fw = iwl_get_ucode_image(mvm, IWL_UCODE_REGULAR_USNIFFER);
+	else
+		fw = iwl_get_ucode_image(mvm, ucode_type);
 	if (WARN_ON(!fw))
 		return -EINVAL;
 	mvm->cur_ucode = ucode_type;
@@ -227,6 +232,10 @@
 	st_fwrd_space.addr = mvm->sf_space.addr;
 	st_fwrd_space.size = mvm->sf_space.size;
 	ret = iwl_trans_update_sf(mvm->trans, &st_fwrd_space);
+	if (ret) {
+		IWL_ERR(mvm, "Failed to update SF size. ret %d\n", ret);
+		return ret;
+	}
 
 	iwl_trans_fw_alive(mvm->trans, alive_data.scd_base_addr);
 
@@ -284,7 +293,7 @@
 
 	lockdep_assert_held(&mvm->mutex);
 
-	if (WARN_ON_ONCE(mvm->init_ucode_complete))
+	if (WARN_ON_ONCE(mvm->init_ucode_complete || mvm->calibrating))
 		return 0;
 
 	iwl_init_notification_wait(&mvm->notif_wait,
@@ -334,6 +343,8 @@
 		goto out;
 	}
 
+	mvm->calibrating = true;
+
 	/* Send TX valid antennas before triggering calibrations */
 	ret = iwl_send_tx_ant_cfg(mvm, mvm->fw->valid_tx_ant);
 	if (ret)
@@ -358,11 +369,17 @@
 			MVM_UCODE_CALIB_TIMEOUT);
 	if (!ret)
 		mvm->init_ucode_complete = true;
+
+	if (ret && iwl_mvm_is_radio_killed(mvm)) {
+		IWL_DEBUG_RF_KILL(mvm, "RFKILL while calibrating.\n");
+		ret = 1;
+	}
 	goto out;
 
 error:
 	iwl_remove_notification(&mvm->notif_wait, &calib_wait);
 out:
+	mvm->calibrating = false;
 	if (iwlmvm_mod_params.init_dbg && !mvm->nvm_data) {
 		/* we want to debug INIT and we have no NVM - fake */
 		mvm->nvm_data = kzalloc(sizeof(struct iwl_nvm_data) +
@@ -382,6 +399,42 @@
 	return ret;
 }
 
+static int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm,
+				     enum iwl_fw_dbg_conf conf_id)
+{
+	u8 *ptr;
+	int ret;
+	int i;
+
+	if (WARN_ONCE(conf_id >= ARRAY_SIZE(mvm->fw->dbg_conf_tlv),
+		      "Invalid configuration %d\n", conf_id))
+		return -EINVAL;
+
+	if (!mvm->fw->dbg_conf_tlv[conf_id])
+		return -EINVAL;
+
+	if (mvm->fw_dbg_conf != FW_DBG_INVALID)
+		IWL_WARN(mvm, "FW already configured (%d) - re-configuring\n",
+			 mvm->fw_dbg_conf);
+
+	/* Send all HCMDs for configuring the FW debug */
+	ptr = (void *)&mvm->fw->dbg_conf_tlv[conf_id]->hcmd;
+	for (i = 0; i < mvm->fw->dbg_conf_tlv[conf_id]->num_of_hcmds; i++) {
+		struct iwl_fw_dbg_conf_hcmd *cmd = (void *)ptr;
+
+		ret = iwl_mvm_send_cmd_pdu(mvm, cmd->id, 0,
+					   le16_to_cpu(cmd->len), cmd->data);
+		if (ret)
+			return ret;
+
+		ptr += sizeof(*cmd);
+		ptr += le16_to_cpu(cmd->len);
+	}
+
+	mvm->fw_dbg_conf = conf_id;
+	return ret;
+}
+
 int iwl_mvm_up(struct iwl_mvm *mvm)
 {
 	int ret, i;
@@ -433,6 +486,9 @@
 	if (ret)
 		IWL_ERR(mvm, "Failed to initialize Smart Fifo\n");
 
+	mvm->fw_dbg_conf = FW_DBG_INVALID;
+	iwl_mvm_start_fw_dbg_conf(mvm, FW_DBG_CUSTOM);
+
 	ret = iwl_send_tx_ant_cfg(mvm, mvm->fw->valid_tx_ant);
 	if (ret)
 		goto error;
@@ -454,6 +510,8 @@
 	for (i = 0; i < IWL_MVM_STATION_COUNT; i++)
 		RCU_INIT_POINTER(mvm->fw_id_to_mac_id[i], NULL);
 
+	mvm->tdls_cs.peer.sta_id = IWL_MVM_STATION_COUNT;
+
 	/* reset quota debouncing buffer - 0xff will yield invalid data */
 	memset(&mvm->last_quota_cmd, 0xff, sizeof(mvm->last_quota_cmd));
 
@@ -493,6 +551,12 @@
 	if (ret)
 		goto error;
 
+	if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN) {
+		ret = iwl_mvm_config_scan(mvm);
+		if (ret)
+			goto error;
+	}
+
 	/* allow FW/transport low power modes if not during restart */
 	if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
 		iwl_mvm_unref(mvm, IWL_MVM_REF_UCODE_DOWN);
@@ -579,3 +643,19 @@
 		       le32_to_cpu(radio_version->radio_dash));
 	return 0;
 }
+
+int iwl_mvm_rx_mfuart_notif(struct iwl_mvm *mvm,
+			    struct iwl_rx_cmd_buffer *rxb,
+			    struct iwl_device_cmd *cmd)
+{
+	struct iwl_rx_packet *pkt = rxb_addr(rxb);
+	struct iwl_mfuart_load_notif *mfuart_notif = (void *)pkt->data;
+
+	IWL_DEBUG_INFO(mvm,
+		       "MFUART: installed ver: 0x%08x, external ver: 0x%08x, status: 0x%08x, duration: 0x%08x\n",
+		       le32_to_cpu(mfuart_notif->installed_ver),
+		       le32_to_cpu(mfuart_notif->external_ver),
+		       le32_to_cpu(mfuart_notif->status),
+		       le32_to_cpu(mfuart_notif->duration));
+	return 0;
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c
index b8ab4a1..f6d86cc 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c
@@ -83,11 +83,15 @@
 	struct ieee80211_vif *vif;
 	unsigned long available_mac_ids[BITS_TO_LONGS(NUM_MAC_INDEX_DRIVER)];
 	unsigned long available_tsf_ids[BITS_TO_LONGS(NUM_TSF_IDS)];
-	u32 used_hw_queues;
 	enum iwl_tsf_id preferred_tsf;
 	bool found_vif;
 };
 
+struct iwl_mvm_hw_queues_iface_iterator_data {
+	struct ieee80211_vif *exclude_vif;
+	unsigned long used_hw_queues;
+};
+
 static void iwl_mvm_mac_tsf_id_iter(void *_data, u8 *mac,
 				    struct ieee80211_vif *vif)
 {
@@ -213,6 +217,54 @@
 	return qmask;
 }
 
+static void iwl_mvm_iface_hw_queues_iter(void *_data, u8 *mac,
+					 struct ieee80211_vif *vif)
+{
+	struct iwl_mvm_hw_queues_iface_iterator_data *data = _data;
+
+	/* exclude the given vif */
+	if (vif == data->exclude_vif)
+		return;
+
+	data->used_hw_queues |= iwl_mvm_mac_get_queues_mask(vif);
+}
+
+static void iwl_mvm_mac_sta_hw_queues_iter(void *_data,
+					   struct ieee80211_sta *sta)
+{
+	struct iwl_mvm_hw_queues_iface_iterator_data *data = _data;
+	struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+
+	/* Mark the queues used by the sta */
+	data->used_hw_queues |= mvmsta->tfd_queue_msk;
+}
+
+unsigned long iwl_mvm_get_used_hw_queues(struct iwl_mvm *mvm,
+					 struct ieee80211_vif *exclude_vif)
+{
+	struct iwl_mvm_hw_queues_iface_iterator_data data = {
+		.exclude_vif = exclude_vif,
+		.used_hw_queues =
+			BIT(IWL_MVM_OFFCHANNEL_QUEUE) |
+			BIT(mvm->aux_queue) |
+			BIT(IWL_MVM_CMD_QUEUE),
+	};
+
+	lockdep_assert_held(&mvm->mutex);
+
+	/* mark all VIF used hw queues */
+	ieee80211_iterate_active_interfaces_atomic(
+		mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
+		iwl_mvm_iface_hw_queues_iter, &data);
+
+	/* don't assign the same hw queues as TDLS stations */
+	ieee80211_iterate_stations_atomic(mvm->hw,
+					  iwl_mvm_mac_sta_hw_queues_iter,
+					  &data);
+
+	return data.used_hw_queues;
+}
+
 static void iwl_mvm_mac_iface_iterator(void *_data, u8 *mac,
 				       struct ieee80211_vif *vif)
 {
@@ -225,9 +277,6 @@
 		return;
 	}
 
-	/* Mark the queues used by the vif */
-	data->used_hw_queues |= iwl_mvm_mac_get_queues_mask(vif);
-
 	/* Mark MAC IDs as used by clearing the available bit, and
 	 * (below) mark TSFs as used if their existing use is not
 	 * compatible with the new interface type.
@@ -274,10 +323,6 @@
 		.available_tsf_ids = { (1 << NUM_TSF_IDS) - 1 },
 		/* no preference yet */
 		.preferred_tsf = NUM_TSF_IDS,
-		.used_hw_queues =
-			BIT(IWL_MVM_OFFCHANNEL_QUEUE) |
-			BIT(mvm->aux_queue) |
-			BIT(IWL_MVM_CMD_QUEUE),
 		.found_vif = false,
 	};
 	u32 ac;
@@ -316,6 +361,8 @@
 		mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
 		iwl_mvm_mac_iface_iterator, &data);
 
+	used_hw_queues = iwl_mvm_get_used_hw_queues(mvm, vif);
+
 	/*
 	 * In the case we're getting here during resume, it's similar to
 	 * firmware restart, and with RESUME_ALL the iterator will find
@@ -365,8 +412,6 @@
 		return 0;
 	}
 
-	used_hw_queues = data.used_hw_queues;
-
 	/* Find available queues, and allocate them to the ACs */
 	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
 		u8 queue = find_first_zero_bit(&used_hw_queues,
@@ -1218,17 +1263,25 @@
 }
 
 static void iwl_mvm_csa_count_down(struct iwl_mvm *mvm,
-				   struct ieee80211_vif *csa_vif, u32 gp2)
+				   struct ieee80211_vif *csa_vif, u32 gp2,
+				   bool tx_success)
 {
 	struct iwl_mvm_vif *mvmvif =
 			iwl_mvm_vif_from_mac80211(csa_vif);
 
+	/* Don't start to countdown from a failed beacon */
+	if (!tx_success && !mvmvif->csa_countdown)
+		return;
+
+	mvmvif->csa_countdown = true;
+
 	if (!ieee80211_csa_is_complete(csa_vif)) {
 		int c = ieee80211_csa_update_counter(csa_vif);
 
 		iwl_mvm_mac_ctxt_beacon_changed(mvm, csa_vif);
 		if (csa_vif->p2p &&
-		    !iwl_mvm_te_scheduled(&mvmvif->time_event_data) && gp2) {
+		    !iwl_mvm_te_scheduled(&mvmvif->time_event_data) && gp2 &&
+		    tx_success) {
 			u32 rel_time = (c + 1) *
 				       csa_vif->bss_conf.beacon_int -
 				       IWL_MVM_CHANNEL_SWITCH_TIME_GO;
@@ -1251,38 +1304,30 @@
 			    struct iwl_device_cmd *cmd)
 {
 	struct iwl_rx_packet *pkt = rxb_addr(rxb);
+	struct iwl_extended_beacon_notif *beacon = (void *)pkt->data;
 	struct iwl_mvm_tx_resp *beacon_notify_hdr;
 	struct ieee80211_vif *csa_vif;
 	struct ieee80211_vif *tx_blocked_vif;
-	u64 tsf;
+	u16 status;
 
 	lockdep_assert_held(&mvm->mutex);
 
-	if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_CAPA_EXTENDED_BEACON) {
-		struct iwl_extended_beacon_notif *beacon = (void *)pkt->data;
+	beacon_notify_hdr = &beacon->beacon_notify_hdr;
+	mvm->ap_last_beacon_gp2 = le32_to_cpu(beacon->gp2);
 
-		beacon_notify_hdr = &beacon->beacon_notify_hdr;
-		tsf = le64_to_cpu(beacon->tsf);
-		mvm->ap_last_beacon_gp2 = le32_to_cpu(beacon->gp2);
-	} else {
-		struct iwl_beacon_notif *beacon = (void *)pkt->data;
-
-		beacon_notify_hdr = &beacon->beacon_notify_hdr;
-		tsf = le64_to_cpu(beacon->tsf);
-	}
-
+	status = le16_to_cpu(beacon_notify_hdr->status.status) & TX_STATUS_MSK;
 	IWL_DEBUG_RX(mvm,
 		     "beacon status %#x retries:%d tsf:0x%16llX gp2:0x%X rate:%d\n",
-		     le16_to_cpu(beacon_notify_hdr->status.status) &
-								TX_STATUS_MSK,
-		     beacon_notify_hdr->failure_frame, tsf,
+		     status, beacon_notify_hdr->failure_frame,
+		     le64_to_cpu(beacon->tsf),
 		     mvm->ap_last_beacon_gp2,
 		     le32_to_cpu(beacon_notify_hdr->initial_rate));
 
 	csa_vif = rcu_dereference_protected(mvm->csa_vif,
 					    lockdep_is_held(&mvm->mutex));
 	if (unlikely(csa_vif && csa_vif->csa_active))
-		iwl_mvm_csa_count_down(mvm, csa_vif, mvm->ap_last_beacon_gp2);
+		iwl_mvm_csa_count_down(mvm, csa_vif, mvm->ap_last_beacon_gp2,
+				       (status == TX_STATUS_SUCCESS));
 
 	tx_blocked_vif = rcu_dereference_protected(mvm->csa_tx_blocked_vif,
 						lockdep_is_held(&mvm->mutex));
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
index f308e52..31a5b3f 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
@@ -254,6 +254,26 @@
 	spin_unlock_bh(&mvm->refs_lock);
 }
 
+bool iwl_mvm_ref_taken(struct iwl_mvm *mvm)
+{
+	int i;
+	bool taken = false;
+
+	if (!iwl_mvm_is_d0i3_supported(mvm))
+		return true;
+
+	spin_lock_bh(&mvm->refs_lock);
+	for (i = 0; i < IWL_MVM_REF_COUNT; i++) {
+		if (mvm->refs[i]) {
+			taken = true;
+			break;
+		}
+	}
+	spin_unlock_bh(&mvm->refs_lock);
+
+	return taken;
+}
+
 int iwl_mvm_ref_sync(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type)
 {
 	iwl_mvm_ref(mvm, ref_type);
@@ -303,7 +323,8 @@
 	hw->offchannel_tx_hw_queue = IWL_MVM_OFFCHANNEL_QUEUE;
 	hw->radiotap_mcs_details |= IEEE80211_RADIOTAP_MCS_HAVE_FEC |
 				    IEEE80211_RADIOTAP_MCS_HAVE_STBC;
-	hw->radiotap_vht_details |= IEEE80211_RADIOTAP_VHT_KNOWN_STBC;
+	hw->radiotap_vht_details |= IEEE80211_RADIOTAP_VHT_KNOWN_STBC |
+		IEEE80211_RADIOTAP_VHT_KNOWN_BEAMFORMED;
 	hw->rate_control_algorithm = "iwl-mvm-rs";
 
 	/*
@@ -316,15 +337,19 @@
 		hw->flags |= IEEE80211_HW_MFP_CAPABLE;
 
 	if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT &&
-	    IWL_UCODE_API(mvm->fw->ucode_ver) >= 9 &&
 	    !iwlwifi_mod_params.uapsd_disable) {
 		hw->flags |= IEEE80211_HW_SUPPORTS_UAPSD;
 		hw->uapsd_queues = IWL_MVM_UAPSD_QUEUES;
 		hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP;
 	}
 
-	if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
+	if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN ||
+	    mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN) {
 		hw->flags |= IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS;
+		hw->wiphy->features |=
+			NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR |
+			NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR;
+	}
 
 	hw->sta_data_size = sizeof(struct iwl_mvm_sta);
 	hw->vif_data_size = sizeof(struct iwl_mvm_vif);
@@ -344,8 +369,7 @@
 	if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_GO_UAPSD)
 		hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
 
-	if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_CSA_FLOW)
-		hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
+	hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
 
 	hw->wiphy->iface_combinations = iwl_mvm_iface_combinations;
 	hw->wiphy->n_iface_combinations =
@@ -403,7 +427,8 @@
 			       NL80211_FEATURE_LOW_PRIORITY_SCAN |
 			       NL80211_FEATURE_P2P_GO_OPPPS |
 			       NL80211_FEATURE_DYNAMIC_SMPS |
-			       NL80211_FEATURE_STATIC_SMPS;
+			       NL80211_FEATURE_STATIC_SMPS |
+			       NL80211_FEATURE_SUPPORTS_WMM_ADMISSION;
 
 	if (mvm->fw->ucode_capa.capa[0] &
 	    IWL_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT)
@@ -441,7 +466,8 @@
 		mvm->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
 				    WIPHY_WOWLAN_DISCONNECT |
 				    WIPHY_WOWLAN_EAP_IDENTITY_REQ |
-				    WIPHY_WOWLAN_RFKILL_RELEASE;
+				    WIPHY_WOWLAN_RFKILL_RELEASE |
+				    WIPHY_WOWLAN_NET_DETECT;
 		if (!iwlwifi_mod_params.sw_crypto)
 			mvm->wowlan.flags |= WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
 					     WIPHY_WOWLAN_GTK_REKEY_FAILURE |
@@ -450,6 +476,7 @@
 		mvm->wowlan.n_patterns = IWL_WOWLAN_MAX_PATTERNS;
 		mvm->wowlan.pattern_min_len = IWL_WOWLAN_MIN_PATTERN_LEN;
 		mvm->wowlan.pattern_max_len = IWL_WOWLAN_MAX_PATTERN_LEN;
+		mvm->wowlan.max_nd_match_sets = IWL_SCAN_MAX_PROFILES;
 		mvm->wowlan.tcp = &iwl_mvm_wowlan_tcp_support;
 		hw->wiphy->wowlan = &mvm->wowlan;
 	}
@@ -464,6 +491,17 @@
 	if (ret)
 		return ret;
 
+	if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_TDLS_SUPPORT) {
+		IWL_DEBUG_TDLS(mvm, "TDLS supported\n");
+		hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS;
+	}
+
+	if (mvm->fw->ucode_capa.capa[0] &
+	    IWL_UCODE_TLV_CAPA_TDLS_CHANNEL_SWITCH) {
+		IWL_DEBUG_TDLS(mvm, "TDLS channel switch supported\n");
+		hw->wiphy->features |= NL80211_FEATURE_TDLS_CHANNEL_SWITCH;
+	}
+
 	ret = ieee80211_register_hw(mvm->hw);
 	if (ret)
 		iwl_mvm_leds_exit(mvm);
@@ -819,12 +857,18 @@
 
 static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
 {
-	iwl_mvm_fw_error_dump(mvm);
+	/* clear the D3 reconfig, we only need it to avoid dumping a
+	 * firmware coredump on reconfiguration, we shouldn't do that
+	 * on D3->D0 transition
+	 */
+	if (!test_and_clear_bit(IWL_MVM_STATUS_D3_RECONFIG, &mvm->status))
+		iwl_mvm_fw_error_dump(mvm);
 
 	iwl_trans_stop_device(mvm->trans);
 
 	mvm->scan_status = IWL_MVM_SCAN_NONE;
 	mvm->ps_disabled = false;
+	mvm->calibrating = false;
 
 	/* just in case one was running */
 	ieee80211_remain_on_channel_expired(mvm->hw);
@@ -839,6 +883,7 @@
 	iwl_mvm_reset_phy_ctxts(mvm);
 	memset(mvm->fw_key_table, 0, sizeof(mvm->fw_key_table));
 	memset(mvm->sta_drained, 0, sizeof(mvm->sta_drained));
+	memset(mvm->tfd_drained, 0, sizeof(mvm->tfd_drained));
 	memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif));
 	memset(&mvm->last_bt_notif_old, 0, sizeof(mvm->last_bt_notif_old));
 	memset(&mvm->last_bt_ci_cmd, 0, sizeof(mvm->last_bt_ci_cmd));
@@ -911,9 +956,34 @@
 	/* allow transport/FW low power modes */
 	iwl_mvm_unref(mvm, IWL_MVM_REF_UCODE_DOWN);
 
+	/*
+	 * If we have TDLS peers, remove them. We don't know the last seqno/PN
+	 * of packets the FW sent out, so we must reconnect.
+	 */
+	iwl_mvm_teardown_tdls_peers(mvm);
+
 	mutex_unlock(&mvm->mutex);
 }
 
+static void iwl_mvm_resume_complete(struct iwl_mvm *mvm)
+{
+	bool exit_now;
+
+	if (!iwl_mvm_is_d0i3_supported(mvm))
+		return;
+
+	mutex_lock(&mvm->d0i3_suspend_mutex);
+	__clear_bit(D0I3_DEFER_WAKEUP, &mvm->d0i3_suspend_flags);
+	exit_now = __test_and_clear_bit(D0I3_PENDING_WAKEUP,
+					&mvm->d0i3_suspend_flags);
+	mutex_unlock(&mvm->d0i3_suspend_mutex);
+
+	if (exit_now) {
+		IWL_DEBUG_RPM(mvm, "Run deferred d0i3 exit\n");
+		_iwl_mvm_exit_d0i3(mvm);
+	}
+}
+
 static void
 iwl_mvm_mac_reconfig_complete(struct ieee80211_hw *hw,
 			      enum ieee80211_reconfig_type reconfig_type)
@@ -925,6 +995,7 @@
 		iwl_mvm_restart_complete(mvm);
 		break;
 	case IEEE80211_RECONFIG_TYPE_SUSPEND:
+		iwl_mvm_resume_complete(mvm);
 		break;
 	}
 }
@@ -1888,9 +1959,11 @@
 	    req->n_channels > mvm->fw->ucode_capa.n_scan_channels)
 		return -EINVAL;
 
-	ret = iwl_mvm_cancel_scan_wait_notif(mvm, IWL_MVM_SCAN_SCHED);
-	if (ret)
-		return ret;
+	if (!(mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN)) {
+		ret = iwl_mvm_cancel_scan_wait_notif(mvm, IWL_MVM_SCAN_SCHED);
+		if (ret)
+			return ret;
+	}
 
 	mutex_lock(&mvm->mutex);
 
@@ -1901,7 +1974,9 @@
 
 	iwl_mvm_ref(mvm, IWL_MVM_REF_SCAN);
 
-	if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
+	if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN)
+		ret = iwl_mvm_scan_umac(mvm, vif, hw_req);
+	else if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
 		ret = iwl_mvm_unified_scan_lmac(mvm, vif, hw_req);
 	else
 		ret = iwl_mvm_scan_request(mvm, vif, req);
@@ -2118,6 +2193,15 @@
  out_unlock:
 	mutex_unlock(&mvm->mutex);
 
+	if (sta->tdls && ret == 0) {
+		if (old_state == IEEE80211_STA_NOTEXIST &&
+		    new_state == IEEE80211_STA_NONE)
+			ieee80211_reserve_tid(sta, IWL_MVM_TDLS_FW_TID);
+		else if (old_state == IEEE80211_STA_NONE &&
+			 new_state == IEEE80211_STA_NOTEXIST)
+			ieee80211_unreserve_tid(sta, IWL_MVM_TDLS_FW_TID);
+	}
+
 	return ret;
 }
 
@@ -2200,9 +2284,11 @@
 	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
 	int ret;
 
-	ret = iwl_mvm_cancel_scan_wait_notif(mvm, IWL_MVM_SCAN_OS);
-	if (ret)
-		return ret;
+	if (!(mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN)) {
+		ret = iwl_mvm_cancel_scan_wait_notif(mvm, IWL_MVM_SCAN_OS);
+		if (ret)
+			return ret;
+	}
 
 	mutex_lock(&mvm->mutex);
 
@@ -2222,11 +2308,10 @@
 		goto out;
 	}
 
-	mvm->scan_status = IWL_MVM_SCAN_SCHED;
-
 	ret = iwl_mvm_scan_offload_start(mvm, vif, req, ies);
 	if (ret)
 		mvm->scan_status = IWL_MVM_SCAN_NONE;
+
 out:
 	mutex_unlock(&mvm->mutex);
 	return ret;
@@ -2244,6 +2329,7 @@
 	iwl_mvm_wait_for_async_handlers(mvm);
 
 	return ret;
+
 }
 
 static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
@@ -2272,12 +2358,16 @@
 		break;
 	case WLAN_CIPHER_SUITE_WEP40:
 	case WLAN_CIPHER_SUITE_WEP104:
-		/*
-		 * Support for TX only, at least for now, so accept
-		 * the key and do nothing else. Then mac80211 will
-		 * pass it for TX but we don't have to use it for RX.
+		/* For non-client mode, only use WEP keys for TX as we probably
+		 * don't have a station yet anyway and would then have to keep
+		 * track of the keys, linking them to each of the clients/peers
+		 * as they appear. For now, don't do that, for performance WEP
+		 * offload doesn't really matter much, but we need it for some
+		 * other offload features in client mode.
 		 */
-		return 0;
+		if (vif->type != NL80211_IFTYPE_STATION)
+			return 0;
+		break;
 	default:
 		/* currently FW supports only one optional cipher scheme */
 		if (hw->n_cipher_schemes &&
@@ -2485,9 +2575,15 @@
 
 	switch (vif->type) {
 	case NL80211_IFTYPE_STATION:
-		/* Use aux roc framework (HS20) */
-		ret = iwl_mvm_send_aux_roc_cmd(mvm, channel,
-					       vif, duration);
+		if (mvm->fw->ucode_capa.capa[0] &
+		    IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT) {
+			/* Use aux roc framework (HS20) */
+			ret = iwl_mvm_send_aux_roc_cmd(mvm, channel,
+						       vif, duration);
+			goto out_unlock;
+		}
+		IWL_ERR(mvm, "hotspot not supported\n");
+		ret = -EINVAL;
 		goto out_unlock;
 	case NL80211_IFTYPE_P2P_DEVICE:
 		/* handle below */
@@ -2594,7 +2690,7 @@
 	IWL_DEBUG_MAC80211(mvm, "enter\n");
 
 	mutex_lock(&mvm->mutex);
-	iwl_mvm_stop_p2p_roc(mvm);
+	iwl_mvm_stop_roc(mvm);
 	mutex_unlock(&mvm->mutex);
 
 	IWL_DEBUG_MAC80211(mvm, "leave\n");
@@ -2707,8 +2803,8 @@
 
 	switch (vif->type) {
 	case NL80211_IFTYPE_AP:
-		/* Unless it's a CSA flow we have nothing to do here */
-		if (vif->csa_active) {
+		/* only needed if we're switching chanctx (i.e. during CSA) */
+		if (switching_chanctx) {
 			mvmvif->ap_ibss_active = true;
 			break;
 		}
@@ -2752,23 +2848,32 @@
 	}
 
 	/* Handle binding during CSA */
-	if ((vif->type == NL80211_IFTYPE_AP) ||
-	    (switching_chanctx && (vif->type == NL80211_IFTYPE_STATION))) {
+	if (vif->type == NL80211_IFTYPE_AP) {
 		iwl_mvm_update_quotas(mvm, NULL);
 		iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
 	}
 
-	if (vif->csa_active && vif->type == NL80211_IFTYPE_STATION) {
-		struct iwl_mvm_sta *mvmsta;
+	if (switching_chanctx && vif->type == NL80211_IFTYPE_STATION) {
+		u32 duration = 2 * vif->bss_conf.beacon_int;
 
-		mvmsta = iwl_mvm_sta_from_staid_protected(mvm,
-							  mvmvif->ap_sta_id);
+		/* iwl_mvm_protect_session() reads directly from the
+		 * device (the system time), so make sure it is
+		 * available.
+		 */
+		ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PROTECT_CSA);
+		if (ret)
+			goto out_remove_binding;
 
-		if (WARN_ON(!mvmsta))
-			goto out;
+		/* Protect the session to make sure we hear the first
+		 * beacon on the new channel.
+		 */
+		iwl_mvm_protect_session(mvm, vif, duration, duration,
+					vif->bss_conf.beacon_int / 2,
+					true);
 
-		/* TODO: only re-enable after the first beacon */
-		iwl_mvm_sta_modify_disable_tx(mvm, mvmsta, false);
+		iwl_mvm_unref(mvm, IWL_MVM_REF_PROTECT_CSA);
+
+		iwl_mvm_update_quotas(mvm, NULL);
 	}
 
 	goto out;
@@ -2802,7 +2907,6 @@
 {
 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 	struct ieee80211_vif *disabled_vif = NULL;
-	struct iwl_mvm_sta *mvmsta;
 
 	lockdep_assert_held(&mvm->mutex);
 
@@ -2817,9 +2921,11 @@
 		break;
 	case NL80211_IFTYPE_AP:
 		/* This part is triggered only during CSA */
-		if (!vif->csa_active || !mvmvif->ap_ibss_active)
+		if (!switching_chanctx || !mvmvif->ap_ibss_active)
 			goto out;
 
+		mvmvif->csa_countdown = false;
+
 		/* Set CS bit on all the stations */
 		iwl_mvm_modify_all_sta_disable_tx(mvm, mvmvif, true);
 
@@ -2834,12 +2940,6 @@
 
 		disabled_vif = vif;
 
-		mvmsta = iwl_mvm_sta_from_staid_protected(mvm,
-							  mvmvif->ap_sta_id);
-
-		if (!WARN_ON(!mvmsta))
-			iwl_mvm_sta_modify_disable_tx(mvm, mvmsta, true);
-
 		iwl_mvm_mac_ctxt_changed(mvm, vif, true, NULL);
 		break;
 	default:
@@ -2865,18 +2965,12 @@
 	mutex_unlock(&mvm->mutex);
 }
 
-static int iwl_mvm_switch_vif_chanctx(struct ieee80211_hw *hw,
-				      struct ieee80211_vif_chanctx_switch *vifs,
-				      int n_vifs,
-				      enum ieee80211_chanctx_switch_mode mode)
+static int
+iwl_mvm_switch_vif_chanctx_swap(struct iwl_mvm *mvm,
+				struct ieee80211_vif_chanctx_switch *vifs)
 {
-	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
 	int ret;
 
-	/* we only support SWAP_CONTEXTS and with a single-vif right now */
-	if (mode != CHANCTX_SWMODE_SWAP_CONTEXTS || n_vifs > 1)
-		return -EOPNOTSUPP;
-
 	mutex_lock(&mvm->mutex);
 	__iwl_mvm_unassign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx, true);
 	__iwl_mvm_remove_chanctx(mvm, vifs[0].old_ctx);
@@ -2905,15 +2999,13 @@
 	__iwl_mvm_remove_chanctx(mvm, vifs[0].new_ctx);
 
 out_reassign:
-	ret = __iwl_mvm_add_chanctx(mvm, vifs[0].old_ctx);
-	if (ret) {
+	if (__iwl_mvm_add_chanctx(mvm, vifs[0].old_ctx)) {
 		IWL_ERR(mvm, "failed to add old_ctx back after failure.\n");
 		goto out_restart;
 	}
 
-	ret = __iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx,
-					   true);
-	if (ret) {
+	if (__iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx,
+					 true)) {
 		IWL_ERR(mvm, "failed to reassign old_ctx after failure.\n");
 		goto out_restart;
 	}
@@ -2926,6 +3018,72 @@
 
 out:
 	mutex_unlock(&mvm->mutex);
+
+	return ret;
+}
+
+static int
+iwl_mvm_switch_vif_chanctx_reassign(struct iwl_mvm *mvm,
+				    struct ieee80211_vif_chanctx_switch *vifs)
+{
+	int ret;
+
+	mutex_lock(&mvm->mutex);
+	__iwl_mvm_unassign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx, true);
+
+	ret = __iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].new_ctx,
+					   true);
+	if (ret) {
+		IWL_ERR(mvm,
+			"failed to assign new_ctx during channel switch\n");
+		goto out_reassign;
+	}
+
+	goto out;
+
+out_reassign:
+	if (__iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx,
+					 true)) {
+		IWL_ERR(mvm, "failed to reassign old_ctx after failure.\n");
+		goto out_restart;
+	}
+
+	goto out;
+
+out_restart:
+	/* things keep failing, better restart the hw */
+	iwl_mvm_nic_restart(mvm, false);
+
+out:
+	mutex_unlock(&mvm->mutex);
+
+	return ret;
+}
+
+static int iwl_mvm_switch_vif_chanctx(struct ieee80211_hw *hw,
+				      struct ieee80211_vif_chanctx_switch *vifs,
+				      int n_vifs,
+				      enum ieee80211_chanctx_switch_mode mode)
+{
+	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+	int ret;
+
+	/* we only support a single-vif right now */
+	if (n_vifs > 1)
+		return -EOPNOTSUPP;
+
+	switch (mode) {
+	case CHANCTX_SWMODE_SWAP_CONTEXTS:
+		ret = iwl_mvm_switch_vif_chanctx_swap(mvm, vifs);
+		break;
+	case CHANCTX_SWMODE_REASSIGN_VIF:
+		ret = iwl_mvm_switch_vif_chanctx_reassign(mvm, vifs);
+		break;
+	default:
+		ret = -EOPNOTSUPP;
+		break;
+	}
+
 	return ret;
 }
 
@@ -3011,27 +3169,134 @@
 }
 #endif
 
-static void iwl_mvm_channel_switch_beacon(struct ieee80211_hw *hw,
-					  struct ieee80211_vif *vif,
-					  struct cfg80211_chan_def *chandef)
+static void iwl_mvm_channel_switch(struct ieee80211_hw *hw,
+				   struct ieee80211_vif *vif,
+				   struct ieee80211_channel_switch *chsw)
+{
+	/* By implementing this operation, we prevent mac80211 from
+	 * starting its own channel switch timer, so that we can call
+	 * ieee80211_chswitch_done() ourselves at the right time
+	 * (which is when the absence time event starts).
+	 */
+
+	IWL_DEBUG_MAC80211(IWL_MAC80211_GET_MVM(hw),
+			   "dummy channel switch op\n");
+}
+
+static int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw,
+				      struct ieee80211_vif *vif,
+				      struct ieee80211_channel_switch *chsw)
 {
 	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
 	struct ieee80211_vif *csa_vif;
+	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+	u32 apply_time;
+	int ret;
 
 	mutex_lock(&mvm->mutex);
 
-	csa_vif = rcu_dereference_protected(mvm->csa_vif,
-					    lockdep_is_held(&mvm->mutex));
-	if (WARN(csa_vif && csa_vif->csa_active,
-		 "Another CSA is already in progress"))
+	IWL_DEBUG_MAC80211(mvm, "pre CSA to freq %d\n",
+			   chsw->chandef.center_freq1);
+
+	switch (vif->type) {
+	case NL80211_IFTYPE_AP:
+		csa_vif =
+			rcu_dereference_protected(mvm->csa_vif,
+						  lockdep_is_held(&mvm->mutex));
+		if (WARN_ONCE(csa_vif && csa_vif->csa_active,
+			      "Another CSA is already in progress")) {
+			ret = -EBUSY;
+			goto out_unlock;
+		}
+
+		rcu_assign_pointer(mvm->csa_vif, vif);
+
+		if (WARN_ONCE(mvmvif->csa_countdown,
+			      "Previous CSA countdown didn't complete")) {
+			ret = -EBUSY;
+			goto out_unlock;
+		}
+
+		break;
+	case NL80211_IFTYPE_STATION:
+		/* Schedule the time event to a bit before beacon 1,
+		 * to make sure we're in the new channel when the
+		 * GO/AP arrives.
+		 */
+		apply_time = chsw->device_timestamp +
+			((vif->bss_conf.beacon_int * (chsw->count - 1) -
+			  IWL_MVM_CHANNEL_SWITCH_TIME_CLIENT) * 1024);
+
+		if (chsw->block_tx)
+			iwl_mvm_csa_client_absent(mvm, vif);
+
+		iwl_mvm_schedule_csa_period(mvm, vif, vif->bss_conf.beacon_int,
+					    apply_time);
+		if (mvmvif->bf_data.bf_enabled) {
+			ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0);
+			if (ret)
+				goto out_unlock;
+		}
+
+		break;
+	default:
+		break;
+	}
+
+	mvmvif->ps_disabled = true;
+
+	ret = iwl_mvm_power_update_ps(mvm);
+	if (ret)
 		goto out_unlock;
 
-	IWL_DEBUG_MAC80211(mvm, "CSA started to freq %d\n",
-			   chandef->center_freq1);
-	rcu_assign_pointer(mvm->csa_vif, vif);
+	/* we won't be on this channel any longer */
+	iwl_mvm_teardown_tdls_peers(mvm);
 
 out_unlock:
 	mutex_unlock(&mvm->mutex);
+
+	return ret;
+}
+
+static int iwl_mvm_post_channel_switch(struct ieee80211_hw *hw,
+				       struct ieee80211_vif *vif)
+{
+	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+	int ret;
+
+	mutex_lock(&mvm->mutex);
+
+	if (vif->type == NL80211_IFTYPE_STATION) {
+		struct iwl_mvm_sta *mvmsta;
+
+		mvmsta = iwl_mvm_sta_from_staid_protected(mvm,
+							  mvmvif->ap_sta_id);
+
+		if (WARN_ON(!mvmsta)) {
+			ret = -EIO;
+			goto out_unlock;
+		}
+
+		iwl_mvm_sta_modify_disable_tx(mvm, mvmsta, false);
+
+		iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
+
+		ret = iwl_mvm_enable_beacon_filter(mvm, vif, 0);
+		if (ret)
+			goto out_unlock;
+
+		iwl_mvm_stop_session_protection(mvm, vif);
+	}
+
+	mvmvif->ps_disabled = false;
+
+	ret = iwl_mvm_power_update_ps(mvm);
+
+out_unlock:
+	mutex_unlock(&mvm->mutex);
+
+	return ret;
 }
 
 static void iwl_mvm_mac_flush(struct ieee80211_hw *hw,
@@ -3040,31 +3305,44 @@
 	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
 	struct iwl_mvm_vif *mvmvif;
 	struct iwl_mvm_sta *mvmsta;
+	struct ieee80211_sta *sta;
+	int i;
+	u32 msk = 0;
 
 	if (!vif || vif->type != NL80211_IFTYPE_STATION)
 		return;
 
 	mutex_lock(&mvm->mutex);
 	mvmvif = iwl_mvm_vif_from_mac80211(vif);
-	mvmsta = iwl_mvm_sta_from_staid_protected(mvm, mvmvif->ap_sta_id);
 
-	if (WARN_ON_ONCE(!mvmsta)) {
-		mutex_unlock(&mvm->mutex);
-		return;
+	/* flush the AP-station and all TDLS peers */
+	for (i = 0; i < IWL_MVM_STATION_COUNT; i++) {
+		sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
+						lockdep_is_held(&mvm->mutex));
+		if (IS_ERR_OR_NULL(sta))
+			continue;
+
+		mvmsta = iwl_mvm_sta_from_mac80211(sta);
+		if (mvmsta->vif != vif)
+			continue;
+
+		/* make sure only TDLS peers or the AP are flushed */
+		WARN_ON(i != mvmvif->ap_sta_id && !sta->tdls);
+
+		msk |= mvmsta->tfd_queue_msk;
 	}
 
 	if (drop) {
-		if (iwl_mvm_flush_tx_path(mvm, mvmsta->tfd_queue_msk, true))
+		if (iwl_mvm_flush_tx_path(mvm, msk, true))
 			IWL_ERR(mvm, "flush request fail\n");
 		mutex_unlock(&mvm->mutex);
 	} else {
-		u32 tfd_queue_msk = mvmsta->tfd_queue_msk;
 		mutex_unlock(&mvm->mutex);
 
 		/* this can take a while, and we may need/want other operations
 		 * to succeed while doing this, so do it without the mutex held
 		 */
-		iwl_trans_wait_tx_queue_empty(mvm->trans, tfd_queue_msk);
+		iwl_trans_wait_tx_queue_empty(mvm->trans, msk);
 	}
 }
 
@@ -3113,7 +3391,13 @@
 
 	.set_tim = iwl_mvm_set_tim,
 
-	.channel_switch_beacon = iwl_mvm_channel_switch_beacon,
+	.channel_switch = iwl_mvm_channel_switch,
+	.pre_channel_switch = iwl_mvm_pre_channel_switch,
+	.post_channel_switch = iwl_mvm_post_channel_switch,
+
+	.tdls_channel_switch = iwl_mvm_tdls_channel_switch,
+	.tdls_cancel_channel_switch = iwl_mvm_tdls_cancel_channel_switch,
+	.tdls_recv_channel_switch = iwl_mvm_tdls_recv_channel_switch,
 
 	CFG80211_TESTMODE_CMD(iwl_mvm_mac_testmode_cmd)
 
diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h
index 256765a..d24660fb 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mvm.h
+++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h
@@ -87,12 +87,18 @@
 /* A TimeUnit is 1024 microsecond */
 #define MSEC_TO_TU(_msec)	(_msec*1000/1024)
 
-/* This value represents the number of TUs before CSA "beacon 0" TBTT
- * when the CSA time-event needs to be scheduled to start.  It must be
- * big enough to ensure that we switch in time.
+/* For GO, this value represents the number of TUs before CSA "beacon
+ * 0" TBTT when the CSA time-event needs to be scheduled to start.  It
+ * must be big enough to ensure that we switch in time.
  */
 #define IWL_MVM_CHANNEL_SWITCH_TIME_GO		40
 
+/* For client, this value represents the number of TUs before CSA
+ * "beacon 1" TBTT, instead.  This is because we don't know when the
+ * GO/AP will be in the new channel, so we switch early enough.
+ */
+#define IWL_MVM_CHANNEL_SWITCH_TIME_CLIENT	10
+
 /*
  * This value (in TUs) is used to fine tune the CSA NoA end time which should
  * be just before "beacon 0" TBTT.
@@ -269,6 +275,7 @@
 	IWL_MVM_REF_NMI,
 	IWL_MVM_REF_TM_CMD,
 	IWL_MVM_REF_EXIT_WORK,
+	IWL_MVM_REF_PROTECT_CSA,
 
 	/* update debugfs.c when changing this */
 
@@ -288,7 +295,6 @@
 * struct iwl_mvm_vif_bf_data - beacon filtering related data
 * @bf_enabled: indicates if beacon filtering is enabled
 * @ba_enabled: indicated if beacon abort is enabled
-* @last_beacon_signal: last beacon rssi signal in dbm
 * @ave_beacon_signal: average beacon signal
 * @last_cqm_event: rssi of the last cqm event
 * @bt_coex_min_thold: minimum threshold for BT coex
@@ -399,6 +405,9 @@
 
 	/* FW identified misbehaving AP */
 	u8 uapsd_misbehaving_bssid[ETH_ALEN];
+
+	/* Indicates that CSA countdown may be started */
+	bool csa_countdown;
 };
 
 static inline struct iwl_mvm_vif *
@@ -519,6 +528,13 @@
 #define IWL_MVM_DEBUG_SET_TEMPERATURE_MIN -100
 #define IWL_MVM_DEBUG_SET_TEMPERATURE_MAX 200
 
+enum iwl_mvm_tdls_cs_state {
+	IWL_MVM_TDLS_SW_IDLE = 0,
+	IWL_MVM_TDLS_SW_REQ_SENT,
+	IWL_MVM_TDLS_SW_REQ_RCVD,
+	IWL_MVM_TDLS_SW_ACTIVE,
+};
+
 struct iwl_mvm {
 	/* for logger access */
 	struct device *dev;
@@ -548,6 +564,7 @@
 	enum iwl_ucode_type cur_ucode;
 	bool ucode_loaded;
 	bool init_ucode_complete;
+	bool calibrating;
 	u32 error_event_table;
 	u32 log_event_table;
 	u32 umac_error_event_table;
@@ -577,6 +594,7 @@
 	struct work_struct sta_drained_wk;
 	unsigned long sta_drained[BITS_TO_LONGS(IWL_MVM_STATION_COUNT)];
 	atomic_t pending_frames[IWL_MVM_STATION_COUNT];
+	u32 tfd_drained[IWL_MVM_STATION_COUNT];
 	u8 rx_ba_sessions;
 
 	/* configured by mac80211 */
@@ -587,6 +605,10 @@
 	void *scan_cmd;
 	struct iwl_mcast_filter_cmd *mcast_filter_cmd;
 
+	/* UMAC scan tracking */
+	u32 scan_uid[IWL_MVM_MAX_SIMULTANEOUS_SCANS];
+	u8 scan_seq_num, sched_scan_seq_num;
+
 	/* rx chain antennas set through debugfs for the scan command */
 	u8 scan_rx_ant;
 
@@ -648,6 +670,7 @@
 	/* -1 for always, 0 for never, >0 for that many times */
 	s8 restart_fw;
 	struct work_struct fw_error_dump_wk;
+	enum iwl_fw_dbg_conf fw_dbg_conf;
 
 #ifdef CONFIG_IWLWIFI_LEDS
 	struct led_classdev led;
@@ -661,7 +684,12 @@
 
 	/* sched scan settings for net detect */
 	struct cfg80211_sched_scan_request *nd_config;
-	struct ieee80211_scan_ies *nd_ies;
+	struct ieee80211_scan_ies nd_ies;
+	struct cfg80211_match_set *nd_match_sets;
+	int n_nd_match_sets;
+	struct ieee80211_channel **nd_channels;
+	int n_nd_channels;
+	bool net_detect;
 #ifdef CONFIG_IWLWIFI_DEBUGFS
 	u32 d3_wake_sysassert; /* must be u32 for debugfs_create_bool */
 	bool d3_test_active;
@@ -734,6 +762,28 @@
 	u32 ap_last_beacon_gp2;
 
 	u8 low_latency_agg_frame_limit;
+
+	/* TDLS channel switch data */
+	struct {
+		struct delayed_work dwork;
+		enum iwl_mvm_tdls_cs_state state;
+
+		/*
+		 * Current cs sta - might be different from periodic cs peer
+		 * station. Value is meaningless when the cs-state is idle.
+		 */
+		u8 cur_sta_id;
+
+		/* TDLS periodic channel-switch peer */
+		struct {
+			u8 sta_id;
+			u8 op_class;
+			bool initiator; /* are we the link initiator */
+			struct cfg80211_chan_def chandef;
+			struct sk_buff *skb; /* ch sw template */
+			u32 ch_sw_tm_ie;
+		} peer;
+	} tdls_cs;
 };
 
 /* Extract MVM priv from op_mode and _hw */
@@ -750,6 +800,7 @@
 	IWL_MVM_STATUS_IN_HW_RESTART,
 	IWL_MVM_STATUS_IN_D0I3,
 	IWL_MVM_STATUS_ROC_AUX_RUNNING,
+	IWL_MVM_STATUS_D3_RECONFIG,
 };
 
 static inline bool iwl_mvm_is_radio_killed(struct iwl_mvm *mvm)
@@ -758,6 +809,26 @@
 	       test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status);
 }
 
+/* Must be called with rcu_read_lock() held and it can only be
+ * released when mvmsta is not needed anymore.
+ */
+static inline struct iwl_mvm_sta *
+iwl_mvm_sta_from_staid_rcu(struct iwl_mvm *mvm, u8 sta_id)
+{
+	struct ieee80211_sta *sta;
+
+	if (sta_id >= ARRAY_SIZE(mvm->fw_id_to_mac_id))
+		return NULL;
+
+	sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
+
+	/* This can happen if the station has been removed right now */
+	if (IS_ERR_OR_NULL(sta))
+		return NULL;
+
+	return iwl_mvm_sta_from_mac80211(sta);
+}
+
 static inline struct iwl_mvm_sta *
 iwl_mvm_sta_from_staid_protected(struct iwl_mvm *mvm, u8 sta_id)
 {
@@ -831,6 +902,16 @@
 int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb,
 		   struct ieee80211_sta *sta);
 int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb);
+void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb,
+			struct iwl_tx_cmd *tx_cmd,
+			struct ieee80211_tx_info *info, u8 sta_id);
+void iwl_mvm_set_tx_cmd_crypto(struct iwl_mvm *mvm,
+			       struct ieee80211_tx_info *info,
+			       struct iwl_tx_cmd *tx_cmd,
+			       struct sk_buff *skb_frag);
+void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm, struct iwl_tx_cmd *tx_cmd,
+			    struct ieee80211_tx_info *info,
+			    struct ieee80211_sta *sta, __le16 fc);
 #ifdef CONFIG_IWLWIFI_DEBUG
 const char *iwl_mvm_get_tx_fail_reason(u32 status);
 #else
@@ -887,6 +968,8 @@
 				struct iwl_device_cmd *cmd);
 int iwl_mvm_rx_radio_ver(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
 			 struct iwl_device_cmd *cmd);
+int iwl_mvm_rx_mfuart_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+			    struct iwl_device_cmd *cmd);
 
 /* MVM PHY */
 int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
@@ -900,6 +983,8 @@
 void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm,
 			    struct iwl_mvm_phy_ctxt *ctxt);
 int iwl_mvm_phy_ctx_count(struct iwl_mvm *mvm);
+u8 iwl_mvm_get_channel_width(struct cfg80211_chan_def *chandef);
+u8 iwl_mvm_get_ctrl_pos(struct cfg80211_chan_def *chandef);
 
 /* MAC (virtual interface) programming */
 int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
@@ -919,6 +1004,8 @@
 				    struct iwl_device_cmd *cmd);
 void iwl_mvm_mac_ctxt_recalc_tsf_id(struct iwl_mvm *mvm,
 				    struct ieee80211_vif *vif);
+unsigned long iwl_mvm_get_used_hw_queues(struct iwl_mvm *mvm,
+					 struct ieee80211_vif *exclude_vif);
 
 /* Bindings */
 int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
@@ -929,6 +1016,7 @@
 			  struct ieee80211_vif *disabled_vif);
 
 /* Scanning */
+int iwl_mvm_scan_size(struct iwl_mvm *mvm);
 int iwl_mvm_scan_request(struct iwl_mvm *mvm,
 			 struct ieee80211_vif *vif,
 			 struct cfg80211_scan_request *req);
@@ -969,6 +1057,17 @@
 				    struct cfg80211_sched_scan_request *req,
 				    struct ieee80211_scan_ies *ies);
 
+/* UMAC scan */
+int iwl_mvm_config_scan(struct iwl_mvm *mvm);
+int iwl_mvm_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+		      struct ieee80211_scan_request *req);
+int iwl_mvm_sched_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+			    struct cfg80211_sched_scan_request *req,
+			    struct ieee80211_scan_ies *ies);
+int iwl_mvm_rx_umac_scan_complete_notif(struct iwl_mvm *mvm,
+					struct iwl_rx_cmd_buffer *rxb,
+					struct iwl_device_cmd *cmd);
+
 /* MVM debugfs */
 #ifdef CONFIG_IWLWIFI_DEBUGFS
 int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir);
@@ -1048,7 +1147,7 @@
 }
 #endif
 void iwl_mvm_set_wowlan_qos_seq(struct iwl_mvm_sta *mvm_ap_sta,
-				struct iwl_wowlan_config_cmd_v2 *cmd);
+				struct iwl_wowlan_config_cmd *cmd);
 int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
 			       struct ieee80211_vif *vif,
 			       bool disable_offloading,
@@ -1058,6 +1157,7 @@
 void iwl_mvm_ref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type);
 void iwl_mvm_unref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type);
 int iwl_mvm_ref_sync(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type);
+bool iwl_mvm_ref_taken(struct iwl_mvm *mvm);
 void iwl_mvm_d0i3_enable_tx(struct iwl_mvm *mvm, __le16 *qos_seq);
 int _iwl_mvm_exit_d0i3(struct iwl_mvm *mvm);
 
@@ -1073,12 +1173,14 @@
 				struct ieee80211_sta *sta);
 bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm,
 				     struct ieee80211_sta *sta);
+bool iwl_mvm_bt_coex_is_ant_avail(struct iwl_mvm *mvm, u8 ant);
 bool iwl_mvm_bt_coex_is_shared_ant_avail(struct iwl_mvm *mvm);
 bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm,
 				    enum ieee80211_band band);
 u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr,
 			   struct ieee80211_tx_info *info, u8 ac);
 
+bool iwl_mvm_bt_coex_is_ant_avail_old(struct iwl_mvm *mvm, u8 ant);
 bool iwl_mvm_bt_coex_is_shared_ant_avail_old(struct iwl_mvm *mvm);
 void iwl_mvm_bt_coex_vif_change_old(struct iwl_mvm *mvm);
 int iwl_send_bt_init_conf_old(struct iwl_mvm *mvm);
@@ -1194,6 +1296,10 @@
 
 /* Thermal management and CT-kill */
 void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff);
+void iwl_mvm_tt_temp_changed(struct iwl_mvm *mvm, u32 temp);
+int iwl_mvm_temp_notif(struct iwl_mvm *mvm,
+		       struct iwl_rx_cmd_buffer *rxb,
+		       struct iwl_device_cmd *cmd);
 void iwl_mvm_tt_handler(struct iwl_mvm *mvm);
 void iwl_mvm_tt_initialize(struct iwl_mvm *mvm, u32 min_backoff);
 void iwl_mvm_tt_exit(struct iwl_mvm *mvm);
@@ -1205,12 +1311,33 @@
 		      bool added_vif);
 
 /* TDLS */
+
+/*
+ * We use TID 4 (VI) as a FW-used-only TID when TDLS connections are present.
+ * This TID is marked as used vs the AP and all connected TDLS peers.
+ */
+#define IWL_MVM_TDLS_FW_TID 4
+
 int iwl_mvm_tdls_sta_count(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
 void iwl_mvm_teardown_tdls_peers(struct iwl_mvm *mvm);
 void iwl_mvm_recalc_tdls_state(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
 			       bool sta_added);
 void iwl_mvm_mac_mgd_protect_tdls_discover(struct ieee80211_hw *hw,
 					   struct ieee80211_vif *vif);
+int iwl_mvm_tdls_channel_switch(struct ieee80211_hw *hw,
+				struct ieee80211_vif *vif,
+				struct ieee80211_sta *sta, u8 oper_class,
+				struct cfg80211_chan_def *chandef,
+				struct sk_buff *tmpl_skb, u32 ch_sw_tm_ie);
+void iwl_mvm_tdls_recv_channel_switch(struct ieee80211_hw *hw,
+				      struct ieee80211_vif *vif,
+				      struct ieee80211_tdls_ch_sw_params *params);
+void iwl_mvm_tdls_cancel_channel_switch(struct ieee80211_hw *hw,
+					struct ieee80211_vif *vif,
+					struct ieee80211_sta *sta);
+int iwl_mvm_rx_tdls_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+			  struct iwl_device_cmd *cmd);
+void iwl_mvm_tdls_ch_switch_work(struct work_struct *work);
 
 struct ieee80211_vif *iwl_mvm_get_bss_vif(struct iwl_mvm *mvm);
 
diff --git a/drivers/net/wireless/iwlwifi/mvm/nvm.c b/drivers/net/wireless/iwlwifi/mvm/nvm.c
index af07456..d55fd8e 100644
--- a/drivers/net/wireless/iwlwifi/mvm/nvm.c
+++ b/drivers/net/wireless/iwlwifi/mvm/nvm.c
@@ -339,11 +339,15 @@
 	} *file_sec;
 	const u8 *eof, *temp;
 	int max_section_size;
+	const __le32 *dword_buff;
 
 #define NVM_WORD1_LEN(x) (8 * (x & 0x03FF))
 #define NVM_WORD2_ID(x) (x >> 12)
 #define NVM_WORD2_LEN_FAMILY_8000(x) (2 * ((x & 0xFF) << 8 | x >> 8))
 #define NVM_WORD1_ID_FAMILY_8000(x) (x >> 4)
+#define NVM_HEADER_0	(0x2A504C54)
+#define NVM_HEADER_1	(0x4E564D2A)
+#define NVM_HEADER_SIZE	(4 * sizeof(u32))
 
 	IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from external NVM\n");
 
@@ -372,12 +376,6 @@
 	IWL_INFO(mvm, "Loaded NVM file %s (%zu bytes)\n",
 		 mvm->nvm_file_name, fw_entry->size);
 
-	if (fw_entry->size < sizeof(*file_sec)) {
-		IWL_ERR(mvm, "NVM file too small\n");
-		ret = -EINVAL;
-		goto out;
-	}
-
 	if (fw_entry->size > MAX_NVM_FILE_LEN) {
 		IWL_ERR(mvm, "NVM file too large\n");
 		ret = -EINVAL;
@@ -385,8 +383,25 @@
 	}
 
 	eof = fw_entry->data + fw_entry->size;
+	dword_buff = (__le32 *)fw_entry->data;
 
-	file_sec = (void *)fw_entry->data;
+	/* some NVM file will contain a header.
+	 * The header is identified by 2 dwords header as follow:
+	 * dword[0] = 0x2A504C54
+	 * dword[1] = 0x4E564D2A
+	 *
+	 * This header must be skipped when providing the NVM data to the FW.
+	 */
+	if (fw_entry->size > NVM_HEADER_SIZE &&
+	    dword_buff[0] == cpu_to_le32(NVM_HEADER_0) &&
+	    dword_buff[1] == cpu_to_le32(NVM_HEADER_1)) {
+		file_sec = (void *)(fw_entry->data + NVM_HEADER_SIZE);
+		IWL_INFO(mvm, "NVM Version %08X\n", le32_to_cpu(dword_buff[2]));
+		IWL_INFO(mvm, "NVM Manufacturing date %08X\n",
+			 le32_to_cpu(dword_buff[3]));
+	} else {
+		file_sec = (void *)fw_entry->data;
+	}
 
 	while (true) {
 		if (file_sec->data > eof) {
diff --git a/drivers/net/wireless/iwlwifi/mvm/offloading.c b/drivers/net/wireless/iwlwifi/mvm/offloading.c
index adcbf4c..68b0169 100644
--- a/drivers/net/wireless/iwlwifi/mvm/offloading.c
+++ b/drivers/net/wireless/iwlwifi/mvm/offloading.c
@@ -67,7 +67,7 @@
 #include "mvm.h"
 
 void iwl_mvm_set_wowlan_qos_seq(struct iwl_mvm_sta *mvm_ap_sta,
-				struct iwl_wowlan_config_cmd_v2 *cmd)
+				struct iwl_wowlan_config_cmd *cmd)
 {
 	int i;
 
diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c
index bd52ecfa..97dfba5 100644
--- a/drivers/net/wireless/iwlwifi/mvm/ops.c
+++ b/drivers/net/wireless/iwlwifi/mvm/ops.c
@@ -244,6 +244,8 @@
 		   iwl_mvm_rx_scan_offload_complete_notif, true),
 	RX_HANDLER(MATCH_FOUND_NOTIFICATION, iwl_mvm_rx_scan_offload_results,
 		   false),
+	RX_HANDLER(SCAN_COMPLETE_UMAC, iwl_mvm_rx_umac_scan_complete_notif,
+		   true),
 
 	RX_HANDLER(RADIO_VERSION_NOTIFICATION, iwl_mvm_rx_radio_ver, false),
 	RX_HANDLER(CARD_STATE_NOTIFICATION, iwl_mvm_rx_card_state_notif, false),
@@ -254,6 +256,12 @@
 	RX_HANDLER(REPLY_ERROR, iwl_mvm_rx_fw_error, false),
 	RX_HANDLER(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION,
 		   iwl_mvm_power_uapsd_misbehaving_ap_notif, false),
+	RX_HANDLER(DTS_MEASUREMENT_NOTIFICATION, iwl_mvm_temp_notif, true),
+
+	RX_HANDLER(TDLS_CHANNEL_SWITCH_NOTIFICATION, iwl_mvm_rx_tdls_notif,
+		   true),
+	RX_HANDLER(MFUART_LOAD_NOTIFICATION, iwl_mvm_rx_mfuart_notif, false),
+
 };
 #undef RX_HANDLER
 #define CMD(x) [x] = #x
@@ -317,11 +325,9 @@
 	CMD(WOWLAN_KEK_KCK_MATERIAL),
 	CMD(WOWLAN_GET_STATUSES),
 	CMD(WOWLAN_TX_POWER_PER_DB),
-	CMD(NET_DETECT_CONFIG_CMD),
-	CMD(NET_DETECT_PROFILES_QUERY_CMD),
-	CMD(NET_DETECT_PROFILES_CMD),
-	CMD(NET_DETECT_HOTSPOTS_CMD),
-	CMD(NET_DETECT_HOTSPOTS_QUERY_CMD),
+	CMD(SCAN_OFFLOAD_PROFILES_QUERY_CMD),
+	CMD(SCAN_OFFLOAD_HOTSPOTS_CONFIG_CMD),
+	CMD(SCAN_OFFLOAD_HOTSPOTS_QUERY_CMD),
 	CMD(CARD_STATE_NOTIFICATION),
 	CMD(MISSED_BEACONS_NOTIFICATION),
 	CMD(BT_COEX_PRIO_TABLE),
@@ -344,6 +350,13 @@
 	CMD(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION),
 	CMD(ANTENNA_COUPLING_NOTIFICATION),
 	CMD(SCD_QUEUE_CFG),
+	CMD(SCAN_CFG_CMD),
+	CMD(SCAN_REQ_UMAC),
+	CMD(SCAN_ABORT_UMAC),
+	CMD(SCAN_COMPLETE_UMAC),
+	CMD(TDLS_CHANNEL_SWITCH_CMD),
+	CMD(TDLS_CHANNEL_SWITCH_NOTIFICATION),
+	CMD(TDLS_CONFIG_CMD),
 };
 #undef CMD
 
@@ -427,6 +440,7 @@
 	}
 	mvm->sf_state = SF_UNINIT;
 	mvm->low_latency_agg_frame_limit = 6;
+	mvm->cur_ucode = IWL_UCODE_INIT;
 
 	mutex_init(&mvm->mutex);
 	mutex_init(&mvm->d0i3_suspend_mutex);
@@ -441,6 +455,7 @@
 	INIT_WORK(&mvm->sta_drained_wk, iwl_mvm_sta_drained_wk);
 	INIT_WORK(&mvm->d0i3_exit_work, iwl_mvm_d0i3_exit_work);
 	INIT_WORK(&mvm->fw_error_dump_wk, iwl_mvm_fw_error_dump_wk);
+	INIT_DELAYED_WORK(&mvm->tdls_cs.dwork, iwl_mvm_tdls_ch_switch_work);
 
 	spin_lock_init(&mvm->d0i3_tx_lock);
 	spin_lock_init(&mvm->refs_lock);
@@ -481,6 +496,10 @@
 
 	trans->rx_mpdu_cmd = REPLY_RX_MPDU_CMD;
 	trans->rx_mpdu_cmd_hdr_size = sizeof(struct iwl_rx_mpdu_res_start);
+	trans->dbg_dest_tlv = mvm->fw->dbg_dest_tlv;
+	trans->dbg_dest_reg_num = mvm->fw->dbg_dest_reg_num;
+	memcpy(trans->dbg_conf_tlv, mvm->fw->dbg_conf_tlv,
+	       sizeof(trans->dbg_conf_tlv));
 
 	/* set up notification wait support */
 	iwl_notification_wait_init(&mvm->notif_wait);
@@ -524,7 +543,8 @@
 
 		mutex_lock(&mvm->mutex);
 		err = iwl_run_init_mvm_ucode(mvm, true);
-		iwl_trans_stop_device(trans);
+		if (!err || !iwlmvm_mod_params.init_dbg)
+			iwl_trans_stop_device(trans);
 		mutex_unlock(&mvm->mutex);
 		/* returns 0 if successful, 1 if success but in rfkill */
 		if (err < 0 && !iwlmvm_mod_params.init_dbg) {
@@ -533,16 +553,7 @@
 		}
 	}
 
-	if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
-		scan_size = sizeof(struct iwl_scan_req_unified_lmac) +
-			sizeof(struct iwl_scan_channel_cfg_lmac) *
-				mvm->fw->ucode_capa.n_scan_channels +
-			sizeof(struct iwl_scan_probe_req);
-	else
-		scan_size = sizeof(struct iwl_scan_cmd) +
-			mvm->fw->ucode_capa.max_probe_length +
-			mvm->fw->ucode_capa.n_scan_channels *
-				sizeof(struct iwl_scan_channel);
+	scan_size = iwl_mvm_scan_size(mvm);
 
 	mvm->scan_cmd = kmalloc(scan_size, GFP_KERNEL);
 	if (!mvm->scan_cmd)
@@ -596,8 +607,6 @@
 		kfree(mvm->nd_config->match_sets);
 		kfree(mvm->nd_config);
 		mvm->nd_config = NULL;
-		kfree(mvm->nd_ies);
-		mvm->nd_ies = NULL;
 	}
 #endif
 
@@ -757,6 +766,7 @@
 static bool iwl_mvm_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state)
 {
 	struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+	bool calibrating = ACCESS_ONCE(mvm->calibrating);
 
 	if (state)
 		set_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status);
@@ -765,7 +775,15 @@
 
 	wiphy_rfkill_set_hw_state(mvm->hw->wiphy, iwl_mvm_is_radio_killed(mvm));
 
-	return state && mvm->cur_ucode != IWL_UCODE_INIT;
+	/* iwl_run_init_mvm_ucode is waiting for results, abort it */
+	if (calibrating)
+		iwl_abort_notification_waits(&mvm->notif_wait);
+
+	/*
+	 * Stop the device if we run OPERATIONAL firmware or if we are in the
+	 * middle of the calibrations.
+	 */
+	return state && (mvm->cur_ucode != IWL_UCODE_INIT || calibrating);
 }
 
 static void iwl_mvm_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb)
@@ -986,7 +1004,7 @@
 }
 
 static void iwl_mvm_set_wowlan_data(struct iwl_mvm *mvm,
-				    struct iwl_wowlan_config_cmd_v3 *cmd,
+				    struct iwl_wowlan_config_cmd *cmd,
 				    struct iwl_d0i3_iter_data *iter_data)
 {
 	struct ieee80211_sta *ap_sta;
@@ -1002,14 +1020,14 @@
 		goto out;
 
 	mvm_ap_sta = iwl_mvm_sta_from_mac80211(ap_sta);
-	cmd->common.is_11n_connection = ap_sta->ht_cap.ht_supported;
+	cmd->is_11n_connection = ap_sta->ht_cap.ht_supported;
 	cmd->offloading_tid = iter_data->offloading_tid;
 
 	/*
 	 * The d0i3 uCode takes care of the nonqos counters,
 	 * so configure only the qos seq ones.
 	 */
-	iwl_mvm_set_wowlan_qos_seq(mvm_ap_sta, &cmd->common);
+	iwl_mvm_set_wowlan_qos_seq(mvm_ap_sta, cmd);
 out:
 	rcu_read_unlock();
 }
@@ -1021,14 +1039,11 @@
 	struct iwl_d0i3_iter_data d0i3_iter_data = {
 		.mvm = mvm,
 	};
-	struct iwl_wowlan_config_cmd_v3 wowlan_config_cmd = {
-		.common = {
-			.wakeup_filter =
-				cpu_to_le32(IWL_WOWLAN_WAKEUP_RX_FRAME |
-					    IWL_WOWLAN_WAKEUP_BEACON_MISS |
-					    IWL_WOWLAN_WAKEUP_LINK_CHANGE |
-					    IWL_WOWLAN_WAKEUP_BCN_FILTERING),
-		},
+	struct iwl_wowlan_config_cmd wowlan_config_cmd = {
+		.wakeup_filter = cpu_to_le32(IWL_WOWLAN_WAKEUP_RX_FRAME |
+					     IWL_WOWLAN_WAKEUP_BEACON_MISS |
+					     IWL_WOWLAN_WAKEUP_LINK_CHANGE |
+					     IWL_WOWLAN_WAKEUP_BCN_FILTERING),
 	};
 	struct iwl_d3_manager_config d3_cfg_cmd = {
 		.min_sleep_time = cpu_to_le32(1000),
@@ -1040,6 +1055,19 @@
 	set_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status);
 	synchronize_net();
 
+	/*
+	 * iwl_mvm_ref_sync takes a reference before checking the flag.
+	 * so by checking there is no held reference we prevent a state
+	 * in which iwl_mvm_ref_sync continues successfully while we
+	 * configure the firmware to enter d0i3
+	 */
+	if (iwl_mvm_ref_taken(mvm)) {
+		IWL_DEBUG_RPM(mvm->trans, "abort d0i3 due to taken ref\n");
+		clear_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status);
+		wake_up(&mvm->d0i3_exit_waitq);
+		return 1;
+	}
+
 	ieee80211_iterate_active_interfaces_atomic(mvm->hw,
 						   IEEE80211_IFACE_ITER_NORMAL,
 						   iwl_mvm_enter_d0i3_iterator,
diff --git a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c
index 12283b5..1c0d4a4 100644
--- a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c
+++ b/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c
@@ -68,7 +68,7 @@
 #include "mvm.h"
 
 /* Maps the driver specific channel width definition to the the fw values */
-static inline u8 iwl_mvm_get_channel_width(struct cfg80211_chan_def *chandef)
+u8 iwl_mvm_get_channel_width(struct cfg80211_chan_def *chandef)
 {
 	switch (chandef->width) {
 	case NL80211_CHAN_WIDTH_20_NOHT:
@@ -90,7 +90,7 @@
  * Maps the driver specific control channel position (relative to the center
  * freq) definitions to the the fw values
  */
-static inline u8 iwl_mvm_get_ctrl_pos(struct cfg80211_chan_def *chandef)
+u8 iwl_mvm_get_ctrl_pos(struct cfg80211_chan_def *chandef)
 {
 	switch (chandef->chan->center_freq - chandef->center_freq1) {
 	case -70:
diff --git a/drivers/net/wireless/iwlwifi/mvm/power.c b/drivers/net/wireless/iwlwifi/mvm/power.c
index 5b85b0c..2620dd0 100644
--- a/drivers/net/wireless/iwlwifi/mvm/power.c
+++ b/drivers/net/wireless/iwlwifi/mvm/power.c
@@ -286,6 +286,27 @@
 	return true;
 }
 
+static int iwl_mvm_power_get_skip_over_dtim(int dtimper, int bi)
+{
+	int numerator;
+	int dtim_interval = dtimper * bi;
+
+	if (WARN_ON(!dtim_interval))
+		return 0;
+
+	if (dtimper == 1) {
+		if (bi > 100)
+			numerator = 408;
+		else
+			numerator = 510;
+	} else if (dtimper < 10) {
+		numerator = 612;
+	} else {
+		return 0;
+	}
+	return max(1, (numerator / dtim_interval));
+}
+
 static bool iwl_mvm_power_is_radar(struct ieee80211_vif *vif)
 {
 	struct ieee80211_chanctx_conf *chanctx_conf;
@@ -308,7 +329,7 @@
 				    struct ieee80211_vif *vif,
 				    struct iwl_mac_power_cmd *cmd)
 {
-	int dtimper, dtimper_msec;
+	int dtimper, bi;
 	int keep_alive;
 	bool radar_detect = false;
 	struct iwl_mvm_vif *mvmvif __maybe_unused =
@@ -317,6 +338,7 @@
 	cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
 							    mvmvif->color));
 	dtimper = vif->bss_conf.dtim_period;
+	bi = vif->bss_conf.beacon_int;
 
 	/*
 	 * Regardless of power management state the driver must set
@@ -324,10 +346,9 @@
 	 * immediately after association. Check that keep alive period
 	 * is at least 3 * DTIM
 	 */
-	dtimper_msec = dtimper * vif->bss_conf.beacon_int;
-	keep_alive = max_t(int, 3 * dtimper_msec,
-			   MSEC_PER_SEC * POWER_KEEP_ALIVE_PERIOD_SEC);
-	keep_alive = DIV_ROUND_UP(keep_alive, MSEC_PER_SEC);
+	keep_alive = DIV_ROUND_UP(ieee80211_tu_to_usec(3 * dtimper * bi),
+				  USEC_PER_SEC);
+	keep_alive = max(keep_alive, POWER_KEEP_ALIVE_PERIOD_SEC);
 	cmd->keep_alive_seconds = cpu_to_le16(keep_alive);
 
 	if (mvm->ps_disabled)
@@ -352,11 +373,14 @@
 	radar_detect = iwl_mvm_power_is_radar(vif);
 
 	/* Check skip over DTIM conditions */
-	if (!radar_detect && (dtimper <= 10) &&
+	if (!radar_detect && (dtimper < 10) &&
 	    (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_LP ||
 	     mvm->cur_ucode == IWL_UCODE_WOWLAN)) {
-		cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
-		cmd->skip_dtim_periods = 3;
+		cmd->skip_dtim_periods =
+			iwl_mvm_power_get_skip_over_dtim(dtimper, bi);
+		if (cmd->skip_dtim_periods)
+			cmd->flags |=
+				cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
 	}
 
 	if (mvm->cur_ucode != IWL_UCODE_WOWLAN) {
diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c
index ce88484..30ceb67 100644
--- a/drivers/net/wireless/iwlwifi/mvm/rs.c
+++ b/drivers/net/wireless/iwlwifi/mvm/rs.c
@@ -158,6 +158,12 @@
 	allow_column_func_t checks[MAX_COLUMN_CHECKS];
 };
 
+static bool rs_ant_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
+			 struct iwl_scale_tbl_info *tbl)
+{
+	return iwl_mvm_bt_coex_is_ant_avail(mvm, tbl->rate.ant);
+}
+
 static bool rs_mimo_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
 			  struct iwl_scale_tbl_info *tbl)
 {
@@ -218,6 +224,9 @@
 			RS_COLUMN_INVALID,
 			RS_COLUMN_INVALID,
 		},
+		.checks = {
+			rs_ant_allow,
+		},
 	},
 	[RS_COLUMN_LEGACY_ANT_B] = {
 		.mode = RS_LEGACY,
@@ -231,6 +240,9 @@
 			RS_COLUMN_INVALID,
 			RS_COLUMN_INVALID,
 		},
+		.checks = {
+			rs_ant_allow,
+		},
 	},
 	[RS_COLUMN_SISO_ANT_A] = {
 		.mode = RS_SISO,
@@ -246,6 +258,7 @@
 		},
 		.checks = {
 			rs_siso_allow,
+			rs_ant_allow,
 		},
 	},
 	[RS_COLUMN_SISO_ANT_B] = {
@@ -262,6 +275,7 @@
 		},
 		.checks = {
 			rs_siso_allow,
+			rs_ant_allow,
 		},
 	},
 	[RS_COLUMN_SISO_ANT_A_SGI] = {
@@ -279,6 +293,7 @@
 		},
 		.checks = {
 			rs_siso_allow,
+			rs_ant_allow,
 			rs_sgi_allow,
 		},
 	},
@@ -297,6 +312,7 @@
 		},
 		.checks = {
 			rs_siso_allow,
+			rs_ant_allow,
 			rs_sgi_allow,
 		},
 	},
@@ -506,7 +522,7 @@
 				const char *prefix)
 {
 	IWL_DEBUG_RATE(mvm,
-		       "%s: (%s: %d) ANT: %s BW: %d SGI: %d LDPC: %d STBC %d\n",
+		       "%s: (%s: %d) ANT: %s BW: %d SGI: %d LDPC: %d STBC: %d\n",
 		       prefix, rs_pretty_lq_type(rate->type),
 		       rate->index, rs_pretty_ant(rate->ant),
 		       rate->bw, rate->sgi, rate->ldpc, rate->stbc);
@@ -816,7 +832,7 @@
 
 		if (nss == 1) {
 			rate->type = LQ_VHT_SISO;
-			WARN_ON_ONCE(num_of_ant != 1);
+			WARN_ON_ONCE(!rate->stbc && num_of_ant != 1);
 		} else if (nss == 2) {
 			rate->type = LQ_VHT_MIMO2;
 			WARN_ON_ONCE(num_of_ant != 2);
@@ -1110,10 +1126,11 @@
 
 	if (time_after(jiffies,
 		       (unsigned long)(lq_sta->last_tx + RS_IDLE_TIMEOUT))) {
-		int tid;
+		int t;
+
 		IWL_DEBUG_RATE(mvm, "Tx idle for too long. reinit rs\n");
-		for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++)
-			ieee80211_stop_tx_ba_session(sta, tid);
+		for (t = 0; t < IWL_MAX_TID_COUNT; t++)
+			ieee80211_stop_tx_ba_session(sta, t);
 
 		iwl_mvm_rs_rate_init(mvm, sta, info->band, false);
 		return;
@@ -1154,16 +1171,15 @@
 		/* Rate did match, so reset the missed_rate_counter */
 		lq_sta->missed_rate_counter = 0;
 
-	/* Figure out if rate scale algorithm is in active or search table */
-	if (rs_rate_match(&rate,
-			  &(lq_sta->lq_info[lq_sta->active_tbl].rate))) {
+	if (!lq_sta->search_better_tbl) {
 		curr_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
 		other_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]);
-	} else if (rs_rate_match(&rate,
-			 &lq_sta->lq_info[1 - lq_sta->active_tbl].rate)) {
+	} else {
 		curr_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]);
 		other_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
-	} else {
+	}
+
+	if (WARN_ON_ONCE(!rs_rate_match(&rate, &curr_tbl->rate))) {
 		IWL_DEBUG_RATE(mvm,
 			       "Neither active nor search matches tx rate\n");
 		tmp_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
@@ -1188,6 +1204,13 @@
 	 * first index into rate scale table.
 	 */
 	if (info->flags & IEEE80211_TX_STAT_AMPDU) {
+		/* ampdu_ack_len = 0 marks no BA was received. In this case
+		 * treat it as a single frame loss as we don't want the success
+		 * ratio to dip too quickly because a BA wasn't received
+		 */
+		if (info->status.ampdu_ack_len == 0)
+			info->status.ampdu_len = 1;
+
 		ucode_rate = le32_to_cpu(table->rs_table[0]);
 		rs_rate_from_ucode_rate(ucode_rate, info->band, &rate);
 		rs_collect_tx_data(lq_sta, curr_tbl, rate.index,
diff --git a/drivers/net/wireless/iwlwifi/mvm/rx.c b/drivers/net/wireless/iwlwifi/mvm/rx.c
index 3cf40f3..94b6e72 100644
--- a/drivers/net/wireless/iwlwifi/mvm/rx.c
+++ b/drivers/net/wireless/iwlwifi/mvm/rx.c
@@ -96,27 +96,27 @@
  * Adds the rxb to a new skb and give it to mac80211
  */
 static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm,
+					    struct sk_buff *skb,
 					    struct ieee80211_hdr *hdr, u16 len,
-					    u32 ampdu_status,
-					    struct iwl_rx_cmd_buffer *rxb,
-					    struct ieee80211_rx_status *stats)
+					    u32 ampdu_status, u8 crypt_len,
+					    struct iwl_rx_cmd_buffer *rxb)
 {
-	struct sk_buff *skb;
 	unsigned int hdrlen, fraglen;
 
-	/* Dont use dev_alloc_skb(), we'll have enough headroom once
-	 * ieee80211_hdr pulled.
-	 */
-	skb = alloc_skb(128, GFP_ATOMIC);
-	if (!skb) {
-		IWL_ERR(mvm, "alloc_skb failed\n");
-		return;
-	}
 	/* If frame is small enough to fit in skb->head, pull it completely.
-	 * If not, only pull ieee80211_hdr so that splice() or TCP coalesce
-	 * are more efficient.
+	 * If not, only pull ieee80211_hdr (including crypto if present, and
+	 * an additional 8 bytes for SNAP/ethertype, see below) so that
+	 * splice() or TCP coalesce are more efficient.
+	 *
+	 * Since, in addition, ieee80211_data_to_8023() always pull in at
+	 * least 8 bytes (possibly more for mesh) we can do the same here
+	 * to save the cost of doing it later. That still doesn't pull in
+	 * the actual IP header since the typical case has a SNAP header.
+	 * If the latter changes (there are efforts in the standards group
+	 * to do so) we should revisit this and ieee80211_data_to_8023().
 	 */
-	hdrlen = (len <= skb_tailroom(skb)) ? len : sizeof(*hdr);
+	hdrlen = (len <= skb_tailroom(skb)) ? len :
+					      sizeof(*hdr) + crypt_len + 8;
 
 	memcpy(skb_put(skb, hdrlen), hdr, hdrlen);
 	fraglen = len - hdrlen;
@@ -129,8 +129,6 @@
 				fraglen, rxb->truesize);
 	}
 
-	memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats));
-
 	ieee80211_rx(mvm->hw, skb);
 }
 
@@ -185,7 +183,8 @@
 static u32 iwl_mvm_set_mac80211_rx_flag(struct iwl_mvm *mvm,
 					struct ieee80211_hdr *hdr,
 					struct ieee80211_rx_status *stats,
-					u32 rx_pkt_status)
+					u32 rx_pkt_status,
+					u8 *crypt_len)
 {
 	if (!ieee80211_has_protected(hdr->frame_control) ||
 	    (rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) ==
@@ -205,12 +204,14 @@
 
 		stats->flag |= RX_FLAG_DECRYPTED;
 		IWL_DEBUG_WEP(mvm, "hw decrypted CCMP successfully\n");
+		*crypt_len = IEEE80211_CCMP_HDR_LEN;
 		return 0;
 
 	case RX_MPDU_RES_STATUS_SEC_TKIP_ENC:
 		/* Don't drop the frame and decrypt it in SW */
 		if (!(rx_pkt_status & RX_MPDU_RES_STATUS_TTAK_OK))
 			return 0;
+		*crypt_len = IEEE80211_TKIP_IV_LEN;
 		/* fall through if TTAK OK */
 
 	case RX_MPDU_RES_STATUS_SEC_WEP_ENC:
@@ -218,6 +219,9 @@
 			return -1;
 
 		stats->flag |= RX_FLAG_DECRYPTED;
+		if ((rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) ==
+				RX_MPDU_RES_STATUS_SEC_WEP_ENC)
+			*crypt_len = IEEE80211_WEP_IV_LEN;
 		return 0;
 
 	case RX_MPDU_RES_STATUS_SEC_EXT_ENC:
@@ -242,15 +246,17 @@
 		       struct iwl_device_cmd *cmd)
 {
 	struct ieee80211_hdr *hdr;
-	struct ieee80211_rx_status rx_status = {};
+	struct ieee80211_rx_status *rx_status;
 	struct iwl_rx_packet *pkt = rxb_addr(rxb);
 	struct iwl_rx_phy_info *phy_info;
 	struct iwl_rx_mpdu_res_start *rx_res;
 	struct ieee80211_sta *sta;
+	struct sk_buff *skb;
 	u32 len;
 	u32 ampdu_status;
 	u32 rate_n_flags;
 	u32 rx_pkt_status;
+	u8 crypt_len = 0;
 
 	phy_info = &mvm->last_phy_info;
 	rx_res = (struct iwl_rx_mpdu_res_start *)pkt->data;
@@ -259,20 +265,32 @@
 	rx_pkt_status = le32_to_cpup((__le32 *)
 		(pkt->data + sizeof(*rx_res) + len));
 
-	memset(&rx_status, 0, sizeof(rx_status));
+	/* Dont use dev_alloc_skb(), we'll have enough headroom once
+	 * ieee80211_hdr pulled.
+	 */
+	skb = alloc_skb(128, GFP_ATOMIC);
+	if (!skb) {
+		IWL_ERR(mvm, "alloc_skb failed\n");
+		return 0;
+	}
+
+	rx_status = IEEE80211_SKB_RXCB(skb);
 
 	/*
 	 * drop the packet if it has failed being decrypted by HW
 	 */
-	if (iwl_mvm_set_mac80211_rx_flag(mvm, hdr, &rx_status, rx_pkt_status)) {
+	if (iwl_mvm_set_mac80211_rx_flag(mvm, hdr, rx_status, rx_pkt_status,
+					 &crypt_len)) {
 		IWL_DEBUG_DROP(mvm, "Bad decryption results 0x%08x\n",
 			       rx_pkt_status);
+		kfree_skb(skb);
 		return 0;
 	}
 
 	if ((unlikely(phy_info->cfg_phy_cnt > 20))) {
 		IWL_DEBUG_DROP(mvm, "dsp size out of range [0,20]: %d\n",
 			       phy_info->cfg_phy_cnt);
+		kfree_skb(skb);
 		return 0;
 	}
 
@@ -283,31 +301,31 @@
 	if (!(rx_pkt_status & RX_MPDU_RES_STATUS_CRC_OK) ||
 	    !(rx_pkt_status & RX_MPDU_RES_STATUS_OVERRUN_OK)) {
 		IWL_DEBUG_RX(mvm, "Bad CRC or FIFO: 0x%08X.\n", rx_pkt_status);
-		rx_status.flag |= RX_FLAG_FAILED_FCS_CRC;
+		rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
 	}
 
 	/* This will be used in several places later */
 	rate_n_flags = le32_to_cpu(phy_info->rate_n_flags);
 
 	/* rx_status carries information about the packet to mac80211 */
-	rx_status.mactime = le64_to_cpu(phy_info->timestamp);
-	rx_status.device_timestamp = le32_to_cpu(phy_info->system_timestamp);
-	rx_status.band =
+	rx_status->mactime = le64_to_cpu(phy_info->timestamp);
+	rx_status->device_timestamp = le32_to_cpu(phy_info->system_timestamp);
+	rx_status->band =
 		(phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_BAND_24)) ?
 				IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ;
-	rx_status.freq =
+	rx_status->freq =
 		ieee80211_channel_to_frequency(le16_to_cpu(phy_info->channel),
-					       rx_status.band);
+					       rx_status->band);
 	/*
 	 * TSF as indicated by the fw is at INA time, but mac80211 expects the
 	 * TSF at the beginning of the MPDU.
 	 */
-	/*rx_status.flag |= RX_FLAG_MACTIME_MPDU;*/
+	/*rx_status->flag |= RX_FLAG_MACTIME_MPDU;*/
 
-	iwl_mvm_get_signal_strength(mvm, phy_info, &rx_status);
+	iwl_mvm_get_signal_strength(mvm, phy_info, rx_status);
 
-	IWL_DEBUG_STATS_LIMIT(mvm, "Rssi %d, TSF %llu\n", rx_status.signal,
-			      (unsigned long long)rx_status.mactime);
+	IWL_DEBUG_STATS_LIMIT(mvm, "Rssi %d, TSF %llu\n", rx_status->signal,
+			      (unsigned long long)rx_status->mactime);
 
 	rcu_read_lock();
 	/*
@@ -326,15 +344,14 @@
 	if (sta) {
 		struct iwl_mvm_sta *mvmsta;
 		mvmsta = iwl_mvm_sta_from_mac80211(sta);
-		rs_update_last_rssi(mvm, &mvmsta->lq_sta,
-				    &rx_status);
+		rs_update_last_rssi(mvm, &mvmsta->lq_sta, rx_status);
 	}
 
 	rcu_read_unlock();
 
 	/* set the preamble flag if appropriate */
 	if (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_SHORT_PREAMBLE))
-		rx_status.flag |= RX_FLAG_SHORTPRE;
+		rx_status->flag |= RX_FLAG_SHORTPRE;
 
 	if (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_AGG)) {
 		/*
@@ -342,8 +359,8 @@
 		 * together since we get a single PHY response
 		 * from the firmware for all of them
 		 */
-		rx_status.flag |= RX_FLAG_AMPDU_DETAILS;
-		rx_status.ampdu_reference = mvm->ampdu_ref;
+		rx_status->flag |= RX_FLAG_AMPDU_DETAILS;
+		rx_status->ampdu_reference = mvm->ampdu_ref;
 	}
 
 	/* Set up the HT phy flags */
@@ -351,50 +368,50 @@
 	case RATE_MCS_CHAN_WIDTH_20:
 		break;
 	case RATE_MCS_CHAN_WIDTH_40:
-		rx_status.flag |= RX_FLAG_40MHZ;
+		rx_status->flag |= RX_FLAG_40MHZ;
 		break;
 	case RATE_MCS_CHAN_WIDTH_80:
-		rx_status.vht_flag |= RX_VHT_FLAG_80MHZ;
+		rx_status->vht_flag |= RX_VHT_FLAG_80MHZ;
 		break;
 	case RATE_MCS_CHAN_WIDTH_160:
-		rx_status.vht_flag |= RX_VHT_FLAG_160MHZ;
+		rx_status->vht_flag |= RX_VHT_FLAG_160MHZ;
 		break;
 	}
 	if (rate_n_flags & RATE_MCS_SGI_MSK)
-		rx_status.flag |= RX_FLAG_SHORT_GI;
+		rx_status->flag |= RX_FLAG_SHORT_GI;
 	if (rate_n_flags & RATE_HT_MCS_GF_MSK)
-		rx_status.flag |= RX_FLAG_HT_GF;
+		rx_status->flag |= RX_FLAG_HT_GF;
 	if (rate_n_flags & RATE_MCS_LDPC_MSK)
-		rx_status.flag |= RX_FLAG_LDPC;
+		rx_status->flag |= RX_FLAG_LDPC;
 	if (rate_n_flags & RATE_MCS_HT_MSK) {
 		u8 stbc = (rate_n_flags & RATE_MCS_HT_STBC_MSK) >>
 				RATE_MCS_STBC_POS;
-		rx_status.flag |= RX_FLAG_HT;
-		rx_status.rate_idx = rate_n_flags & RATE_HT_MCS_INDEX_MSK;
-		rx_status.flag |= stbc << RX_FLAG_STBC_SHIFT;
+		rx_status->flag |= RX_FLAG_HT;
+		rx_status->rate_idx = rate_n_flags & RATE_HT_MCS_INDEX_MSK;
+		rx_status->flag |= stbc << RX_FLAG_STBC_SHIFT;
 	} else if (rate_n_flags & RATE_MCS_VHT_MSK) {
 		u8 stbc = (rate_n_flags & RATE_MCS_VHT_STBC_MSK) >>
 				RATE_MCS_STBC_POS;
-		rx_status.vht_nss =
+		rx_status->vht_nss =
 			((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >>
 						RATE_VHT_MCS_NSS_POS) + 1;
-		rx_status.rate_idx = rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK;
-		rx_status.flag |= RX_FLAG_VHT;
-		rx_status.flag |= stbc << RX_FLAG_STBC_SHIFT;
+		rx_status->rate_idx = rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK;
+		rx_status->flag |= RX_FLAG_VHT;
+		rx_status->flag |= stbc << RX_FLAG_STBC_SHIFT;
 		if (rate_n_flags & RATE_MCS_BF_MSK)
-			rx_status.vht_flag |= RX_VHT_FLAG_BF;
+			rx_status->vht_flag |= RX_VHT_FLAG_BF;
 	} else {
-		rx_status.rate_idx =
+		rx_status->rate_idx =
 			iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags,
-							    rx_status.band);
+							    rx_status->band);
 	}
 
 #ifdef CONFIG_IWLWIFI_DEBUGFS
 	iwl_mvm_update_frame_stats(mvm, &mvm->drv_rx_stats, rate_n_flags,
-				   rx_status.flag & RX_FLAG_AMPDU_DETAILS);
+				   rx_status->flag & RX_FLAG_AMPDU_DETAILS);
 #endif
-	iwl_mvm_pass_packet_to_mac80211(mvm, hdr, len, ampdu_status,
-					rxb, &rx_status);
+	iwl_mvm_pass_packet_to_mac80211(mvm, skb, hdr, len, ampdu_status,
+					crypt_len, rxb);
 	return 0;
 }
 
@@ -500,29 +517,8 @@
 		.mvm = mvm,
 	};
 
-	/*
-	 * set temperature debug enabled - ignore FW temperature updates
-	 * and use the user set temperature.
-	 */
-	if (mvm->temperature_test) {
-		if (mvm->temperature < le32_to_cpu(common->temperature))
-			IWL_DEBUG_TEMP(mvm,
-				       "Ignoring FW temperature update that is greater than the debug set temperature (debug temp = %d, fw temp = %d)\n",
-				       mvm->temperature,
-				       le32_to_cpu(common->temperature));
-		/*
-		 * skip iwl_mvm_tt_handler since we are in
-		 * temperature debug mode and we are ignoring
-		 * the new temperature value
-		 */
-		goto update;
-	}
+	iwl_mvm_tt_temp_changed(mvm, le32_to_cpu(common->temperature));
 
-	if (mvm->temperature != le32_to_cpu(common->temperature)) {
-		mvm->temperature = le32_to_cpu(common->temperature);
-		iwl_mvm_tt_handler(mvm);
-	}
-update:
 	iwl_mvm_update_rx_statistics(mvm, stats);
 
 	ieee80211_iterate_active_interfaces(mvm->hw,
diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c
index 5cd59a4..e5294d0 100644
--- a/drivers/net/wireless/iwlwifi/mvm/scan.c
+++ b/drivers/net/wireless/iwlwifi/mvm/scan.c
@@ -83,15 +83,29 @@
 	} dwell[IEEE80211_NUM_BANDS];
 };
 
+enum iwl_umac_scan_uid_type {
+	IWL_UMAC_SCAN_UID_REG_SCAN	= BIT(0),
+	IWL_UMAC_SCAN_UID_SCHED_SCAN	= BIT(1),
+	IWL_UMAC_SCAN_UID_ALL		= IWL_UMAC_SCAN_UID_REG_SCAN |
+					  IWL_UMAC_SCAN_UID_SCHED_SCAN,
+};
+
+static int iwl_umac_scan_stop(struct iwl_mvm *mvm,
+			      enum iwl_umac_scan_uid_type type, bool notify);
+
+static u8 iwl_mvm_scan_rx_ant(struct iwl_mvm *mvm)
+{
+	if (mvm->scan_rx_ant != ANT_NONE)
+		return mvm->scan_rx_ant;
+	return mvm->fw->valid_rx_ant;
+}
+
 static inline __le16 iwl_mvm_scan_rx_chain(struct iwl_mvm *mvm)
 {
 	u16 rx_chain;
 	u8 rx_ant;
 
-	if (mvm->scan_rx_ant != ANT_NONE)
-		rx_ant = mvm->scan_rx_ant;
-	else
-		rx_ant = mvm->fw->valid_rx_ant;
+	rx_ant = iwl_mvm_scan_rx_ant(mvm);
 	rx_chain = rx_ant << PHY_RX_CHAIN_VALID_POS;
 	rx_chain |= rx_ant << PHY_RX_CHAIN_FORCE_MIMO_SEL_POS;
 	rx_chain |= rx_ant << PHY_RX_CHAIN_FORCE_SEL_POS;
@@ -366,6 +380,10 @@
 	    !is_sched_scan)
 		max_probe_len -= 32;
 
+	/* DS parameter set element is added on 2.4GHZ band if required */
+	if (iwl_mvm_rrm_scan_needed(mvm))
+		max_probe_len -= 3;
+
 	return max_probe_len;
 }
 
@@ -537,23 +555,17 @@
 				    struct iwl_device_cmd *cmd)
 {
 	struct iwl_rx_packet *pkt = rxb_addr(rxb);
-	u8 client_bitmap = 0;
 
-	if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)) {
+	if (!(mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN) &&
+	    !(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)) {
 		struct iwl_sched_scan_results *notif = (void *)pkt->data;
 
-		client_bitmap = notif->client_bitmap;
+		if (!(notif->client_bitmap & SCAN_CLIENT_SCHED_SCAN))
+			return 0;
 	}
 
-	if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN ||
-	    client_bitmap & SCAN_CLIENT_SCHED_SCAN) {
-		if (mvm->scan_status == IWL_MVM_SCAN_SCHED) {
-			IWL_DEBUG_SCAN(mvm, "Scheduled scan results\n");
-			ieee80211_sched_scan_results(mvm->hw);
-		} else {
-			IWL_DEBUG_SCAN(mvm, "Scan results\n");
-		}
-	}
+	IWL_DEBUG_SCAN(mvm, "Scheduled scan results\n");
+	ieee80211_sched_scan_results(mvm->hw);
 
 	return 0;
 }
@@ -603,16 +615,6 @@
 					       SCAN_COMPLETE_NOTIFICATION };
 	int ret;
 
-	if (mvm->scan_status == IWL_MVM_SCAN_NONE)
-		return 0;
-
-	if (iwl_mvm_is_radio_killed(mvm)) {
-		ieee80211_scan_completed(mvm->hw, true);
-		iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
-		mvm->scan_status = IWL_MVM_SCAN_NONE;
-		return 0;
-	}
-
 	iwl_init_notification_wait(&mvm->notif_wait, &wait_scan_abort,
 				   scan_abort_notif,
 				   ARRAY_SIZE(scan_abort_notif),
@@ -975,6 +977,20 @@
 	return ret;
 }
 
+static bool iwl_mvm_scan_pass_all(struct iwl_mvm *mvm,
+				  struct cfg80211_sched_scan_request *req)
+{
+	if (req->n_match_sets && req->match_sets[0].ssid.ssid_len) {
+		IWL_DEBUG_SCAN(mvm,
+			       "Sending scheduled scan with filtering, n_match_sets %d\n",
+			       req->n_match_sets);
+		return false;
+	}
+
+	IWL_DEBUG_SCAN(mvm, "Sending Scheduled scan without filtering\n");
+	return true;
+}
+
 int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm,
 			     struct cfg80211_sched_scan_request *req)
 {
@@ -990,15 +1006,8 @@
 		.schedule_line[1].full_scan_mul = IWL_FULL_SCAN_MULTIPLIER,
 	};
 
-	if (req->n_match_sets && req->match_sets[0].ssid.ssid_len) {
-		IWL_DEBUG_SCAN(mvm,
-			       "Sending scheduled scan with filtering, filter len %d\n",
-			       req->n_match_sets);
-	} else {
-		IWL_DEBUG_SCAN(mvm,
-			       "Sending Scheduled scan without filtering\n");
+	if (iwl_mvm_scan_pass_all(mvm, req))
 		scan_req.flags |= cpu_to_le16(IWL_SCAN_OFFLOAD_FLAG_PASS_ALL);
-	}
 
 	if (mvm->last_ebs_successful &&
 	    mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_EBS_SUPPORT)
@@ -1016,12 +1025,19 @@
 {
 	int ret;
 
-	if ((mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)) {
+	if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN) {
+		ret = iwl_mvm_config_sched_scan_profiles(mvm, req);
+		if (ret)
+			return ret;
+		ret = iwl_mvm_sched_scan_umac(mvm, vif, req, ies);
+	} else if ((mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)) {
+		mvm->scan_status = IWL_MVM_SCAN_SCHED;
 		ret = iwl_mvm_config_sched_scan_profiles(mvm, req);
 		if (ret)
 			return ret;
 		ret = iwl_mvm_unified_sched_scan_lmac(mvm, vif, req, ies);
 	} else {
+		mvm->scan_status = IWL_MVM_SCAN_SCHED;
 		ret = iwl_mvm_config_sched_scan(mvm, vif, req, ies);
 		if (ret)
 			return ret;
@@ -1078,6 +1094,10 @@
 
 	lockdep_assert_held(&mvm->mutex);
 
+	if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN)
+		return iwl_umac_scan_stop(mvm, IWL_UMAC_SCAN_UID_SCHED_SCAN,
+					  notify);
+
 	if (mvm->scan_status != IWL_MVM_SCAN_SCHED &&
 	    (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN) ||
 	     mvm->scan_status != IWL_MVM_SCAN_OS)) {
@@ -1165,20 +1185,64 @@
 	}
 }
 
+static u8 *iwl_mvm_copy_and_insert_ds_elem(struct iwl_mvm *mvm, const u8 *ies,
+					   size_t len, u8 *const pos)
+{
+	static const u8 before_ds_params[] = {
+			WLAN_EID_SSID,
+			WLAN_EID_SUPP_RATES,
+			WLAN_EID_REQUEST,
+			WLAN_EID_EXT_SUPP_RATES,
+	};
+	size_t offs;
+	u8 *newpos = pos;
+
+	if (!iwl_mvm_rrm_scan_needed(mvm)) {
+		memcpy(newpos, ies, len);
+		return newpos + len;
+	}
+
+	offs = ieee80211_ie_split(ies, len,
+				  before_ds_params,
+				  ARRAY_SIZE(before_ds_params),
+				  0);
+
+	memcpy(newpos, ies, offs);
+	newpos += offs;
+
+	/* Add a placeholder for DS Parameter Set element */
+	*newpos++ = WLAN_EID_DS_PARAMS;
+	*newpos++ = 1;
+	*newpos++ = 0;
+
+	memcpy(newpos, ies + offs, len - offs);
+	newpos += len - offs;
+
+	return newpos;
+}
+
 static void
 iwl_mvm_build_unified_scan_probe(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
 				 struct ieee80211_scan_ies *ies,
-				 struct iwl_scan_req_unified_lmac *cmd)
+				 struct iwl_scan_probe_req *preq,
+				 const u8 *mac_addr, const u8 *mac_addr_mask)
 {
-	struct iwl_scan_probe_req *preq = (void *)(cmd->data +
-		sizeof(struct iwl_scan_channel_cfg_lmac) *
-			mvm->fw->ucode_capa.n_scan_channels);
 	struct ieee80211_mgmt *frame = (struct ieee80211_mgmt *)preq->buf;
-	u8 *pos;
+	u8 *pos, *newpos;
+
+	/*
+	 * Unfortunately, right now the offload scan doesn't support randomising
+	 * within the firmware, so until the firmware API is ready we implement
+	 * it in the driver. This means that the scan iterations won't really be
+	 * random, only when it's restarted, but at least that helps a bit.
+	 */
+	if (mac_addr)
+		get_random_mask_addr(frame->sa, mac_addr, mac_addr_mask);
+	else
+		memcpy(frame->sa, vif->addr, ETH_ALEN);
 
 	frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ);
 	eth_broadcast_addr(frame->da);
-	memcpy(frame->sa, vif->addr, ETH_ALEN);
 	eth_broadcast_addr(frame->bssid);
 	frame->seq_ctrl = 0;
 
@@ -1189,11 +1253,14 @@
 	preq->mac_header.offset = 0;
 	preq->mac_header.len = cpu_to_le16(24 + 2);
 
-	memcpy(pos, ies->ies[IEEE80211_BAND_2GHZ],
-	       ies->len[IEEE80211_BAND_2GHZ]);
+	/* Insert ds parameter set element on 2.4 GHz band */
+	newpos = iwl_mvm_copy_and_insert_ds_elem(mvm,
+						 ies->ies[IEEE80211_BAND_2GHZ],
+						 ies->len[IEEE80211_BAND_2GHZ],
+						 pos);
 	preq->band_data[0].offset = cpu_to_le16(pos - preq->buf);
-	preq->band_data[0].len = cpu_to_le16(ies->len[IEEE80211_BAND_2GHZ]);
-	pos += ies->len[IEEE80211_BAND_2GHZ];
+	preq->band_data[0].len = cpu_to_le16(newpos - pos);
+	pos = newpos;
 
 	memcpy(pos, ies->ies[IEEE80211_BAND_5GHZ],
 	       ies->len[IEEE80211_BAND_5GHZ]);
@@ -1254,9 +1321,10 @@
 		.dataflags = { IWL_HCMD_DFL_NOCOPY, },
 	};
 	struct iwl_scan_req_unified_lmac *cmd = mvm->scan_cmd;
+	struct iwl_scan_probe_req *preq;
 	struct iwl_mvm_scan_params params = {};
 	u32 flags;
-	int ssid_bitmap = 0;
+	u32 ssid_bitmap = 0;
 	int ret, i;
 
 	lockdep_assert_held(&mvm->mutex);
@@ -1315,7 +1383,13 @@
 				       req->req.n_channels, ssid_bitmap,
 				       cmd);
 
-	iwl_mvm_build_unified_scan_probe(mvm, vif, &req->ies, cmd);
+	preq = (void *)(cmd->data + sizeof(struct iwl_scan_channel_cfg_lmac) *
+			mvm->fw->ucode_capa.n_scan_channels);
+
+	iwl_mvm_build_unified_scan_probe(mvm, vif, &req->ies, preq,
+		req->req.flags & NL80211_SCAN_FLAG_RANDOM_ADDR ?
+			req->req.mac_addr : NULL,
+		req->req.mac_addr_mask);
 
 	ret = iwl_mvm_send_cmd(mvm, &hcmd);
 	if (!ret) {
@@ -1348,6 +1422,7 @@
 		.dataflags = { IWL_HCMD_DFL_NOCOPY, },
 	};
 	struct iwl_scan_req_unified_lmac *cmd = mvm->scan_cmd;
+	struct iwl_scan_probe_req *preq;
 	struct iwl_mvm_scan_params params = {};
 	int ret;
 	u32 flags = 0, ssid_bitmap = 0;
@@ -1371,15 +1446,8 @@
 
 	cmd->n_channels = (u8)req->n_channels;
 
-	if (req->n_match_sets && req->match_sets[0].ssid.ssid_len) {
-		IWL_DEBUG_SCAN(mvm,
-			       "Sending scheduled scan with filtering, n_match_sets %d\n",
-			       req->n_match_sets);
-	} else {
-		IWL_DEBUG_SCAN(mvm,
-			       "Sending Scheduled scan without filtering\n");
+	if (iwl_mvm_scan_pass_all(mvm, req))
 		flags |= IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL;
-	}
 
 	if (req->n_ssids == 1 && req->ssids[0].ssid_len != 0)
 		flags |= IWL_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION;
@@ -1409,7 +1477,13 @@
 	iwl_mvm_lmac_scan_cfg_channels(mvm, req->channels, req->n_channels,
 				       ssid_bitmap, cmd);
 
-	iwl_mvm_build_unified_scan_probe(mvm, vif, ies, cmd);
+	preq = (void *)(cmd->data + sizeof(struct iwl_scan_channel_cfg_lmac) *
+			mvm->fw->ucode_capa.n_scan_channels);
+
+	iwl_mvm_build_unified_scan_probe(mvm, vif, ies, preq,
+		req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR ?
+			req->mac_addr : NULL,
+		req->mac_addr_mask);
 
 	ret = iwl_mvm_send_cmd(mvm, &hcmd);
 	if (!ret) {
@@ -1431,7 +1505,594 @@
 
 int iwl_mvm_cancel_scan(struct iwl_mvm *mvm)
 {
+	if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN)
+		return iwl_umac_scan_stop(mvm, IWL_UMAC_SCAN_UID_REG_SCAN,
+					  true);
+
+	if (mvm->scan_status == IWL_MVM_SCAN_NONE)
+		return 0;
+
+	if (iwl_mvm_is_radio_killed(mvm)) {
+		ieee80211_scan_completed(mvm->hw, true);
+		iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
+		mvm->scan_status = IWL_MVM_SCAN_NONE;
+		return 0;
+	}
+
 	if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
 		return iwl_mvm_scan_offload_stop(mvm, true);
 	return iwl_mvm_cancel_regular_scan(mvm);
 }
+
+/* UMAC scan API */
+
+struct iwl_umac_scan_done {
+	struct iwl_mvm *mvm;
+	enum iwl_umac_scan_uid_type type;
+};
+
+static int rate_to_scan_rate_flag(unsigned int rate)
+{
+	static const int rate_to_scan_rate[IWL_RATE_COUNT] = {
+		[IWL_RATE_1M_INDEX]	= SCAN_CONFIG_RATE_1M,
+		[IWL_RATE_2M_INDEX]	= SCAN_CONFIG_RATE_2M,
+		[IWL_RATE_5M_INDEX]	= SCAN_CONFIG_RATE_5M,
+		[IWL_RATE_11M_INDEX]	= SCAN_CONFIG_RATE_11M,
+		[IWL_RATE_6M_INDEX]	= SCAN_CONFIG_RATE_6M,
+		[IWL_RATE_9M_INDEX]	= SCAN_CONFIG_RATE_9M,
+		[IWL_RATE_12M_INDEX]	= SCAN_CONFIG_RATE_12M,
+		[IWL_RATE_18M_INDEX]	= SCAN_CONFIG_RATE_18M,
+		[IWL_RATE_24M_INDEX]	= SCAN_CONFIG_RATE_24M,
+		[IWL_RATE_36M_INDEX]	= SCAN_CONFIG_RATE_36M,
+		[IWL_RATE_48M_INDEX]	= SCAN_CONFIG_RATE_48M,
+		[IWL_RATE_54M_INDEX]	= SCAN_CONFIG_RATE_54M,
+	};
+
+	return rate_to_scan_rate[rate];
+}
+
+static __le32 iwl_mvm_scan_config_rates(struct iwl_mvm *mvm)
+{
+	struct ieee80211_supported_band *band;
+	unsigned int rates = 0;
+	int i;
+
+	band = &mvm->nvm_data->bands[IEEE80211_BAND_2GHZ];
+	for (i = 0; i < band->n_bitrates; i++)
+		rates |= rate_to_scan_rate_flag(band->bitrates[i].hw_value);
+	band = &mvm->nvm_data->bands[IEEE80211_BAND_5GHZ];
+	for (i = 0; i < band->n_bitrates; i++)
+		rates |= rate_to_scan_rate_flag(band->bitrates[i].hw_value);
+
+	/* Set both basic rates and supported rates */
+	rates |= SCAN_CONFIG_SUPPORTED_RATE(rates);
+
+	return cpu_to_le32(rates);
+}
+
+int iwl_mvm_config_scan(struct iwl_mvm *mvm)
+{
+
+	struct iwl_scan_config *scan_config;
+	struct ieee80211_supported_band *band;
+	int num_channels =
+		mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels +
+		mvm->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels;
+	int ret, i, j = 0, cmd_size, data_size;
+	struct iwl_host_cmd cmd = {
+		.id = SCAN_CFG_CMD,
+	};
+
+	if (WARN_ON(num_channels > mvm->fw->ucode_capa.n_scan_channels))
+		return -ENOBUFS;
+
+	cmd_size = sizeof(*scan_config) + mvm->fw->ucode_capa.n_scan_channels;
+
+	scan_config = kzalloc(cmd_size, GFP_KERNEL);
+	if (!scan_config)
+		return -ENOMEM;
+
+	data_size = cmd_size - sizeof(struct iwl_mvm_umac_cmd_hdr);
+	scan_config->hdr.size = cpu_to_le16(data_size);
+	scan_config->flags = cpu_to_le32(SCAN_CONFIG_FLAG_ACTIVATE |
+					 SCAN_CONFIG_FLAG_ALLOW_CHUB_REQS |
+					 SCAN_CONFIG_FLAG_SET_TX_CHAINS |
+					 SCAN_CONFIG_FLAG_SET_RX_CHAINS |
+					 SCAN_CONFIG_FLAG_SET_ALL_TIMES |
+					 SCAN_CONFIG_FLAG_SET_LEGACY_RATES |
+					 SCAN_CONFIG_FLAG_SET_MAC_ADDR |
+					 SCAN_CONFIG_FLAG_SET_CHANNEL_FLAGS|
+					 SCAN_CONFIG_N_CHANNELS(num_channels));
+	scan_config->tx_chains = cpu_to_le32(mvm->fw->valid_tx_ant);
+	scan_config->rx_chains = cpu_to_le32(iwl_mvm_scan_rx_ant(mvm));
+	scan_config->legacy_rates = iwl_mvm_scan_config_rates(mvm);
+	scan_config->out_of_channel_time = cpu_to_le32(170);
+	scan_config->suspend_time = cpu_to_le32(30);
+	scan_config->dwell_active = 20;
+	scan_config->dwell_passive = 110;
+	scan_config->dwell_fragmented = 20;
+
+	memcpy(&scan_config->mac_addr, &mvm->addresses[0].addr, ETH_ALEN);
+
+	scan_config->bcast_sta_id = mvm->aux_sta.sta_id;
+	scan_config->channel_flags = IWL_CHANNEL_FLAG_EBS |
+				     IWL_CHANNEL_FLAG_ACCURATE_EBS |
+				     IWL_CHANNEL_FLAG_EBS_ADD |
+				     IWL_CHANNEL_FLAG_PRE_SCAN_PASSIVE2ACTIVE;
+
+	band = &mvm->nvm_data->bands[IEEE80211_BAND_2GHZ];
+	for (i = 0; i < band->n_channels; i++, j++)
+		scan_config->channel_array[j] = band->channels[i].center_freq;
+	band = &mvm->nvm_data->bands[IEEE80211_BAND_5GHZ];
+	for (i = 0; i < band->n_channels; i++, j++)
+		scan_config->channel_array[j] = band->channels[i].center_freq;
+
+	cmd.data[0] = scan_config;
+	cmd.len[0] = cmd_size;
+	cmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY;
+
+	IWL_DEBUG_SCAN(mvm, "Sending UMAC scan config\n");
+
+	ret = iwl_mvm_send_cmd(mvm, &cmd);
+
+	kfree(scan_config);
+	return ret;
+}
+
+static int iwl_mvm_find_scan_uid(struct iwl_mvm *mvm, u32 uid)
+{
+	int i;
+
+	for (i = 0; i < IWL_MVM_MAX_SIMULTANEOUS_SCANS; i++)
+		if (mvm->scan_uid[i] == uid)
+			return i;
+
+	return i;
+}
+
+static int iwl_mvm_find_free_scan_uid(struct iwl_mvm *mvm)
+{
+	return iwl_mvm_find_scan_uid(mvm, 0);
+}
+
+static bool iwl_mvm_find_scan_type(struct iwl_mvm *mvm,
+				   enum iwl_umac_scan_uid_type type)
+{
+	int i;
+
+	for (i = 0; i < IWL_MVM_MAX_SIMULTANEOUS_SCANS; i++)
+		if (mvm->scan_uid[i] & type)
+			return true;
+
+	return false;
+}
+
+static u32 iwl_generate_scan_uid(struct iwl_mvm *mvm,
+				 enum iwl_umac_scan_uid_type type)
+{
+	u32 uid;
+
+	/* make sure exactly one bit is on in scan type */
+	WARN_ON(hweight8(type) != 1);
+
+	/*
+	 * Make sure scan uids are unique. If one scan lasts long time while
+	 * others are completing frequently, the seq number will wrap up and
+	 * we may have more than one scan with the same uid.
+	 */
+	do {
+		uid = type | (mvm->scan_seq_num <<
+			      IWL_UMAC_SCAN_UID_SEQ_OFFSET);
+		mvm->scan_seq_num++;
+	} while (iwl_mvm_find_scan_uid(mvm, uid) <
+		 IWL_MVM_MAX_SIMULTANEOUS_SCANS);
+
+	IWL_DEBUG_SCAN(mvm, "Generated scan UID %u\n", uid);
+
+	return uid;
+}
+
+static void
+iwl_mvm_build_generic_umac_scan_cmd(struct iwl_mvm *mvm,
+				    struct iwl_scan_req_umac *cmd,
+				    struct iwl_mvm_scan_params *params)
+{
+	memset(cmd, 0, ksize(cmd));
+	cmd->hdr.size = cpu_to_le16(iwl_mvm_scan_size(mvm) -
+				    sizeof(struct iwl_mvm_umac_cmd_hdr));
+	cmd->active_dwell = params->dwell[IEEE80211_BAND_2GHZ].active;
+	cmd->passive_dwell = params->dwell[IEEE80211_BAND_2GHZ].passive;
+	if (params->passive_fragmented)
+		cmd->fragmented_dwell =
+				params->dwell[IEEE80211_BAND_2GHZ].passive;
+	cmd->max_out_time = cpu_to_le32(params->max_out_time);
+	cmd->suspend_time = cpu_to_le32(params->suspend_time);
+	cmd->scan_priority = cpu_to_le32(IWL_SCAN_PRIORITY_HIGH);
+}
+
+static void
+iwl_mvm_umac_scan_cfg_channels(struct iwl_mvm *mvm,
+			       struct ieee80211_channel **channels,
+			       int n_channels, u32 ssid_bitmap,
+			       struct iwl_scan_req_umac *cmd)
+{
+	struct iwl_scan_channel_cfg_umac *channel_cfg = (void *)&cmd->data;
+	int i;
+
+	for (i = 0; i < n_channels; i++) {
+		channel_cfg[i].flags = cpu_to_le32(ssid_bitmap);
+		channel_cfg[i].channel_num = channels[i]->hw_value;
+		channel_cfg[i].iter_count = 1;
+		channel_cfg[i].iter_interval = 0;
+	}
+}
+
+static u32 iwl_mvm_scan_umac_common_flags(struct iwl_mvm *mvm, int n_ssids,
+					  struct cfg80211_ssid *ssids,
+					  int fragmented)
+{
+	int flags = 0;
+
+	if (n_ssids == 0)
+		flags = IWL_UMAC_SCAN_GEN_FLAGS_PASSIVE;
+
+	if (n_ssids == 1 && ssids[0].ssid_len != 0)
+		flags |= IWL_UMAC_SCAN_GEN_FLAGS_PRE_CONNECT;
+
+	if (fragmented)
+		flags |= IWL_UMAC_SCAN_GEN_FLAGS_FRAGMENTED;
+
+	if (iwl_mvm_rrm_scan_needed(mvm))
+		flags |= IWL_UMAC_SCAN_GEN_FLAGS_RRM_ENABLED;
+
+	return flags;
+}
+
+int iwl_mvm_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+		      struct ieee80211_scan_request *req)
+{
+	struct iwl_host_cmd hcmd = {
+		.id = SCAN_REQ_UMAC,
+		.len = { iwl_mvm_scan_size(mvm), },
+		.data = { mvm->scan_cmd, },
+		.dataflags = { IWL_HCMD_DFL_NOCOPY, },
+	};
+	struct iwl_scan_req_umac *cmd = mvm->scan_cmd;
+	struct iwl_scan_req_umac_tail *sec_part = (void *)&cmd->data +
+		sizeof(struct iwl_scan_channel_cfg_umac) *
+			mvm->fw->ucode_capa.n_scan_channels;
+	struct iwl_mvm_scan_params params = {};
+	u32 uid, flags;
+	u32 ssid_bitmap = 0;
+	int ret, i, uid_idx;
+
+	lockdep_assert_held(&mvm->mutex);
+
+	uid_idx = iwl_mvm_find_free_scan_uid(mvm);
+	if (uid_idx >= IWL_MVM_MAX_SIMULTANEOUS_SCANS)
+		return -EBUSY;
+
+	/* we should have failed registration if scan_cmd was NULL */
+	if (WARN_ON(mvm->scan_cmd == NULL))
+		return -ENOMEM;
+
+	if (WARN_ON(req->req.n_ssids > PROBE_OPTION_MAX ||
+		    req->ies.common_ie_len +
+		    req->ies.len[NL80211_BAND_2GHZ] +
+		    req->ies.len[NL80211_BAND_5GHZ] + 24 + 2 >
+		    SCAN_OFFLOAD_PROBE_REQ_SIZE || req->req.n_channels >
+		    mvm->fw->ucode_capa.n_scan_channels))
+		return -ENOBUFS;
+
+	iwl_mvm_scan_calc_params(mvm, vif, req->req.n_ssids, req->req.flags,
+				 &params);
+
+	iwl_mvm_build_generic_umac_scan_cmd(mvm, cmd, &params);
+
+	uid = iwl_generate_scan_uid(mvm, IWL_UMAC_SCAN_UID_REG_SCAN);
+	mvm->scan_uid[uid_idx] = uid;
+	cmd->uid = cpu_to_le32(uid);
+
+	cmd->ooc_priority = cpu_to_le32(IWL_SCAN_PRIORITY_HIGH);
+
+	flags = iwl_mvm_scan_umac_common_flags(mvm, req->req.n_ssids,
+					       req->req.ssids,
+					       params.passive_fragmented);
+
+	flags |= IWL_UMAC_SCAN_GEN_FLAGS_PASS_ALL;
+
+	cmd->general_flags = cpu_to_le32(flags);
+	cmd->n_channels = req->req.n_channels;
+
+	for (i = 0; i < req->req.n_ssids; i++)
+		ssid_bitmap |= BIT(i);
+
+	iwl_mvm_umac_scan_cfg_channels(mvm, req->req.channels,
+				       req->req.n_channels, ssid_bitmap, cmd);
+
+	sec_part->schedule[0].iter_count = 1;
+	sec_part->delay = 0;
+
+	iwl_mvm_build_unified_scan_probe(mvm, vif, &req->ies, &sec_part->preq,
+		req->req.flags & NL80211_SCAN_FLAG_RANDOM_ADDR ?
+			req->req.mac_addr : NULL,
+		req->req.mac_addr_mask);
+
+	iwl_mvm_scan_fill_ssids(sec_part->direct_scan, req->req.ssids,
+				req->req.n_ssids, 0);
+
+	ret = iwl_mvm_send_cmd(mvm, &hcmd);
+	if (!ret) {
+		IWL_DEBUG_SCAN(mvm,
+			       "Scan request was sent successfully\n");
+	} else {
+		/*
+		 * If the scan failed, it usually means that the FW was unable
+		 * to allocate the time events. Warn on it, but maybe we
+		 * should try to send the command again with different params.
+		 */
+		IWL_ERR(mvm, "Scan failed! ret %d\n", ret);
+	}
+	return ret;
+}
+
+int iwl_mvm_sched_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+			    struct cfg80211_sched_scan_request *req,
+			    struct ieee80211_scan_ies *ies)
+{
+
+	struct iwl_host_cmd hcmd = {
+		.id = SCAN_REQ_UMAC,
+		.len = { iwl_mvm_scan_size(mvm), },
+		.data = { mvm->scan_cmd, },
+		.dataflags = { IWL_HCMD_DFL_NOCOPY, },
+	};
+	struct iwl_scan_req_umac *cmd = mvm->scan_cmd;
+	struct iwl_scan_req_umac_tail *sec_part = (void *)&cmd->data +
+		sizeof(struct iwl_scan_channel_cfg_umac) *
+			mvm->fw->ucode_capa.n_scan_channels;
+	struct iwl_mvm_scan_params params = {};
+	u32 uid, flags;
+	u32 ssid_bitmap = 0;
+	int ret, uid_idx;
+
+	lockdep_assert_held(&mvm->mutex);
+
+	uid_idx = iwl_mvm_find_free_scan_uid(mvm);
+	if (uid_idx >= IWL_MVM_MAX_SIMULTANEOUS_SCANS)
+		return -EBUSY;
+
+	/* we should have failed registration if scan_cmd was NULL */
+	if (WARN_ON(mvm->scan_cmd == NULL))
+		return -ENOMEM;
+
+	if (WARN_ON(req->n_ssids > PROBE_OPTION_MAX ||
+		    ies->common_ie_len + ies->len[NL80211_BAND_2GHZ] +
+		    ies->len[NL80211_BAND_5GHZ] + 24 + 2 >
+		    SCAN_OFFLOAD_PROBE_REQ_SIZE || req->n_channels >
+		    mvm->fw->ucode_capa.n_scan_channels))
+		return -ENOBUFS;
+
+	iwl_mvm_scan_calc_params(mvm, vif, req->n_ssids, req->flags,
+					 &params);
+
+	iwl_mvm_build_generic_umac_scan_cmd(mvm, cmd, &params);
+
+	cmd->flags = cpu_to_le32(IWL_UMAC_SCAN_FLAG_PREEMPTIVE);
+
+	uid = iwl_generate_scan_uid(mvm, IWL_UMAC_SCAN_UID_SCHED_SCAN);
+	mvm->scan_uid[uid_idx] = uid;
+	cmd->uid = cpu_to_le32(uid);
+
+	cmd->ooc_priority = cpu_to_le32(IWL_SCAN_PRIORITY_LOW);
+
+	flags = iwl_mvm_scan_umac_common_flags(mvm, req->n_ssids, req->ssids,
+					       params.passive_fragmented);
+
+	flags |= IWL_UMAC_SCAN_GEN_FLAGS_PERIODIC;
+
+	if (iwl_mvm_scan_pass_all(mvm, req))
+		flags |= IWL_UMAC_SCAN_GEN_FLAGS_PASS_ALL;
+	else
+		flags |= IWL_UMAC_SCAN_GEN_FLAGS_MATCH;
+
+	cmd->general_flags = cpu_to_le32(flags);
+
+	if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_EBS_SUPPORT &&
+	    mvm->last_ebs_successful)
+		cmd->channel_flags = IWL_SCAN_CHANNEL_FLAG_EBS |
+				     IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE |
+				     IWL_SCAN_CHANNEL_FLAG_CACHE_ADD;
+
+	cmd->n_channels = req->n_channels;
+
+	iwl_scan_offload_build_ssid(req, sec_part->direct_scan, &ssid_bitmap,
+				    false);
+
+	/* This API uses bits 0-19 instead of 1-20. */
+	ssid_bitmap = ssid_bitmap >> 1;
+
+	iwl_mvm_umac_scan_cfg_channels(mvm, req->channels, req->n_channels,
+				       ssid_bitmap, cmd);
+
+	sec_part->schedule[0].interval =
+				cpu_to_le16(req->interval / MSEC_PER_SEC);
+	sec_part->schedule[0].iter_count = 0xff;
+
+	sec_part->delay = 0;
+
+	iwl_mvm_build_unified_scan_probe(mvm, vif, ies, &sec_part->preq,
+		req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR ?
+			req->mac_addr : NULL,
+		req->mac_addr_mask);
+
+	ret = iwl_mvm_send_cmd(mvm, &hcmd);
+	if (!ret) {
+		IWL_DEBUG_SCAN(mvm,
+			       "Sched scan request was sent successfully\n");
+	} else {
+		/*
+		 * If the scan failed, it usually means that the FW was unable
+		 * to allocate the time events. Warn on it, but maybe we
+		 * should try to send the command again with different params.
+		 */
+		IWL_ERR(mvm, "Sched scan failed! ret %d\n", ret);
+	}
+	return ret;
+}
+
+int iwl_mvm_rx_umac_scan_complete_notif(struct iwl_mvm *mvm,
+					struct iwl_rx_cmd_buffer *rxb,
+					struct iwl_device_cmd *cmd)
+{
+	struct iwl_rx_packet *pkt = rxb_addr(rxb);
+	struct iwl_umac_scan_complete *notif = (void *)pkt->data;
+	u32 uid = __le32_to_cpu(notif->uid);
+	bool sched = !!(uid & IWL_UMAC_SCAN_UID_SCHED_SCAN);
+	int uid_idx = iwl_mvm_find_scan_uid(mvm, uid);
+
+	/*
+	 * Scan uid may be set to zero in case of scan abort request from above.
+	 */
+	if (uid_idx >= IWL_MVM_MAX_SIMULTANEOUS_SCANS)
+		return 0;
+
+	IWL_DEBUG_SCAN(mvm,
+		       "Scan completed, uid %u type %s, status %s, EBS status %s\n",
+		       uid, sched ? "sched" : "regular",
+		       notif->status == IWL_SCAN_OFFLOAD_COMPLETED ?
+				"completed" : "aborted",
+		       notif->ebs_status == IWL_SCAN_EBS_SUCCESS ?
+				"success" : "failed");
+
+	mvm->last_ebs_successful = !notif->ebs_status;
+	mvm->scan_uid[uid_idx] = 0;
+
+	if (!sched) {
+		ieee80211_scan_completed(mvm->hw,
+					 notif->status ==
+						IWL_SCAN_OFFLOAD_ABORTED);
+		iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
+	} else if (!iwl_mvm_find_scan_type(mvm, IWL_UMAC_SCAN_UID_SCHED_SCAN)) {
+		ieee80211_sched_scan_stopped(mvm->hw);
+	} else {
+		IWL_DEBUG_SCAN(mvm, "Another sched scan is running\n");
+	}
+
+	return 0;
+}
+
+static bool iwl_scan_umac_done_check(struct iwl_notif_wait_data *notif_wait,
+				     struct iwl_rx_packet *pkt, void *data)
+{
+	struct iwl_umac_scan_done *scan_done = data;
+	struct iwl_umac_scan_complete *notif = (void *)pkt->data;
+	u32 uid = __le32_to_cpu(notif->uid);
+	int uid_idx = iwl_mvm_find_scan_uid(scan_done->mvm, uid);
+
+	if (WARN_ON(pkt->hdr.cmd != SCAN_COMPLETE_UMAC))
+		return false;
+
+	if (uid_idx >= IWL_MVM_MAX_SIMULTANEOUS_SCANS)
+		return false;
+
+	/*
+	 * Clear scan uid of scans that was aborted from above and completed
+	 * in FW so the RX handler does nothing.
+	 */
+	scan_done->mvm->scan_uid[uid_idx] = 0;
+
+	return !iwl_mvm_find_scan_type(scan_done->mvm, scan_done->type);
+}
+
+static int iwl_umac_scan_abort_one(struct iwl_mvm *mvm, u32 uid)
+{
+	struct iwl_umac_scan_abort cmd = {
+		.hdr.size = cpu_to_le16(sizeof(struct iwl_umac_scan_abort) -
+					sizeof(struct iwl_mvm_umac_cmd_hdr)),
+		.uid = cpu_to_le32(uid),
+	};
+
+	lockdep_assert_held(&mvm->mutex);
+
+	IWL_DEBUG_SCAN(mvm, "Sending scan abort, uid %u\n", uid);
+
+	return iwl_mvm_send_cmd_pdu(mvm, SCAN_ABORT_UMAC, 0, sizeof(cmd), &cmd);
+}
+
+static int iwl_umac_scan_stop(struct iwl_mvm *mvm,
+			      enum iwl_umac_scan_uid_type type, bool notify)
+{
+	struct iwl_notification_wait wait_scan_done;
+	static const u8 scan_done_notif[] = { SCAN_COMPLETE_UMAC, };
+	struct iwl_umac_scan_done scan_done = {
+		.mvm = mvm,
+		.type = type,
+	};
+	int i, ret = -EIO;
+
+	iwl_init_notification_wait(&mvm->notif_wait, &wait_scan_done,
+				   scan_done_notif,
+				   ARRAY_SIZE(scan_done_notif),
+				   iwl_scan_umac_done_check, &scan_done);
+
+	IWL_DEBUG_SCAN(mvm, "Preparing to stop scan, type %x\n", type);
+
+	for (i = 0; i < IWL_MVM_MAX_SIMULTANEOUS_SCANS; i++) {
+		if (mvm->scan_uid[i] & type) {
+			int err;
+
+			if (iwl_mvm_is_radio_killed(mvm) &&
+			    (type & IWL_UMAC_SCAN_UID_REG_SCAN)) {
+				ieee80211_scan_completed(mvm->hw, true);
+				iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
+				break;
+			}
+
+			err = iwl_umac_scan_abort_one(mvm, mvm->scan_uid[i]);
+			if (!err)
+				ret = 0;
+		}
+	}
+
+	if (ret) {
+		IWL_DEBUG_SCAN(mvm, "Couldn't stop scan\n");
+		iwl_remove_notification(&mvm->notif_wait, &wait_scan_done);
+		return ret;
+	}
+
+	ret = iwl_wait_notification(&mvm->notif_wait, &wait_scan_done, 1 * HZ);
+	if (ret)
+		return ret;
+
+	if (notify) {
+		if (type & IWL_UMAC_SCAN_UID_SCHED_SCAN)
+			ieee80211_sched_scan_stopped(mvm->hw);
+		if (type & IWL_UMAC_SCAN_UID_REG_SCAN) {
+			ieee80211_scan_completed(mvm->hw, true);
+			iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
+		}
+	}
+
+	return ret;
+}
+
+int iwl_mvm_scan_size(struct iwl_mvm *mvm)
+{
+	if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN)
+		return sizeof(struct iwl_scan_req_umac) +
+			sizeof(struct iwl_scan_channel_cfg_umac) *
+				mvm->fw->ucode_capa.n_scan_channels +
+			sizeof(struct iwl_scan_req_umac_tail);
+
+	if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
+		return sizeof(struct iwl_scan_req_unified_lmac) +
+			sizeof(struct iwl_scan_channel_cfg_lmac) *
+				mvm->fw->ucode_capa.n_scan_channels +
+			sizeof(struct iwl_scan_probe_req);
+
+	return sizeof(struct iwl_scan_cmd) +
+		mvm->fw->ucode_capa.max_probe_length +
+			mvm->fw->ucode_capa.n_scan_channels *
+		sizeof(struct iwl_scan_channel);
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c
index dd0dc5b..d86fe43 100644
--- a/drivers/net/wireless/iwlwifi/mvm/sta.c
+++ b/drivers/net/wireless/iwlwifi/mvm/sta.c
@@ -204,6 +204,56 @@
 	return ret;
 }
 
+static int iwl_mvm_tdls_sta_init(struct iwl_mvm *mvm,
+				 struct ieee80211_sta *sta)
+{
+	unsigned long used_hw_queues;
+	struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+	u32 ac;
+
+	lockdep_assert_held(&mvm->mutex);
+
+	used_hw_queues = iwl_mvm_get_used_hw_queues(mvm, NULL);
+
+	/* Find available queues, and allocate them to the ACs */
+	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+		u8 queue = find_first_zero_bit(&used_hw_queues,
+					       mvm->first_agg_queue);
+
+		if (queue >= mvm->first_agg_queue) {
+			IWL_ERR(mvm, "Failed to allocate STA queue\n");
+			return -EBUSY;
+		}
+
+		__set_bit(queue, &used_hw_queues);
+		mvmsta->hw_queue[ac] = queue;
+	}
+
+	/* Found a place for all queues - enable them */
+	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+		iwl_mvm_enable_ac_txq(mvm, mvmsta->hw_queue[ac],
+				      iwl_mvm_ac_to_tx_fifo[ac]);
+		mvmsta->tfd_queue_msk |= BIT(mvmsta->hw_queue[ac]);
+	}
+
+	return 0;
+}
+
+static void iwl_mvm_tdls_sta_deinit(struct iwl_mvm *mvm,
+				    struct ieee80211_sta *sta)
+{
+	struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+	unsigned long sta_msk;
+	int i;
+
+	lockdep_assert_held(&mvm->mutex);
+
+	/* disable the TDLS STA-specific queues */
+	sta_msk = mvmsta->tfd_queue_msk;
+	for_each_set_bit(i, &sta_msk, sizeof(sta_msk))
+		iwl_mvm_disable_txq(mvm, i);
+}
+
 int iwl_mvm_add_sta(struct iwl_mvm *mvm,
 		    struct ieee80211_vif *vif,
 		    struct ieee80211_sta *sta)
@@ -237,9 +287,17 @@
 	atomic_set(&mvm->pending_frames[sta_id], 0);
 	mvm_sta->tid_disable_agg = 0;
 	mvm_sta->tfd_queue_msk = 0;
-	for (i = 0; i < IEEE80211_NUM_ACS; i++)
-		if (vif->hw_queue[i] != IEEE80211_INVAL_HW_QUEUE)
-			mvm_sta->tfd_queue_msk |= BIT(vif->hw_queue[i]);
+
+	/* allocate new queues for a TDLS station */
+	if (sta->tdls) {
+		ret = iwl_mvm_tdls_sta_init(mvm, sta);
+		if (ret)
+			return ret;
+	} else {
+		for (i = 0; i < IEEE80211_NUM_ACS; i++)
+			if (vif->hw_queue[i] != IEEE80211_INVAL_HW_QUEUE)
+				mvm_sta->tfd_queue_msk |= BIT(vif->hw_queue[i]);
+	}
 
 	/* for HW restart - reset everything but the sequence number */
 	for (i = 0; i < IWL_MAX_TID_COUNT; i++) {
@@ -251,7 +309,7 @@
 
 	ret = iwl_mvm_sta_send_to_fw(mvm, sta, false);
 	if (ret)
-		return ret;
+		goto err;
 
 	if (vif->type == NL80211_IFTYPE_STATION) {
 		if (!sta->tdls) {
@@ -265,6 +323,10 @@
 	rcu_assign_pointer(mvm->fw_id_to_mac_id[sta_id], sta);
 
 	return 0;
+
+err:
+	iwl_mvm_tdls_sta_deinit(mvm, sta);
+	return ret;
 }
 
 int iwl_mvm_update_sta(struct iwl_mvm *mvm,
@@ -398,6 +460,17 @@
 		}
 		RCU_INIT_POINTER(mvm->fw_id_to_mac_id[sta_id], NULL);
 		clear_bit(sta_id, mvm->sta_drained);
+
+		if (mvm->tfd_drained[sta_id]) {
+			unsigned long i, msk = mvm->tfd_drained[sta_id];
+
+			for_each_set_bit(i, &msk, sizeof(msk))
+				iwl_mvm_disable_txq(mvm, i);
+
+			mvm->tfd_drained[sta_id] = 0;
+			IWL_DEBUG_TDLS(mvm, "Drained sta %d, with queues %ld\n",
+				       sta_id, msk);
+		}
 	}
 
 	mutex_unlock(&mvm->mutex);
@@ -431,6 +504,15 @@
 	}
 
 	/*
+	 * This shouldn't happen - the TDLS channel switch should be canceled
+	 * before the STA is removed.
+	 */
+	if (WARN_ON_ONCE(mvm->tdls_cs.peer.sta_id == mvm_sta->sta_id)) {
+		mvm->tdls_cs.peer.sta_id = IWL_MVM_STATION_COUNT;
+		cancel_delayed_work(&mvm->tdls_cs.dwork);
+	}
+
+	/*
 	 * Make sure that the tx response code sees the station as -EBUSY and
 	 * calls the drain worker.
 	 */
@@ -443,9 +525,22 @@
 		rcu_assign_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id],
 				   ERR_PTR(-EBUSY));
 		spin_unlock_bh(&mvm_sta->lock);
+
+		/* disable TDLS sta queues on drain complete */
+		if (sta->tdls) {
+			mvm->tfd_drained[mvm_sta->sta_id] =
+							mvm_sta->tfd_queue_msk;
+			IWL_DEBUG_TDLS(mvm, "Draining TDLS sta %d\n",
+				       mvm_sta->sta_id);
+		}
+
 		ret = iwl_mvm_drain_sta(mvm, mvm_sta, true);
 	} else {
 		spin_unlock_bh(&mvm_sta->lock);
+
+		if (sta->tdls)
+			iwl_mvm_tdls_sta_deinit(mvm, sta);
+
 		ret = iwl_mvm_rm_sta_common(mvm, mvm_sta->sta_id);
 		RCU_INIT_POINTER(mvm->fw_id_to_mac_id[mvm_sta->sta_id], NULL);
 	}
@@ -1071,15 +1166,16 @@
 
 static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm,
 				struct iwl_mvm_sta *mvm_sta,
-				struct ieee80211_key_conf *keyconf,
-				u8 sta_id, u32 tkip_iv32, u16 *tkip_p1k,
-				u32 cmd_flags)
+				struct ieee80211_key_conf *keyconf, bool mcast,
+				u32 tkip_iv32, u16 *tkip_p1k, u32 cmd_flags)
 {
 	struct iwl_mvm_add_sta_key_cmd cmd = {};
 	__le16 key_flags;
-	int ret, status;
+	int ret;
+	u32 status;
 	u16 keyidx;
 	int i;
+	u8 sta_id = mvm_sta->sta_id;
 
 	keyidx = (keyconf->keyidx << STA_KEY_FLG_KEYID_POS) &
 		 STA_KEY_FLG_KEYID_MSK;
@@ -1098,12 +1194,18 @@
 		key_flags |= cpu_to_le16(STA_KEY_FLG_CCM);
 		memcpy(cmd.key, keyconf->key, keyconf->keylen);
 		break;
+	case WLAN_CIPHER_SUITE_WEP104:
+		key_flags |= cpu_to_le16(STA_KEY_FLG_WEP_13BYTES);
+	case WLAN_CIPHER_SUITE_WEP40:
+		key_flags |= cpu_to_le16(STA_KEY_FLG_WEP);
+		memcpy(cmd.key + 3, keyconf->key, keyconf->keylen);
+		break;
 	default:
 		key_flags |= cpu_to_le16(STA_KEY_FLG_EXT);
 		memcpy(cmd.key, keyconf->key, keyconf->keylen);
 	}
 
-	if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE))
+	if (mcast)
 		key_flags |= cpu_to_le16(STA_KEY_MULTICAST);
 
 	cmd.key_offset = keyconf->hw_key_idx;
@@ -1195,17 +1297,88 @@
 	return NULL;
 }
 
+static int __iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
+				 struct ieee80211_vif *vif,
+				 struct ieee80211_sta *sta,
+				 struct ieee80211_key_conf *keyconf,
+				 bool mcast)
+{
+	struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
+	int ret;
+	const u8 *addr;
+	struct ieee80211_key_seq seq;
+	u16 p1k[5];
+
+	switch (keyconf->cipher) {
+	case WLAN_CIPHER_SUITE_TKIP:
+		addr = iwl_mvm_get_mac_addr(mvm, vif, sta);
+		/* get phase 1 key from mac80211 */
+		ieee80211_get_key_rx_seq(keyconf, 0, &seq);
+		ieee80211_get_tkip_rx_p1k(keyconf, addr, seq.tkip.iv32, p1k);
+		ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast,
+					   seq.tkip.iv32, p1k, 0);
+		break;
+	case WLAN_CIPHER_SUITE_CCMP:
+	case WLAN_CIPHER_SUITE_WEP40:
+	case WLAN_CIPHER_SUITE_WEP104:
+		ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast,
+					   0, NULL, 0);
+		break;
+	default:
+		ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast,
+					   0, NULL, 0);
+	}
+
+	return ret;
+}
+
+static int __iwl_mvm_remove_sta_key(struct iwl_mvm *mvm, u8 sta_id,
+				    struct ieee80211_key_conf *keyconf,
+				    bool mcast)
+{
+	struct iwl_mvm_add_sta_key_cmd cmd = {};
+	__le16 key_flags;
+	int ret;
+	u32 status;
+
+	key_flags = cpu_to_le16((keyconf->keyidx << STA_KEY_FLG_KEYID_POS) &
+				 STA_KEY_FLG_KEYID_MSK);
+	key_flags |= cpu_to_le16(STA_KEY_FLG_NO_ENC | STA_KEY_FLG_WEP_KEY_MAP);
+	key_flags |= cpu_to_le16(STA_KEY_NOT_VALID);
+
+	if (mcast)
+		key_flags |= cpu_to_le16(STA_KEY_MULTICAST);
+
+	cmd.key_flags = key_flags;
+	cmd.key_offset = keyconf->hw_key_idx;
+	cmd.sta_id = sta_id;
+
+	status = ADD_STA_SUCCESS;
+	ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA_KEY, sizeof(cmd),
+					  &cmd, &status);
+
+	switch (status) {
+	case ADD_STA_SUCCESS:
+		IWL_DEBUG_WEP(mvm, "MODIFY_STA: remove sta key passed\n");
+		break;
+	default:
+		ret = -EIO;
+		IWL_ERR(mvm, "MODIFY_STA: remove sta key failed\n");
+		break;
+	}
+
+	return ret;
+}
+
 int iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
 			struct ieee80211_vif *vif,
 			struct ieee80211_sta *sta,
 			struct ieee80211_key_conf *keyconf,
 			bool have_key_offset)
 {
-	struct iwl_mvm_sta *mvm_sta;
+	bool mcast = !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE);
+	u8 sta_id;
 	int ret;
-	u8 *addr, sta_id;
-	struct ieee80211_key_seq seq;
-	u16 p1k[5];
 
 	lockdep_assert_held(&mvm->mutex);
 
@@ -1234,8 +1407,7 @@
 		}
 	}
 
-	mvm_sta = (struct iwl_mvm_sta *)sta->drv_priv;
-	if (WARN_ON_ONCE(mvm_sta->vif != vif))
+	if (WARN_ON_ONCE(iwl_mvm_sta_from_mac80211(sta)->vif != vif))
 		return -EINVAL;
 
 	if (!have_key_offset) {
@@ -1249,26 +1421,26 @@
 			return -ENOSPC;
 	}
 
-	switch (keyconf->cipher) {
-	case WLAN_CIPHER_SUITE_TKIP:
-		addr = iwl_mvm_get_mac_addr(mvm, vif, sta);
-		/* get phase 1 key from mac80211 */
-		ieee80211_get_key_rx_seq(keyconf, 0, &seq);
-		ieee80211_get_tkip_rx_p1k(keyconf, addr, seq.tkip.iv32, p1k);
-		ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, sta_id,
-					   seq.tkip.iv32, p1k, 0);
-		break;
-	case WLAN_CIPHER_SUITE_CCMP:
-		ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, sta_id,
-					   0, NULL, 0);
-		break;
-	default:
-		ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf,
-					   sta_id, 0, NULL, 0);
+	ret = __iwl_mvm_set_sta_key(mvm, vif, sta, keyconf, mcast);
+	if (ret) {
+		__clear_bit(keyconf->hw_key_idx, mvm->fw_key_table);
+		goto end;
 	}
 
-	if (ret)
-		__clear_bit(keyconf->hw_key_idx, mvm->fw_key_table);
+	/*
+	 * For WEP, the same key is used for multicast and unicast. Upload it
+	 * again, using the same key offset, and now pointing the other one
+	 * to the same key slot (offset).
+	 * If this fails, remove the original as well.
+	 */
+	if (keyconf->cipher == WLAN_CIPHER_SUITE_WEP40 ||
+	    keyconf->cipher == WLAN_CIPHER_SUITE_WEP104) {
+		ret = __iwl_mvm_set_sta_key(mvm, vif, sta, keyconf, !mcast);
+		if (ret) {
+			__clear_bit(keyconf->hw_key_idx, mvm->fw_key_table);
+			__iwl_mvm_remove_sta_key(mvm, sta_id, keyconf, mcast);
+		}
+	}
 
 end:
 	IWL_DEBUG_WEP(mvm, "key: cipher=%x len=%d idx=%d sta=%pM ret=%d\n",
@@ -1282,11 +1454,9 @@
 			   struct ieee80211_sta *sta,
 			   struct ieee80211_key_conf *keyconf)
 {
-	struct iwl_mvm_sta *mvm_sta;
-	struct iwl_mvm_add_sta_key_cmd cmd = {};
-	__le16 key_flags;
-	int ret, status;
+	bool mcast = !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE);
 	u8 sta_id;
+	int ret;
 
 	lockdep_assert_held(&mvm->mutex);
 
@@ -1299,8 +1469,7 @@
 	if (keyconf->cipher == WLAN_CIPHER_SUITE_AES_CMAC)
 		return iwl_mvm_send_sta_igtk(mvm, keyconf, sta_id, true);
 
-	ret = __test_and_clear_bit(keyconf->hw_key_idx, mvm->fw_key_table);
-	if (!ret) {
+	if (!__test_and_clear_bit(keyconf->hw_key_idx, mvm->fw_key_table)) {
 		IWL_ERR(mvm, "offset %d not used in fw key table.\n",
 			keyconf->hw_key_idx);
 		return -ENOENT;
@@ -1326,35 +1495,17 @@
 		}
 	}
 
-	mvm_sta = (struct iwl_mvm_sta *)sta->drv_priv;
-	if (WARN_ON_ONCE(mvm_sta->vif != vif))
+	if (WARN_ON_ONCE(iwl_mvm_sta_from_mac80211(sta)->vif != vif))
 		return -EINVAL;
 
-	key_flags = cpu_to_le16((keyconf->keyidx << STA_KEY_FLG_KEYID_POS) &
-				 STA_KEY_FLG_KEYID_MSK);
-	key_flags |= cpu_to_le16(STA_KEY_FLG_NO_ENC | STA_KEY_FLG_WEP_KEY_MAP);
-	key_flags |= cpu_to_le16(STA_KEY_NOT_VALID);
+	ret = __iwl_mvm_remove_sta_key(mvm, sta_id, keyconf, mcast);
+	if (ret)
+		return ret;
 
-	if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE))
-		key_flags |= cpu_to_le16(STA_KEY_MULTICAST);
-
-	cmd.key_flags = key_flags;
-	cmd.key_offset = keyconf->hw_key_idx;
-	cmd.sta_id = sta_id;
-
-	status = ADD_STA_SUCCESS;
-	ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA_KEY, sizeof(cmd),
-					  &cmd, &status);
-
-	switch (status) {
-	case ADD_STA_SUCCESS:
-		IWL_DEBUG_WEP(mvm, "MODIFY_STA: remove sta key passed\n");
-		break;
-	default:
-		ret = -EIO;
-		IWL_ERR(mvm, "MODIFY_STA: remove sta key failed\n");
-		break;
-	}
+	/* delete WEP key twice to get rid of (now useless) offset */
+	if (keyconf->cipher == WLAN_CIPHER_SUITE_WEP40 ||
+	    keyconf->cipher == WLAN_CIPHER_SUITE_WEP104)
+		ret = __iwl_mvm_remove_sta_key(mvm, sta_id, keyconf, !mcast);
 
 	return ret;
 }
@@ -1367,6 +1518,7 @@
 {
 	struct iwl_mvm_sta *mvm_sta;
 	u8 sta_id = iwl_mvm_get_key_sta_id(vif, sta);
+	bool mcast = !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE);
 
 	if (WARN_ON_ONCE(sta_id == IWL_MVM_STATION_COUNT))
 		return;
@@ -1381,8 +1533,8 @@
 		}
 	}
 
-	mvm_sta = (void *)sta->drv_priv;
-	iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, sta_id,
+	mvm_sta = iwl_mvm_sta_from_mac80211(sta);
+	iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast,
 			     iv32, phase1key, CMD_ASYNC);
 	rcu_read_unlock();
 }
@@ -1580,3 +1732,18 @@
 		iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, disable);
 	}
 }
+
+void iwl_mvm_csa_client_absent(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+	struct iwl_mvm_sta *mvmsta;
+
+	rcu_read_lock();
+
+	mvmsta = iwl_mvm_sta_from_staid_rcu(mvm, mvmvif->ap_sta_id);
+
+	if (!WARN_ON(!mvmsta))
+		iwl_mvm_sta_modify_disable_tx(mvm, mvmsta, true);
+
+	rcu_read_unlock();
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.h b/drivers/net/wireless/iwlwifi/mvm/sta.h
index d9c0d7b..d8f48975 100644
--- a/drivers/net/wireless/iwlwifi/mvm/sta.h
+++ b/drivers/net/wireless/iwlwifi/mvm/sta.h
@@ -264,6 +264,7 @@
  *	the first packet to be sent in legacy HW queue in Tx AGG stop flow.
  *	Basically when next_reclaimed reaches ssn, we can tell mac80211 that
  *	we are ready to finish the Tx AGG stop / start flow.
+ * @tx_time: medium time consumed by this A-MPDU
  */
 struct iwl_mvm_tid_data {
 	u16 seq_number;
@@ -274,6 +275,7 @@
 	enum iwl_mvm_agg_state state;
 	u16 txq_id;
 	u16 ssn;
+	u16 tx_time;
 };
 
 static inline u16 iwl_mvm_tid_queued(struct iwl_mvm_tid_data *tid_data)
@@ -286,6 +288,7 @@
  * struct iwl_mvm_sta - representation of a station in the driver
  * @sta_id: the index of the station in the fw (will be replaced by id_n_color)
  * @tfd_queue_msk: the tfd queues used by the station
+ * @hw_queue: per-AC mapping of the TFD queues used by station
  * @mac_id_n_color: the MAC context this station is linked to
  * @tid_disable_agg: bitmap: if bit(tid) is set, the fw won't send ampdus for
  *	tid.
@@ -309,6 +312,7 @@
 struct iwl_mvm_sta {
 	u32 sta_id;
 	u32 tfd_queue_msk;
+	u8 hw_queue[IEEE80211_NUM_ACS];
 	u32 mac_id_n_color;
 	u16 tid_disable_agg;
 	u8 max_agg_bufsize;
@@ -418,5 +422,6 @@
 void iwl_mvm_modify_all_sta_disable_tx(struct iwl_mvm *mvm,
 				       struct iwl_mvm_vif *mvmvif,
 				       bool disable);
+void iwl_mvm_csa_client_absent(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
 
 #endif /* __sta_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/tdls.c b/drivers/net/wireless/iwlwifi/mvm/tdls.c
index 66c82df..c0e00ba 100644
--- a/drivers/net/wireless/iwlwifi/mvm/tdls.c
+++ b/drivers/net/wireless/iwlwifi/mvm/tdls.c
@@ -61,9 +61,13 @@
  *
  *****************************************************************************/
 
+#include <linux/etherdevice.h>
 #include "mvm.h"
 #include "time-event.h"
 
+#define TU_TO_US(x) (x * 1024)
+#define TU_TO_MS(x) (TU_TO_US(x) / 1000)
+
 void iwl_mvm_teardown_tdls_peers(struct iwl_mvm *mvm)
 {
 	struct ieee80211_sta *sta;
@@ -113,17 +117,85 @@
 	return count;
 }
 
+static void iwl_mvm_tdls_config(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+	struct iwl_rx_packet *pkt;
+	struct iwl_tdls_config_res *resp;
+	struct iwl_tdls_config_cmd tdls_cfg_cmd = {};
+	struct iwl_host_cmd cmd = {
+		.id = TDLS_CONFIG_CMD,
+		.flags = CMD_WANT_SKB,
+		.data = { &tdls_cfg_cmd, },
+		.len = { sizeof(struct iwl_tdls_config_cmd), },
+	};
+	struct ieee80211_sta *sta;
+	int ret, i, cnt;
+	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+	lockdep_assert_held(&mvm->mutex);
+
+	tdls_cfg_cmd.id_and_color =
+		cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
+	tdls_cfg_cmd.tx_to_ap_tid = IWL_MVM_TDLS_FW_TID;
+	tdls_cfg_cmd.tx_to_ap_ssn = cpu_to_le16(0); /* not used for now */
+
+	/* for now the Tx cmd is empty and unused */
+
+	/* populate TDLS peer data */
+	cnt = 0;
+	for (i = 0; i < IWL_MVM_STATION_COUNT; i++) {
+		sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
+						lockdep_is_held(&mvm->mutex));
+		if (IS_ERR_OR_NULL(sta) || !sta->tdls)
+			continue;
+
+		tdls_cfg_cmd.sta_info[cnt].sta_id = i;
+		tdls_cfg_cmd.sta_info[cnt].tx_to_peer_tid =
+							IWL_MVM_TDLS_FW_TID;
+		tdls_cfg_cmd.sta_info[cnt].tx_to_peer_ssn = cpu_to_le16(0);
+		tdls_cfg_cmd.sta_info[cnt].is_initiator =
+				cpu_to_le32(sta->tdls_initiator ? 1 : 0);
+
+		cnt++;
+	}
+
+	tdls_cfg_cmd.tdls_peer_count = cnt;
+	IWL_DEBUG_TDLS(mvm, "send TDLS config to FW for %d peers\n", cnt);
+
+	ret = iwl_mvm_send_cmd(mvm, &cmd);
+	if (WARN_ON_ONCE(ret))
+		return;
+
+	pkt = cmd.resp_pkt;
+	if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) {
+		IWL_ERR(mvm, "Bad return from TDLS_CONFIG_COMMAND (0x%08X)\n",
+			pkt->hdr.flags);
+		goto exit;
+	}
+
+	if (WARN_ON_ONCE(iwl_rx_packet_payload_len(pkt) != sizeof(*resp)))
+		goto exit;
+
+	/* we don't really care about the response at this point */
+
+exit:
+	iwl_free_resp(&cmd);
+}
+
 void iwl_mvm_recalc_tdls_state(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
 			       bool sta_added)
 {
 	int tdls_sta_cnt = iwl_mvm_tdls_sta_count(mvm, vif);
 
-	/*
-	 * Disable ps when the first TDLS sta is added and re-enable it
-	 * when the last TDLS sta is removed
-	 */
-	if ((tdls_sta_cnt == 1 && sta_added) ||
-	    (tdls_sta_cnt == 0 && !sta_added))
+	/* when the first peer joins, send a power update first */
+	if (tdls_sta_cnt == 1 && sta_added)
+		iwl_mvm_power_update_mac(mvm);
+
+	/* configure the FW with TDLS peer info */
+	iwl_mvm_tdls_config(mvm, vif);
+
+	/* when the last peer leaves, send a power update last */
+	if (tdls_sta_cnt == 0 && !sta_added)
 		iwl_mvm_power_update_mac(mvm);
 }
 
@@ -147,3 +219,488 @@
 
 	iwl_mvm_unref(mvm, IWL_MVM_REF_PROTECT_TDLS);
 }
+
+static const char *
+iwl_mvm_tdls_cs_state_str(enum iwl_mvm_tdls_cs_state state)
+{
+	switch (state) {
+	case IWL_MVM_TDLS_SW_IDLE:
+		return "IDLE";
+	case IWL_MVM_TDLS_SW_REQ_SENT:
+		return "REQ SENT";
+	case IWL_MVM_TDLS_SW_REQ_RCVD:
+		return "REQ RECEIVED";
+	case IWL_MVM_TDLS_SW_ACTIVE:
+		return "ACTIVE";
+	}
+
+	return NULL;
+}
+
+static void iwl_mvm_tdls_update_cs_state(struct iwl_mvm *mvm,
+					 enum iwl_mvm_tdls_cs_state state)
+{
+	if (mvm->tdls_cs.state == state)
+		return;
+
+	IWL_DEBUG_TDLS(mvm, "TDLS channel switch state: %s -> %s\n",
+		       iwl_mvm_tdls_cs_state_str(mvm->tdls_cs.state),
+		       iwl_mvm_tdls_cs_state_str(state));
+	mvm->tdls_cs.state = state;
+
+	if (state == IWL_MVM_TDLS_SW_IDLE)
+		mvm->tdls_cs.cur_sta_id = IWL_MVM_STATION_COUNT;
+}
+
+int iwl_mvm_rx_tdls_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+			  struct iwl_device_cmd *cmd)
+{
+	struct iwl_rx_packet *pkt = rxb_addr(rxb);
+	struct iwl_tdls_channel_switch_notif *notif = (void *)pkt->data;
+	struct ieee80211_sta *sta;
+	unsigned int delay;
+	struct iwl_mvm_sta *mvmsta;
+	struct ieee80211_vif *vif;
+	u32 sta_id = le32_to_cpu(notif->sta_id);
+
+	lockdep_assert_held(&mvm->mutex);
+
+	/* can fail sometimes */
+	if (!le32_to_cpu(notif->status)) {
+		iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_IDLE);
+		goto out;
+	}
+
+	if (WARN_ON(sta_id >= IWL_MVM_STATION_COUNT))
+		goto out;
+
+	sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
+					lockdep_is_held(&mvm->mutex));
+	/* the station may not be here, but if it is, it must be a TDLS peer */
+	if (IS_ERR_OR_NULL(sta) || WARN_ON(!sta->tdls))
+		goto out;
+
+	mvmsta = iwl_mvm_sta_from_mac80211(sta);
+	vif = mvmsta->vif;
+
+	/*
+	 * Update state and possibly switch again after this is over (DTIM).
+	 * Also convert TU to msec.
+	 */
+	delay = TU_TO_MS(vif->bss_conf.dtim_period * vif->bss_conf.beacon_int);
+	mod_delayed_work(system_wq, &mvm->tdls_cs.dwork,
+			 msecs_to_jiffies(delay));
+
+	iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_ACTIVE);
+
+out:
+	return 0;
+}
+
+static int
+iwl_mvm_tdls_check_action(struct iwl_mvm *mvm,
+			  enum iwl_tdls_channel_switch_type type,
+			  const u8 *peer, bool peer_initiator)
+{
+	bool same_peer = false;
+	int ret = 0;
+
+	/* get the existing peer if it's there */
+	if (mvm->tdls_cs.state != IWL_MVM_TDLS_SW_IDLE &&
+	    mvm->tdls_cs.cur_sta_id != IWL_MVM_STATION_COUNT) {
+		struct ieee80211_sta *sta = rcu_dereference_protected(
+				mvm->fw_id_to_mac_id[mvm->tdls_cs.cur_sta_id],
+				lockdep_is_held(&mvm->mutex));
+		if (!IS_ERR_OR_NULL(sta))
+			same_peer = ether_addr_equal(peer, sta->addr);
+	}
+
+	switch (mvm->tdls_cs.state) {
+	case IWL_MVM_TDLS_SW_IDLE:
+		/*
+		 * might be spurious packet from the peer after the switch is
+		 * already done
+		 */
+		if (type == TDLS_MOVE_CH)
+			ret = -EINVAL;
+		break;
+	case IWL_MVM_TDLS_SW_REQ_SENT:
+		/*
+		 * We received a ch-switch request while an outgoing one is
+		 * pending. Allow it to proceed if the other peer is the same
+		 * one we sent to, and we are not the link initiator.
+		 */
+		if (type == TDLS_SEND_CHAN_SW_RESP_AND_MOVE_CH) {
+			if (!same_peer)
+				ret = -EBUSY;
+			else if (!peer_initiator) /* we are the initiator */
+				ret = -EBUSY;
+		}
+		break;
+	case IWL_MVM_TDLS_SW_REQ_RCVD:
+		/* as above, allow the link initiator to proceed */
+		if (type == TDLS_SEND_CHAN_SW_REQ) {
+			if (!same_peer)
+				ret = -EBUSY;
+			else if (peer_initiator) /* they are the initiator */
+				ret = -EBUSY;
+		} else if (type == TDLS_MOVE_CH) {
+			ret = -EINVAL;
+		}
+		break;
+	case IWL_MVM_TDLS_SW_ACTIVE:
+		/* we don't allow initiations during active channel switch */
+		if (type == TDLS_SEND_CHAN_SW_REQ)
+			ret = -EINVAL;
+		break;
+	}
+
+	if (ret)
+		IWL_DEBUG_TDLS(mvm,
+			       "Invalid TDLS action %d state %d peer %pM same_peer %d initiator %d\n",
+			       type, mvm->tdls_cs.state, peer, same_peer,
+			       peer_initiator);
+
+	return ret;
+}
+
+static int
+iwl_mvm_tdls_config_channel_switch(struct iwl_mvm *mvm,
+				   struct ieee80211_vif *vif,
+				   enum iwl_tdls_channel_switch_type type,
+				   const u8 *peer, bool peer_initiator,
+				   u8 oper_class,
+				   struct cfg80211_chan_def *chandef,
+				   u32 timestamp, u16 switch_time,
+				   u16 switch_timeout, struct sk_buff *skb,
+				   u32 ch_sw_tm_ie)
+{
+	struct ieee80211_sta *sta;
+	struct iwl_mvm_sta *mvmsta;
+	struct ieee80211_tx_info *info;
+	struct ieee80211_hdr *hdr;
+	struct iwl_tdls_channel_switch_cmd cmd = {0};
+	int ret;
+
+	lockdep_assert_held(&mvm->mutex);
+
+	ret = iwl_mvm_tdls_check_action(mvm, type, peer, peer_initiator);
+	if (ret)
+		return ret;
+
+	if (!skb || WARN_ON(skb->len > IWL_TDLS_CH_SW_FRAME_MAX_SIZE)) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	cmd.switch_type = type;
+	cmd.timing.frame_timestamp = cpu_to_le32(timestamp);
+	cmd.timing.switch_time = cpu_to_le32(switch_time);
+	cmd.timing.switch_timeout = cpu_to_le32(switch_timeout);
+
+	rcu_read_lock();
+	sta = ieee80211_find_sta(vif, peer);
+	if (!sta) {
+		rcu_read_unlock();
+		ret = -ENOENT;
+		goto out;
+	}
+	mvmsta = iwl_mvm_sta_from_mac80211(sta);
+	cmd.peer_sta_id = cpu_to_le32(mvmsta->sta_id);
+
+	if (!chandef) {
+		if (mvm->tdls_cs.state == IWL_MVM_TDLS_SW_REQ_SENT &&
+		    mvm->tdls_cs.peer.chandef.chan) {
+			/* actually moving to the channel */
+			chandef = &mvm->tdls_cs.peer.chandef;
+		} else if (mvm->tdls_cs.state == IWL_MVM_TDLS_SW_ACTIVE &&
+			   type == TDLS_MOVE_CH) {
+			/* we need to return to base channel */
+			struct ieee80211_chanctx_conf *chanctx =
+					rcu_dereference(vif->chanctx_conf);
+
+			if (WARN_ON_ONCE(!chanctx)) {
+				rcu_read_unlock();
+				goto out;
+			}
+
+			chandef = &chanctx->def;
+		}
+	}
+
+	if (chandef) {
+		cmd.ci.band = (chandef->chan->band == IEEE80211_BAND_2GHZ ?
+			       PHY_BAND_24 : PHY_BAND_5);
+		cmd.ci.channel = chandef->chan->hw_value;
+		cmd.ci.width = iwl_mvm_get_channel_width(chandef);
+		cmd.ci.ctrl_pos = iwl_mvm_get_ctrl_pos(chandef);
+	}
+
+	/* keep quota calculation simple for now - 50% of DTIM for TDLS */
+	cmd.timing.max_offchan_duration =
+			cpu_to_le32(TU_TO_US(vif->bss_conf.dtim_period *
+					     vif->bss_conf.beacon_int) / 2);
+
+	/* Switch time is the first element in the switch-timing IE. */
+	cmd.frame.switch_time_offset = cpu_to_le32(ch_sw_tm_ie + 2);
+
+	info = IEEE80211_SKB_CB(skb);
+	if (info->control.hw_key)
+		iwl_mvm_set_tx_cmd_crypto(mvm, info, &cmd.frame.tx_cmd, skb);
+
+	iwl_mvm_set_tx_cmd(mvm, skb, &cmd.frame.tx_cmd, info,
+			   mvmsta->sta_id);
+
+	hdr = (void *)skb->data;
+	iwl_mvm_set_tx_cmd_rate(mvm, &cmd.frame.tx_cmd, info, sta,
+				hdr->frame_control);
+	rcu_read_unlock();
+
+	memcpy(cmd.frame.data, skb->data, skb->len);
+
+	ret = iwl_mvm_send_cmd_pdu(mvm, TDLS_CHANNEL_SWITCH_CMD, 0,
+				   sizeof(cmd), &cmd);
+	if (ret) {
+		IWL_ERR(mvm, "Failed to send TDLS_CHANNEL_SWITCH cmd: %d\n",
+			ret);
+		goto out;
+	}
+
+	/* channel switch has started, update state */
+	if (type != TDLS_MOVE_CH) {
+		mvm->tdls_cs.cur_sta_id = mvmsta->sta_id;
+		iwl_mvm_tdls_update_cs_state(mvm,
+					     type == TDLS_SEND_CHAN_SW_REQ ?
+					     IWL_MVM_TDLS_SW_REQ_SENT :
+					     IWL_MVM_TDLS_SW_REQ_RCVD);
+	}
+
+out:
+
+	/* channel switch failed - we are idle */
+	if (ret)
+		iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_IDLE);
+
+	return ret;
+}
+
+void iwl_mvm_tdls_ch_switch_work(struct work_struct *work)
+{
+	struct iwl_mvm *mvm;
+	struct ieee80211_sta *sta;
+	struct iwl_mvm_sta *mvmsta;
+	struct ieee80211_vif *vif;
+	unsigned int delay;
+	int ret;
+
+	mvm = container_of(work, struct iwl_mvm, tdls_cs.dwork.work);
+	mutex_lock(&mvm->mutex);
+
+	/* called after an active channel switch has finished or timed-out */
+	iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_IDLE);
+
+	/* station might be gone, in that case do nothing */
+	if (mvm->tdls_cs.peer.sta_id == IWL_MVM_STATION_COUNT)
+		goto out;
+
+	sta = rcu_dereference_protected(
+				mvm->fw_id_to_mac_id[mvm->tdls_cs.peer.sta_id],
+				lockdep_is_held(&mvm->mutex));
+	/* the station may not be here, but if it is, it must be a TDLS peer */
+	if (!sta || IS_ERR(sta) || WARN_ON(!sta->tdls))
+		goto out;
+
+	mvmsta = iwl_mvm_sta_from_mac80211(sta);
+	vif = mvmsta->vif;
+	ret = iwl_mvm_tdls_config_channel_switch(mvm, vif,
+						 TDLS_SEND_CHAN_SW_REQ,
+						 sta->addr,
+						 mvm->tdls_cs.peer.initiator,
+						 mvm->tdls_cs.peer.op_class,
+						 &mvm->tdls_cs.peer.chandef,
+						 0, 0, 0,
+						 mvm->tdls_cs.peer.skb,
+						 mvm->tdls_cs.peer.ch_sw_tm_ie);
+	if (ret)
+		IWL_ERR(mvm, "Not sending TDLS channel switch: %d\n", ret);
+
+	/* retry after a DTIM if we failed sending now */
+	delay = TU_TO_MS(vif->bss_conf.dtim_period * vif->bss_conf.beacon_int);
+	queue_delayed_work(system_wq, &mvm->tdls_cs.dwork,
+			   msecs_to_jiffies(delay));
+out:
+	mutex_unlock(&mvm->mutex);
+}
+
+int
+iwl_mvm_tdls_channel_switch(struct ieee80211_hw *hw,
+			    struct ieee80211_vif *vif,
+			    struct ieee80211_sta *sta, u8 oper_class,
+			    struct cfg80211_chan_def *chandef,
+			    struct sk_buff *tmpl_skb, u32 ch_sw_tm_ie)
+{
+	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+	struct iwl_mvm_sta *mvmsta;
+	unsigned int delay;
+	int ret;
+
+	mutex_lock(&mvm->mutex);
+
+	IWL_DEBUG_TDLS(mvm, "TDLS channel switch with %pM ch %d width %d\n",
+		       sta->addr, chandef->chan->center_freq, chandef->width);
+
+	/* we only support a single peer for channel switching */
+	if (mvm->tdls_cs.peer.sta_id != IWL_MVM_STATION_COUNT) {
+		IWL_DEBUG_TDLS(mvm,
+			       "Existing peer. Can't start switch with %pM\n",
+			       sta->addr);
+		ret = -EBUSY;
+		goto out;
+	}
+
+	ret = iwl_mvm_tdls_config_channel_switch(mvm, vif,
+						 TDLS_SEND_CHAN_SW_REQ,
+						 sta->addr, sta->tdls_initiator,
+						 oper_class, chandef, 0, 0, 0,
+						 tmpl_skb, ch_sw_tm_ie);
+	if (ret)
+		goto out;
+
+	/*
+	 * Mark the peer as "in tdls switch" for this vif. We only allow a
+	 * single such peer per vif.
+	 */
+	mvm->tdls_cs.peer.skb = skb_copy(tmpl_skb, GFP_KERNEL);
+	if (!mvm->tdls_cs.peer.skb) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	mvmsta = iwl_mvm_sta_from_mac80211(sta);
+	mvm->tdls_cs.peer.sta_id = mvmsta->sta_id;
+	mvm->tdls_cs.peer.chandef = *chandef;
+	mvm->tdls_cs.peer.initiator = sta->tdls_initiator;
+	mvm->tdls_cs.peer.op_class = oper_class;
+	mvm->tdls_cs.peer.ch_sw_tm_ie = ch_sw_tm_ie;
+
+	/*
+	 * Wait for 2 DTIM periods before attempting the next switch. The next
+	 * switch will be made sooner if the current one completes before that.
+	 */
+	delay = 2 * TU_TO_MS(vif->bss_conf.dtim_period *
+			     vif->bss_conf.beacon_int);
+	mod_delayed_work(system_wq, &mvm->tdls_cs.dwork,
+			 msecs_to_jiffies(delay));
+
+out:
+	mutex_unlock(&mvm->mutex);
+	return ret;
+}
+
+void iwl_mvm_tdls_cancel_channel_switch(struct ieee80211_hw *hw,
+					struct ieee80211_vif *vif,
+					struct ieee80211_sta *sta)
+{
+	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+	struct ieee80211_sta *cur_sta;
+	bool wait_for_phy = false;
+
+	mutex_lock(&mvm->mutex);
+
+	IWL_DEBUG_TDLS(mvm, "TDLS cancel channel switch with %pM\n", sta->addr);
+
+	/* we only support a single peer for channel switching */
+	if (mvm->tdls_cs.peer.sta_id == IWL_MVM_STATION_COUNT) {
+		IWL_DEBUG_TDLS(mvm, "No ch switch peer - %pM\n", sta->addr);
+		goto out;
+	}
+
+	cur_sta = rcu_dereference_protected(
+				mvm->fw_id_to_mac_id[mvm->tdls_cs.peer.sta_id],
+				lockdep_is_held(&mvm->mutex));
+	/* make sure it's the same peer */
+	if (cur_sta != sta)
+		goto out;
+
+	/*
+	 * If we're currently in a switch because of the now canceled peer,
+	 * wait a DTIM here to make sure the phy is back on the base channel.
+	 * We can't otherwise force it.
+	 */
+	if (mvm->tdls_cs.cur_sta_id == mvm->tdls_cs.peer.sta_id &&
+	    mvm->tdls_cs.state != IWL_MVM_TDLS_SW_IDLE)
+		wait_for_phy = true;
+
+	mvm->tdls_cs.peer.sta_id = IWL_MVM_STATION_COUNT;
+	dev_kfree_skb(mvm->tdls_cs.peer.skb);
+	mvm->tdls_cs.peer.skb = NULL;
+
+out:
+	mutex_unlock(&mvm->mutex);
+
+	/* make sure the phy is on the base channel */
+	if (wait_for_phy)
+		msleep(TU_TO_MS(vif->bss_conf.dtim_period *
+				vif->bss_conf.beacon_int));
+
+	/* flush the channel switch state */
+	flush_delayed_work(&mvm->tdls_cs.dwork);
+
+	IWL_DEBUG_TDLS(mvm, "TDLS ending channel switch with %pM\n", sta->addr);
+}
+
+void
+iwl_mvm_tdls_recv_channel_switch(struct ieee80211_hw *hw,
+				 struct ieee80211_vif *vif,
+				 struct ieee80211_tdls_ch_sw_params *params)
+{
+	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+	enum iwl_tdls_channel_switch_type type;
+	unsigned int delay;
+
+	mutex_lock(&mvm->mutex);
+
+	IWL_DEBUG_TDLS(mvm,
+		       "Received TDLS ch switch action %d from %pM status %d\n",
+		       params->action_code, params->sta->addr, params->status);
+
+	/*
+	 * we got a non-zero status from a peer we were switching to - move to
+	 * the idle state and retry again later
+	 */
+	if (params->action_code == WLAN_TDLS_CHANNEL_SWITCH_RESPONSE &&
+	    params->status != 0 &&
+	    mvm->tdls_cs.state == IWL_MVM_TDLS_SW_REQ_SENT &&
+	    mvm->tdls_cs.cur_sta_id != IWL_MVM_STATION_COUNT) {
+		struct ieee80211_sta *cur_sta;
+
+		/* make sure it's the same peer */
+		cur_sta = rcu_dereference_protected(
+				mvm->fw_id_to_mac_id[mvm->tdls_cs.cur_sta_id],
+				lockdep_is_held(&mvm->mutex));
+		if (cur_sta == params->sta) {
+			iwl_mvm_tdls_update_cs_state(mvm,
+						     IWL_MVM_TDLS_SW_IDLE);
+			goto retry;
+		}
+	}
+
+	type = (params->action_code == WLAN_TDLS_CHANNEL_SWITCH_REQUEST) ?
+	       TDLS_SEND_CHAN_SW_RESP_AND_MOVE_CH : TDLS_MOVE_CH;
+
+	iwl_mvm_tdls_config_channel_switch(mvm, vif, type, params->sta->addr,
+					   params->sta->tdls_initiator, 0,
+					   params->chandef, params->timestamp,
+					   params->switch_time,
+					   params->switch_timeout,
+					   params->tmpl_skb,
+					   params->ch_sw_tm_ie);
+
+retry:
+	/* register a timeout in case we don't succeed in switching */
+	delay = vif->bss_conf.dtim_period * vif->bss_conf.beacon_int *
+		1024 / 1000;
+	mod_delayed_work(system_wq, &mvm->tdls_cs.dwork,
+			 msecs_to_jiffies(delay));
+	mutex_unlock(&mvm->mutex);
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.c b/drivers/net/wireless/iwlwifi/mvm/time-event.c
index 6dfad23..54fafbf 100644
--- a/drivers/net/wireless/iwlwifi/mvm/time-event.c
+++ b/drivers/net/wireless/iwlwifi/mvm/time-event.c
@@ -191,6 +191,35 @@
 	return true;
 }
 
+static void
+iwl_mvm_te_handle_notify_csa(struct iwl_mvm *mvm,
+			     struct iwl_mvm_time_event_data *te_data,
+			     struct iwl_time_event_notif *notif)
+{
+	if (!le32_to_cpu(notif->status)) {
+		IWL_DEBUG_TE(mvm, "CSA time event failed to start\n");
+		iwl_mvm_te_clear_data(mvm, te_data);
+		return;
+	}
+
+	switch (te_data->vif->type) {
+	case NL80211_IFTYPE_AP:
+		iwl_mvm_csa_noa_start(mvm);
+		break;
+	case NL80211_IFTYPE_STATION:
+		iwl_mvm_csa_client_absent(mvm, te_data->vif);
+		ieee80211_chswitch_done(te_data->vif, true);
+		break;
+	default:
+		/* should never happen */
+		WARN_ON_ONCE(1);
+		break;
+	}
+
+	/* we don't need it anymore */
+	iwl_mvm_te_clear_data(mvm, te_data);
+}
+
 /*
  * Handles a FW notification for an event that is known to the driver.
  *
@@ -252,14 +281,8 @@
 			set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status);
 			iwl_mvm_ref(mvm, IWL_MVM_REF_ROC);
 			ieee80211_ready_on_channel(mvm->hw);
-		} else if (te_data->vif->type == NL80211_IFTYPE_AP) {
-			if (le32_to_cpu(notif->status))
-				iwl_mvm_csa_noa_start(mvm);
-			else
-				IWL_DEBUG_TE(mvm, "CSA NOA failed to start\n");
-
-			/* we don't need it anymore */
-			iwl_mvm_te_clear_data(mvm, te_data);
+		} else if (te_data->id == TE_CHANNEL_SWITCH_PERIOD) {
+			iwl_mvm_te_handle_notify_csa(mvm, te_data, notif);
 		}
 	} else {
 		IWL_WARN(mvm, "Got TE with unknown action\n");
@@ -549,18 +572,11 @@
 	}
 }
 
-/*
- * Explicit request to remove a time event. The removal of a time event needs to
- * be synchronized with the flow of a time event's end notification, which also
- * removes the time event from the op mode data structures.
- */
-void iwl_mvm_remove_time_event(struct iwl_mvm *mvm,
-			       struct iwl_mvm_vif *mvmvif,
-			       struct iwl_mvm_time_event_data *te_data)
+static bool __iwl_mvm_remove_time_event(struct iwl_mvm *mvm,
+					struct iwl_mvm_time_event_data *te_data,
+					u32 *uid)
 {
-	struct iwl_time_event_cmd time_cmd = {};
-	u32 id, uid;
-	int ret;
+	u32 id;
 
 	/*
 	 * It is possible that by the time we got to this point the time
@@ -569,7 +585,7 @@
 	spin_lock_bh(&mvm->time_event_lock);
 
 	/* Save time event uid before clearing its data */
-	uid = te_data->uid;
+	*uid = te_data->uid;
 	id = te_data->id;
 
 	/*
@@ -584,10 +600,59 @@
 	 * send a removal command.
 	 */
 	if (id == TE_MAX) {
-		IWL_DEBUG_TE(mvm, "TE 0x%x has already ended\n", uid);
-		return;
+		IWL_DEBUG_TE(mvm, "TE 0x%x has already ended\n", *uid);
+		return false;
 	}
 
+	return true;
+}
+
+/*
+ * Explicit request to remove a aux roc time event. The removal of a time
+ * event needs to be synchronized with the flow of a time event's end
+ * notification, which also removes the time event from the op mode
+ * data structures.
+ */
+static void iwl_mvm_remove_aux_roc_te(struct iwl_mvm *mvm,
+				      struct iwl_mvm_vif *mvmvif,
+				      struct iwl_mvm_time_event_data *te_data)
+{
+	struct iwl_hs20_roc_req aux_cmd = {};
+	u32 uid;
+	int ret;
+
+	if (!__iwl_mvm_remove_time_event(mvm, te_data, &uid))
+		return;
+
+	aux_cmd.event_unique_id = cpu_to_le32(uid);
+	aux_cmd.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE);
+	aux_cmd.id_and_color =
+		cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
+	IWL_DEBUG_TE(mvm, "Removing BSS AUX ROC TE 0x%x\n",
+		     le32_to_cpu(aux_cmd.event_unique_id));
+	ret = iwl_mvm_send_cmd_pdu(mvm, HOT_SPOT_CMD, 0,
+				   sizeof(aux_cmd), &aux_cmd);
+
+	if (WARN_ON(ret))
+		return;
+}
+
+/*
+ * Explicit request to remove a time event. The removal of a time event needs to
+ * be synchronized with the flow of a time event's end notification, which also
+ * removes the time event from the op mode data structures.
+ */
+void iwl_mvm_remove_time_event(struct iwl_mvm *mvm,
+			       struct iwl_mvm_vif *mvmvif,
+			       struct iwl_mvm_time_event_data *te_data)
+{
+	struct iwl_time_event_cmd time_cmd = {};
+	u32 uid;
+	int ret;
+
+	if (!__iwl_mvm_remove_time_event(mvm, te_data, &uid))
+		return;
+
 	/* When we remove a TE, the UID is to be set in the id field */
 	time_cmd.id = cpu_to_le32(uid);
 	time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE);
@@ -666,13 +731,17 @@
 	return iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd);
 }
 
-void iwl_mvm_stop_p2p_roc(struct iwl_mvm *mvm)
+void iwl_mvm_stop_roc(struct iwl_mvm *mvm)
 {
 	struct iwl_mvm_vif *mvmvif;
 	struct iwl_mvm_time_event_data *te_data;
+	bool is_p2p = false;
 
 	lockdep_assert_held(&mvm->mutex);
 
+	mvmvif = NULL;
+	spin_lock_bh(&mvm->time_event_lock);
+
 	/*
 	 * Iterate over the list of time events and find the time event that is
 	 * associated with a P2P_DEVICE interface.
@@ -680,22 +749,41 @@
 	 * event at any given time and this time event coresponds to a ROC
 	 * request
 	 */
-	mvmvif = NULL;
-	spin_lock_bh(&mvm->time_event_lock);
 	list_for_each_entry(te_data, &mvm->time_event_list, list) {
-		if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) {
+		if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE &&
+		    te_data->running) {
 			mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif);
-			break;
+			is_p2p = true;
+			goto remove_te;
 		}
 	}
+
+	/*
+	 * Iterate over the list of aux roc time events and find the time
+	 * event that is associated with a BSS interface.
+	 * This assumes that a BSS interface can have only a single time
+	 * event at any given time and this time event coresponds to a ROC
+	 * request
+	 */
+	list_for_each_entry(te_data, &mvm->aux_roc_te_list, list) {
+		if (te_data->running) {
+			mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif);
+			goto remove_te;
+		}
+	}
+
+remove_te:
 	spin_unlock_bh(&mvm->time_event_lock);
 
 	if (!mvmvif) {
-		IWL_WARN(mvm, "P2P_DEVICE no remain on channel event\n");
+		IWL_WARN(mvm, "No remain on channel event\n");
 		return;
 	}
 
-	iwl_mvm_remove_time_event(mvm, mvmvif, te_data);
+	if (is_p2p)
+		iwl_mvm_remove_time_event(mvm, mvmvif, te_data);
+	else
+		iwl_mvm_remove_aux_roc_te(mvm, mvmvif, te_data);
 
 	iwl_mvm_roc_finished(mvm);
 }
diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.h b/drivers/net/wireless/iwlwifi/mvm/time-event.h
index b350e47..6f6b35d 100644
--- a/drivers/net/wireless/iwlwifi/mvm/time-event.h
+++ b/drivers/net/wireless/iwlwifi/mvm/time-event.h
@@ -182,14 +182,14 @@
 			  int duration, enum ieee80211_roc_type type);
 
 /**
- * iwl_mvm_stop_p2p_roc - stop remain on channel for p2p device functionlity
+ * iwl_mvm_stop_roc - stop remain on channel functionality
  * @mvm: the mvm component
  *
  * This function can be used to cancel an ongoing ROC session.
  * The function is async, it will instruct the FW to stop serving the ROC
  * session, but will not wait for the actual stopping of the session.
  */
-void iwl_mvm_stop_p2p_roc(struct iwl_mvm *mvm);
+void iwl_mvm_stop_roc(struct iwl_mvm *mvm);
 
 /**
  * iwl_mvm_remove_time_event - general function to clean up of time event
diff --git a/drivers/net/wireless/iwlwifi/mvm/tt.c b/drivers/net/wireless/iwlwifi/mvm/tt.c
index d4f2c29..2b1e61f 100644
--- a/drivers/net/wireless/iwlwifi/mvm/tt.c
+++ b/drivers/net/wireless/iwlwifi/mvm/tt.c
@@ -95,32 +95,81 @@
 	iwl_mvm_set_hw_ctkill_state(mvm, false);
 }
 
-static bool iwl_mvm_temp_notif(struct iwl_notif_wait_data *notif_wait,
-			       struct iwl_rx_packet *pkt, void *data)
+void iwl_mvm_tt_temp_changed(struct iwl_mvm *mvm, u32 temp)
 {
-	struct iwl_mvm *mvm =
-		container_of(notif_wait, struct iwl_mvm, notif_wait);
-	int *temp = data;
+	/* ignore the notification if we are in test mode */
+	if (mvm->temperature_test)
+		return;
+
+	if (mvm->temperature == temp)
+		return;
+
+	mvm->temperature = temp;
+	iwl_mvm_tt_handler(mvm);
+}
+
+static int iwl_mvm_temp_notif_parse(struct iwl_mvm *mvm,
+				    struct iwl_rx_packet *pkt)
+{
 	struct iwl_dts_measurement_notif *notif;
 	int len = iwl_rx_packet_payload_len(pkt);
+	int temp;
 
 	if (WARN_ON_ONCE(len != sizeof(*notif))) {
 		IWL_ERR(mvm, "Invalid DTS_MEASUREMENT_NOTIFICATION\n");
-		return true;
+		return -EINVAL;
 	}
 
 	notif = (void *)pkt->data;
 
-	*temp = le32_to_cpu(notif->temp);
+	temp = le32_to_cpu(notif->temp);
 
 	/* shouldn't be negative, but since it's s32, make sure it isn't */
-	if (WARN_ON_ONCE(*temp < 0))
-		*temp = 0;
+	if (WARN_ON_ONCE(temp < 0))
+		temp = 0;
 
-	IWL_DEBUG_TEMP(mvm, "DTS_MEASUREMENT_NOTIFICATION - %d\n", *temp);
+	IWL_DEBUG_TEMP(mvm, "DTS_MEASUREMENT_NOTIFICATION - %d\n", temp);
+
+	return temp;
+}
+
+static bool iwl_mvm_temp_notif_wait(struct iwl_notif_wait_data *notif_wait,
+				    struct iwl_rx_packet *pkt, void *data)
+{
+	struct iwl_mvm *mvm =
+		container_of(notif_wait, struct iwl_mvm, notif_wait);
+	int *temp = data;
+	int ret;
+
+	ret = iwl_mvm_temp_notif_parse(mvm, pkt);
+	if (ret < 0)
+		return true;
+
+	*temp = ret;
+
 	return true;
 }
 
+int iwl_mvm_temp_notif(struct iwl_mvm *mvm,
+		       struct iwl_rx_cmd_buffer *rxb,
+		       struct iwl_device_cmd *cmd)
+{
+	struct iwl_rx_packet *pkt = rxb_addr(rxb);
+	int temp;
+
+	/* the notification is handled synchronously in ctkill, so skip here */
+	if (test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status))
+		return 0;
+
+	temp = iwl_mvm_temp_notif_parse(mvm, pkt);
+	if (temp < 0)
+		return 0;
+
+	iwl_mvm_tt_temp_changed(mvm, temp);
+
+	return 0;
+}
+
 static int iwl_mvm_get_temp_cmd(struct iwl_mvm *mvm)
 {
 	struct iwl_dts_measurement_cmd cmd = {
@@ -141,7 +190,7 @@
 
 	iwl_init_notification_wait(&mvm->notif_wait, &wait_temp_notif,
 				   temp_notif, ARRAY_SIZE(temp_notif),
-				   iwl_mvm_temp_notif, &temp);
+				   iwl_mvm_temp_notif_wait, &temp);
 
 	ret = iwl_mvm_get_temp_cmd(mvm);
 	if (ret) {
diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c
index 8d84873..4f15d9d 100644
--- a/drivers/net/wireless/iwlwifi/mvm/tx.c
+++ b/drivers/net/wireless/iwlwifi/mvm/tx.c
@@ -73,9 +73,9 @@
 /*
  * Sets most of the Tx cmd's fields
  */
-static void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb,
-			       struct iwl_tx_cmd *tx_cmd,
-			       struct ieee80211_tx_info *info, u8 sta_id)
+void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb,
+			struct iwl_tx_cmd *tx_cmd,
+			struct ieee80211_tx_info *info, u8 sta_id)
 {
 	struct ieee80211_hdr *hdr = (void *)skb->data;
 	__le16 fc = hdr->frame_control;
@@ -149,11 +149,9 @@
 /*
  * Sets the fields in the Tx cmd that are rate related
  */
-static void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm,
-				    struct iwl_tx_cmd *tx_cmd,
-				    struct ieee80211_tx_info *info,
-				    struct ieee80211_sta *sta,
-				    __le16 fc)
+void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm, struct iwl_tx_cmd *tx_cmd,
+			    struct ieee80211_tx_info *info,
+			    struct ieee80211_sta *sta, __le16 fc)
 {
 	u32 rate_flags;
 	int rate_idx;
@@ -232,10 +230,10 @@
 /*
  * Sets the fields in the Tx cmd that are crypto related
  */
-static void iwl_mvm_set_tx_cmd_crypto(struct iwl_mvm *mvm,
-				      struct ieee80211_tx_info *info,
-				      struct iwl_tx_cmd *tx_cmd,
-				      struct sk_buff *skb_frag)
+void iwl_mvm_set_tx_cmd_crypto(struct iwl_mvm *mvm,
+			       struct ieee80211_tx_info *info,
+			       struct iwl_tx_cmd *tx_cmd,
+			       struct sk_buff *skb_frag)
 {
 	struct ieee80211_key_conf *keyconf = info->control.hw_key;
 
@@ -426,6 +424,13 @@
 
 	WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM);
 
+	if (sta->tdls) {
+		/* default to TID 0 for non-QoS packets */
+		u8 tdls_tid = tid == IWL_MAX_TID_COUNT ? 0 : tid;
+
+		txq_id = mvmsta->hw_queue[tid_to_mac80211_ac[tdls_tid]];
+	}
+
 	if (is_ampdu) {
 		if (WARN_ON_ONCE(mvmsta->tid_data[tid].state != IWL_AGG_ON))
 			goto drop_unlock_sta;
@@ -660,6 +665,12 @@
 			seq_ctl = le16_to_cpu(hdr->seq_ctrl);
 		}
 
+		/*
+		 * TODO: this is not accurate if we are freeing more than one
+		 * packet.
+		 */
+		info->status.tx_time =
+			le16_to_cpu(tx_resp->wireless_media_time);
 		BUILD_BUG_ON(ARRAY_SIZE(info->status.status_driver_data) < 1);
 		info->status.status_driver_data[0] =
 				(void *)(uintptr_t)tx_resp->reduced_tpc;
@@ -852,6 +863,8 @@
 		mvmsta->tid_data[tid].rate_n_flags =
 			le32_to_cpu(tx_resp->initial_rate);
 		mvmsta->tid_data[tid].reduced_tpc = tx_resp->reduced_tpc;
+		mvmsta->tid_data[tid].tx_time =
+			le16_to_cpu(tx_resp->wireless_media_time);
 	}
 
 	rcu_read_unlock();
@@ -880,6 +893,8 @@
 	info->status.ampdu_len = ba_notif->txed;
 	iwl_mvm_hwrate_to_tx_status(tid_data->rate_n_flags,
 				    info);
+	/* TODO: not accounted if the whole A-MPDU failed */
+	info->status.tx_time = tid_data->tx_time;
 	info->status.status_driver_data[0] =
 		(void *)(uintptr_t)tid_data->reduced_tpc;
 }
diff --git a/drivers/net/wireless/iwlwifi/pcie/drv.c b/drivers/net/wireless/iwlwifi/pcie/drv.c
index 6ced854..3ee8e38 100644
--- a/drivers/net/wireless/iwlwifi/pcie/drv.c
+++ b/drivers/net/wireless/iwlwifi/pcie/drv.c
@@ -499,6 +499,7 @@
 static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
 	const struct iwl_cfg *cfg = (struct iwl_cfg *)(ent->driver_data);
+	const struct iwl_cfg *cfg_7265d __maybe_unused = NULL;
 	struct iwl_trans *iwl_trans;
 	struct iwl_trans_pcie *trans_pcie;
 	int ret;
@@ -507,6 +508,25 @@
 	if (IS_ERR(iwl_trans))
 		return PTR_ERR(iwl_trans);
 
+#if IS_ENABLED(CONFIG_IWLMVM)
+	/*
+	 * special-case 7265D, it has the same PCI IDs.
+	 *
+	 * Note that because we already pass the cfg to the transport above,
+	 * all the parameters that the transport uses must, until that is
+	 * changed, be identical to the ones in the 7265D configuration.
+	 */
+	if (cfg == &iwl7265_2ac_cfg)
+		cfg_7265d = &iwl7265d_2ac_cfg;
+	else if (cfg == &iwl7265_2n_cfg)
+		cfg_7265d = &iwl7265d_2n_cfg;
+	else if (cfg == &iwl7265_n_cfg)
+		cfg_7265d = &iwl7265d_n_cfg;
+	if (cfg_7265d &&
+	    (iwl_trans->hw_rev & CSR_HW_REV_TYPE_MSK) == CSR_HW_REV_TYPE_7265D)
+		cfg = cfg_7265d;
+#endif
+
 	pci_set_drvdata(pdev, iwl_trans);
 
 	trans_pcie = IWL_TRANS_GET_PCIE_TRANS(iwl_trans);
diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c
index 40a2906..5d79a1f 100644
--- a/drivers/net/wireless/iwlwifi/pcie/trans.c
+++ b/drivers/net/wireless/iwlwifi/pcie/trans.c
@@ -78,6 +78,11 @@
 #include "iwl-agn-hw.h"
 #include "iwl-fw-error-dump.h"
 #include "internal.h"
+#include "iwl-fh.h"
+
+/* extended range in FW SRAM */
+#define IWL_FW_MEM_EXTENDED_START	0x40000
+#define IWL_FW_MEM_EXTENDED_END		0x57FFF
 
 static void iwl_pcie_free_fw_monitor(struct iwl_trans *trans)
 {
@@ -512,6 +517,9 @@
 			   CSR_HW_IF_CONFIG_REG_BIT_NIC_READY,
 			   HW_READY_TIMEOUT);
 
+	if (ret >= 0)
+		iwl_set_bit(trans, CSR_MBOX_SET_REG, CSR_MBOX_SET_REG_OS_ALIVE);
+
 	IWL_DEBUG_INFO(trans, "hardware%s ready\n", ret < 0 ? " not" : "");
 	return ret;
 }
@@ -624,14 +632,28 @@
 	}
 
 	for (offset = 0; offset < section->len; offset += chunk_sz) {
-		u32 copy_size;
+		u32 copy_size, dst_addr;
+		bool extended_addr = false;
 
 		copy_size = min_t(u32, chunk_sz, section->len - offset);
+		dst_addr = section->offset + offset;
+
+		if (dst_addr >= IWL_FW_MEM_EXTENDED_START &&
+		    dst_addr <= IWL_FW_MEM_EXTENDED_END)
+			extended_addr = true;
+
+		if (extended_addr)
+			iwl_set_bits_prph(trans, LMPM_CHICK,
+					  LMPM_CHICK_EXTENDED_ADDR_SPACE);
 
 		memcpy(v_addr, (u8 *)section->data + offset, copy_size);
-		ret = iwl_pcie_load_firmware_chunk(trans,
-						   section->offset + offset,
-						   p_addr, copy_size);
+		ret = iwl_pcie_load_firmware_chunk(trans, dst_addr, p_addr,
+						   copy_size);
+
+		if (extended_addr)
+			iwl_clear_bits_prph(trans, LMPM_CHICK,
+					    LMPM_CHICK_EXTENDED_ADDR_SPACE);
+
 		if (ret) {
 			IWL_ERR(trans,
 				"Could not load the [%d] uCode section\n",
@@ -644,14 +666,14 @@
 	return ret;
 }
 
-static int iwl_pcie_load_cpu_secured_sections(struct iwl_trans *trans,
-					      const struct fw_img *image,
-					      int cpu,
-					      int *first_ucode_section)
+static int iwl_pcie_load_cpu_sections_8000b(struct iwl_trans *trans,
+					    const struct fw_img *image,
+					    int cpu,
+					    int *first_ucode_section)
 {
 	int shift_param;
-	int i, ret = 0;
-	u32 last_read_idx = 0;
+	int i, ret = 0, sec_num = 0x1;
+	u32 val, last_read_idx = 0;
 
 	if (cpu == 1) {
 		shift_param = 0;
@@ -672,21 +694,16 @@
 			break;
 		}
 
-		if (i == (*first_ucode_section) + 1)
-			/* set CPU to started */
-			iwl_set_bits_prph(trans,
-					  CSR_UCODE_LOAD_STATUS_ADDR,
-					  LMPM_CPU_HDRS_LOADING_COMPLETED
-					  << shift_param);
-
 		ret = iwl_pcie_load_section(trans, i, &image->sec[i]);
 		if (ret)
 			return ret;
+
+		/* Notify the ucode of the loaded section number and status */
+		val = iwl_read_direct32(trans, FH_UCODE_LOAD_STATUS);
+		val = val | (sec_num << shift_param);
+		iwl_write_direct32(trans, FH_UCODE_LOAD_STATUS, val);
+		sec_num = (sec_num << 1) | 0x1;
 	}
-	/* image loading complete */
-	iwl_set_bits_prph(trans,
-			  CSR_UCODE_LOAD_STATUS_ADDR,
-			  LMPM_CPU_UCODE_LOADING_COMPLETED << shift_param);
 
 	*first_ucode_section = last_read_idx;
 
@@ -739,6 +756,64 @@
 	return 0;
 }
 
+static void iwl_pcie_apply_destination(struct iwl_trans *trans)
+{
+	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+	const struct iwl_fw_dbg_dest_tlv *dest = trans->dbg_dest_tlv;
+	int i;
+
+	if (dest->version)
+		IWL_ERR(trans,
+			"DBG DEST version is %d - expect issues\n",
+			dest->version);
+
+	IWL_INFO(trans, "Applying debug destination %s\n",
+		 get_fw_dbg_mode_string(dest->monitor_mode));
+
+	if (dest->monitor_mode == EXTERNAL_MODE)
+		iwl_pcie_alloc_fw_monitor(trans);
+	else
+		IWL_WARN(trans, "PCI should have external buffer debug\n");
+
+	for (i = 0; i < trans->dbg_dest_reg_num; i++) {
+		u32 addr = le32_to_cpu(dest->reg_ops[i].addr);
+		u32 val = le32_to_cpu(dest->reg_ops[i].val);
+
+		switch (dest->reg_ops[i].op) {
+		case CSR_ASSIGN:
+			iwl_write32(trans, addr, val);
+			break;
+		case CSR_SETBIT:
+			iwl_set_bit(trans, addr, BIT(val));
+			break;
+		case CSR_CLEARBIT:
+			iwl_clear_bit(trans, addr, BIT(val));
+			break;
+		case PRPH_ASSIGN:
+			iwl_write_prph(trans, addr, val);
+			break;
+		case PRPH_SETBIT:
+			iwl_set_bits_prph(trans, addr, BIT(val));
+			break;
+		case PRPH_CLEARBIT:
+			iwl_clear_bits_prph(trans, addr, BIT(val));
+			break;
+		default:
+			IWL_ERR(trans, "FW debug - unknown OP %d\n",
+				dest->reg_ops[i].op);
+			break;
+		}
+	}
+
+	if (dest->monitor_mode == EXTERNAL_MODE && trans_pcie->fw_mon_size) {
+		iwl_write_prph(trans, le32_to_cpu(dest->base_reg),
+			       trans_pcie->fw_mon_phys >> dest->base_shift);
+		iwl_write_prph(trans, le32_to_cpu(dest->end_reg),
+			       (trans_pcie->fw_mon_phys +
+				trans_pcie->fw_mon_size) >> dest->end_shift);
+	}
+}
+
 static int iwl_pcie_load_given_ucode(struct iwl_trans *trans,
 				const struct fw_img *image)
 {
@@ -746,39 +821,13 @@
 	int ret = 0;
 	int first_ucode_section;
 
-	IWL_DEBUG_FW(trans,
-		     "working with %s CPU\n",
+	IWL_DEBUG_FW(trans, "working with %s CPU\n",
 		     image->is_dual_cpus ? "Dual" : "Single");
 
-	/* configure the ucode to be ready to get the secured image */
-	if (iwl_has_secure_boot(trans->hw_rev, trans->cfg->device_family)) {
-		/* set secure boot inspector addresses */
-		iwl_write_prph(trans,
-			       LMPM_SECURE_INSPECTOR_CODE_ADDR,
-			       LMPM_SECURE_INSPECTOR_CODE_MEM_SPACE);
-
-		iwl_write_prph(trans,
-			       LMPM_SECURE_INSPECTOR_DATA_ADDR,
-			       LMPM_SECURE_INSPECTOR_DATA_MEM_SPACE);
-
-		/* set CPU1 header address */
-		iwl_write_prph(trans,
-			       LMPM_SECURE_UCODE_LOAD_CPU1_HDR_ADDR,
-			       LMPM_SECURE_CPU1_HDR_MEM_SPACE);
-
-		/* load to FW the binary Secured sections of CPU1 */
-		ret = iwl_pcie_load_cpu_secured_sections(trans, image, 1,
-							 &first_ucode_section);
-		if (ret)
-			return ret;
-
-	} else {
-		/* load to FW the binary Non secured sections of CPU1 */
-		ret = iwl_pcie_load_cpu_sections(trans, image, 1,
-						 &first_ucode_section);
-		if (ret)
-			return ret;
-	}
+	/* load to FW the binary non secured sections of CPU1 */
+	ret = iwl_pcie_load_cpu_sections(trans, image, 1, &first_ucode_section);
+	if (ret)
+		return ret;
 
 	if (image->is_dual_cpus) {
 		/* set CPU2 header address */
@@ -787,14 +836,8 @@
 			       LMPM_SECURE_CPU2_HDR_MEM_SPACE);
 
 		/* load to FW the binary sections of CPU2 */
-		if (iwl_has_secure_boot(trans->hw_rev,
-					trans->cfg->device_family))
-			ret = iwl_pcie_load_cpu_secured_sections(
-							trans, image, 2,
-							&first_ucode_section);
-		else
-			ret = iwl_pcie_load_cpu_sections(trans, image, 2,
-							 &first_ucode_section);
+		ret = iwl_pcie_load_cpu_sections(trans, image, 2,
+						 &first_ucode_section);
 		if (ret)
 			return ret;
 	}
@@ -811,6 +854,8 @@
 				       (trans_pcie->fw_mon_phys +
 					trans_pcie->fw_mon_size) >> 4);
 		}
+	} else if (trans->dbg_dest_tlv) {
+		iwl_pcie_apply_destination(trans);
 	}
 
 	/* release CPU reset */
@@ -819,18 +864,50 @@
 	else
 		iwl_write32(trans, CSR_RESET, 0);
 
-	if (iwl_has_secure_boot(trans->hw_rev, trans->cfg->device_family)) {
-		/* wait for image verification to complete  */
-		ret = iwl_poll_prph_bit(trans,
-					LMPM_SECURE_BOOT_CPU1_STATUS_ADDR,
-					LMPM_SECURE_BOOT_STATUS_SUCCESS,
-					LMPM_SECURE_BOOT_STATUS_SUCCESS,
-					LMPM_SECURE_TIME_OUT);
+	return 0;
+}
 
-		if (ret < 0) {
-			IWL_ERR(trans, "Time out on secure boot process\n");
-			return ret;
-		}
+static int iwl_pcie_load_given_ucode_8000b(struct iwl_trans *trans,
+					   const struct fw_img *image)
+{
+	int ret = 0;
+	int first_ucode_section;
+	u32 reg;
+
+	IWL_DEBUG_FW(trans, "working with %s CPU\n",
+		     image->is_dual_cpus ? "Dual" : "Single");
+
+	/* configure the ucode to be ready to get the secured image */
+	/* release CPU reset */
+	iwl_write_prph(trans, RELEASE_CPU_RESET, RELEASE_CPU_RESET_BIT);
+
+	/* load to FW the binary Secured sections of CPU1 */
+	ret = iwl_pcie_load_cpu_sections_8000b(trans, image, 1,
+					       &first_ucode_section);
+	if (ret)
+		return ret;
+
+	/* load to FW the binary sections of CPU2 */
+	ret = iwl_pcie_load_cpu_sections_8000b(trans, image, 2,
+					       &first_ucode_section);
+	if (ret)
+		return ret;
+
+	/* Notify FW loading is done */
+	iwl_write_direct32(trans, FH_UCODE_LOAD_STATUS, 0xFFFFFFFF);
+
+	/* wait for image verification to complete  */
+	ret = iwl_poll_prph_bit(trans, LMPM_SECURE_BOOT_CPU1_STATUS_ADDR_B0,
+				LMPM_SECURE_BOOT_STATUS_SUCCESS,
+				LMPM_SECURE_BOOT_STATUS_SUCCESS,
+				LMPM_SECURE_TIME_OUT);
+	if (ret < 0) {
+		reg = iwl_read_prph(trans,
+				    LMPM_SECURE_BOOT_CPU1_STATUS_ADDR_B0);
+
+		IWL_ERR(trans, "Timeout on secure boot process, reg = %x\n",
+			reg);
+		return ret;
 	}
 
 	return 0;
@@ -882,7 +959,11 @@
 	iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
 
 	/* Load the given image to the HW */
-	return iwl_pcie_load_given_ucode(trans, fw);
+	if ((trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) &&
+	    (CSR_HW_REV_STEP(trans->hw_rev) == SILICON_B_STEP))
+		return iwl_pcie_load_given_ucode_8000b(trans, fw);
+	else
+		return iwl_pcie_load_given_ucode(trans, fw);
 }
 
 static void iwl_trans_pcie_fw_alive(struct iwl_trans *trans, u32 scd_addr)
@@ -913,7 +994,8 @@
 	 * restart. So don't process again if the device is
 	 * already dead.
 	 */
-	if (test_bit(STATUS_DEVICE_ENABLED, &trans->status)) {
+	if (test_and_clear_bit(STATUS_DEVICE_ENABLED, &trans->status)) {
+		IWL_DEBUG_INFO(trans, "DEVICE_ENABLED bit was set and is now cleared\n");
 		iwl_pcie_tx_stop(trans);
 		iwl_pcie_rx_stop(trans);
 
@@ -938,12 +1020,12 @@
 	spin_unlock(&trans_pcie->irq_lock);
 
 	/* stop and reset the on-board processor */
-	iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET);
+	iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
+	udelay(20);
 
 	/* clear all status bits */
 	clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
 	clear_bit(STATUS_INT_ENABLED, &trans->status);
-	clear_bit(STATUS_DEVICE_ENABLED, &trans->status);
 	clear_bit(STATUS_TPOWER_PMI, &trans->status);
 	clear_bit(STATUS_RFKILL, &trans->status);
 
@@ -972,6 +1054,9 @@
 		clear_bit(STATUS_RFKILL, &trans->status);
 	if (hw_rfkill != was_hw_rfkill)
 		iwl_trans_pcie_rf_kill(trans, hw_rfkill);
+
+	/* re-take ownership to prevent other users from stealing the deivce */
+	iwl_pcie_prepare_card_hw(trans);
 }
 
 void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state)
@@ -1031,6 +1116,9 @@
 	iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
 	iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
 
+	if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
+		udelay(2);
+
 	ret = iwl_poll_bit(trans, CSR_GP_CNTRL,
 			   CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
 			   CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
@@ -1233,6 +1321,8 @@
 	/* this bit wakes up the NIC */
 	__iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL,
 				 CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+	if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
+		udelay(2);
 
 	/*
 	 * These bits say the device is running, and should keep running for
@@ -1898,8 +1988,7 @@
 		int reg;
 		__le32 *val;
 
-		prph_len += sizeof(*data) + sizeof(*prph) +
-			num_bytes_in_chunk;
+		prph_len += sizeof(**data) + sizeof(*prph) + num_bytes_in_chunk;
 
 		(*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PRPH);
 		(*data)->len = cpu_to_le32(sizeof(*prph) +
@@ -1942,6 +2031,31 @@
 	return csr_len;
 }
 
+static u32 iwl_trans_pcie_fh_regs_dump(struct iwl_trans *trans,
+				       struct iwl_fw_error_dump_data **data)
+{
+	u32 fh_regs_len = FH_MEM_UPPER_BOUND - FH_MEM_LOWER_BOUND;
+	unsigned long flags;
+	__le32 *val;
+	int i;
+
+	if (!iwl_trans_grab_nic_access(trans, false, &flags))
+		return 0;
+
+	(*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_FH_REGS);
+	(*data)->len = cpu_to_le32(fh_regs_len);
+	val = (void *)(*data)->data;
+
+	for (i = FH_MEM_LOWER_BOUND; i < FH_MEM_UPPER_BOUND; i += sizeof(u32))
+		*val++ = cpu_to_le32(iwl_trans_pcie_read32(trans, i));
+
+	iwl_trans_release_nic_access(trans, &flags);
+
+	*data = iwl_fw_error_next_data(*data);
+
+	return sizeof(**data) + fh_regs_len;
+}
+
 static
 struct iwl_trans_dump_data *iwl_trans_pcie_dump_data(struct iwl_trans *trans)
 {
@@ -1951,6 +2065,7 @@
 	struct iwl_fw_error_dump_txcmd *txcmd;
 	struct iwl_trans_dump_data *dump_data;
 	u32 len;
+	u32 monitor_len;
 	int i, ptr;
 
 	/* transport dump header */
@@ -1973,10 +2088,34 @@
 			num_bytes_in_chunk;
 	}
 
+	/* FH registers */
+	len += sizeof(*data) + (FH_MEM_UPPER_BOUND - FH_MEM_LOWER_BOUND);
+
 	/* FW monitor */
-	if (trans_pcie->fw_mon_page)
+	if (trans_pcie->fw_mon_page) {
 		len += sizeof(*data) + sizeof(struct iwl_fw_error_dump_fw_mon) +
-			trans_pcie->fw_mon_size;
+		       trans_pcie->fw_mon_size;
+		monitor_len = trans_pcie->fw_mon_size;
+	} else if (trans->dbg_dest_tlv) {
+		u32 base, end;
+
+		base = le32_to_cpu(trans->dbg_dest_tlv->base_reg);
+		end = le32_to_cpu(trans->dbg_dest_tlv->end_reg);
+
+		base = iwl_read_prph(trans, base) <<
+		       trans->dbg_dest_tlv->base_shift;
+		end = iwl_read_prph(trans, end) <<
+		      trans->dbg_dest_tlv->end_shift;
+
+		/* Make "end" point to the actual end */
+		if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
+			end += (1 << trans->dbg_dest_tlv->end_shift);
+		monitor_len = end - base;
+		len += sizeof(*data) + sizeof(struct iwl_fw_error_dump_fw_mon) +
+		       monitor_len;
+	} else {
+		monitor_len = 0;
+	}
 
 	dump_data = vzalloc(len);
 	if (!dump_data)
@@ -2013,36 +2152,71 @@
 
 	len += iwl_trans_pcie_dump_prph(trans, &data);
 	len += iwl_trans_pcie_dump_csr(trans, &data);
+	len += iwl_trans_pcie_fh_regs_dump(trans, &data);
 	/* data is already pointing to the next section */
 
-	if (trans_pcie->fw_mon_page) {
+	if ((trans_pcie->fw_mon_page &&
+	     trans->cfg->device_family == IWL_DEVICE_FAMILY_7000) ||
+	    trans->dbg_dest_tlv) {
 		struct iwl_fw_error_dump_fw_mon *fw_mon_data;
+		u32 base, write_ptr, wrap_cnt;
+
+		/* If there was a dest TLV - use the values from there */
+		if (trans->dbg_dest_tlv) {
+			write_ptr =
+				le32_to_cpu(trans->dbg_dest_tlv->write_ptr_reg);
+			wrap_cnt = le32_to_cpu(trans->dbg_dest_tlv->wrap_count);
+			base = le32_to_cpu(trans->dbg_dest_tlv->base_reg);
+		} else {
+			base = MON_BUFF_BASE_ADDR;
+			write_ptr = MON_BUFF_WRPTR;
+			wrap_cnt = MON_BUFF_CYCLE_CNT;
+		}
 
 		data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_FW_MONITOR);
-		data->len = cpu_to_le32(trans_pcie->fw_mon_size +
-					sizeof(*fw_mon_data));
 		fw_mon_data = (void *)data->data;
 		fw_mon_data->fw_mon_wr_ptr =
-			cpu_to_le32(iwl_read_prph(trans, MON_BUFF_WRPTR));
+			cpu_to_le32(iwl_read_prph(trans, write_ptr));
 		fw_mon_data->fw_mon_cycle_cnt =
-			cpu_to_le32(iwl_read_prph(trans, MON_BUFF_CYCLE_CNT));
+			cpu_to_le32(iwl_read_prph(trans, wrap_cnt));
 		fw_mon_data->fw_mon_base_ptr =
-			cpu_to_le32(iwl_read_prph(trans, MON_BUFF_BASE_ADDR));
+			cpu_to_le32(iwl_read_prph(trans, base));
 
-		/*
-		 * The firmware is now asserted, it won't write anything to
-		 * the buffer. CPU can take ownership to fetch the data.
-		 * The buffer will be handed back to the device before the
-		 * firmware will be restarted.
-		 */
-		dma_sync_single_for_cpu(trans->dev, trans_pcie->fw_mon_phys,
-					trans_pcie->fw_mon_size,
-					DMA_FROM_DEVICE);
-		memcpy(fw_mon_data->data, page_address(trans_pcie->fw_mon_page),
-		       trans_pcie->fw_mon_size);
+		len += sizeof(*data) + sizeof(*fw_mon_data);
+		if (trans_pcie->fw_mon_page) {
+			data->len = cpu_to_le32(trans_pcie->fw_mon_size +
+						sizeof(*fw_mon_data));
 
-		len += sizeof(*data) + sizeof(*fw_mon_data) +
-			trans_pcie->fw_mon_size;
+			/*
+			 * The firmware is now asserted, it won't write anything
+			 * to the buffer. CPU can take ownership to fetch the
+			 * data. The buffer will be handed back to the device
+			 * before the firmware will be restarted.
+			 */
+			dma_sync_single_for_cpu(trans->dev,
+						trans_pcie->fw_mon_phys,
+						trans_pcie->fw_mon_size,
+						DMA_FROM_DEVICE);
+			memcpy(fw_mon_data->data,
+			       page_address(trans_pcie->fw_mon_page),
+			       trans_pcie->fw_mon_size);
+
+			len += trans_pcie->fw_mon_size;
+		} else {
+			/* If we are here then the buffer is internal */
+
+			/*
+			 * Update pointers to reflect actual values after
+			 * shifting
+			 */
+			base = iwl_read_prph(trans, base) <<
+			       trans->dbg_dest_tlv->base_shift;
+			iwl_trans_read_mem(trans, base, fw_mon_data->data,
+					   monitor_len / sizeof(u32));
+			data->len = cpu_to_le32(sizeof(*fw_mon_data) +
+						monitor_len);
+			len += monitor_len;
+		}
 	}
 
 	dump_data->len = len;
diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c
index eb8e298..8a6c7a0 100644
--- a/drivers/net/wireless/iwlwifi/pcie/tx.c
+++ b/drivers/net/wireless/iwlwifi/pcie/tx.c
@@ -989,6 +989,65 @@
 	spin_unlock_bh(&txq->lock);
 }
 
+static int iwl_pcie_set_cmd_in_flight(struct iwl_trans *trans)
+{
+	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+	int ret;
+
+	lockdep_assert_held(&trans_pcie->reg_lock);
+
+	if (trans_pcie->cmd_in_flight)
+		return 0;
+
+	trans_pcie->cmd_in_flight = true;
+
+	/*
+	 * wake up the NIC to make sure that the firmware will see the host
+	 * command - we will let the NIC sleep once all the host commands
+	 * returned. This needs to be done only on NICs that have
+	 * apmg_wake_up_wa set.
+	 */
+	if (trans->cfg->base_params->apmg_wake_up_wa) {
+		__iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL,
+					 CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+		if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
+			udelay(2);
+
+		ret = iwl_poll_bit(trans, CSR_GP_CNTRL,
+				   CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN,
+				   (CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY |
+				    CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP),
+				   15000);
+		if (ret < 0) {
+			__iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL,
+					CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+			trans_pcie->cmd_in_flight = false;
+			IWL_ERR(trans, "Failed to wake NIC for hcmd\n");
+			return -EIO;
+		}
+	}
+
+	return 0;
+}
+
+static int iwl_pcie_clear_cmd_in_flight(struct iwl_trans *trans)
+{
+	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+	lockdep_assert_held(&trans_pcie->reg_lock);
+
+	if (WARN_ON(!trans_pcie->cmd_in_flight))
+		return 0;
+
+	trans_pcie->cmd_in_flight = false;
+
+	if (trans->cfg->base_params->apmg_wake_up_wa)
+		__iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL,
+					CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+
+	return 0;
+}
+
 /*
  * iwl_pcie_cmdq_reclaim - Reclaim TX command queue entries already Tx'd
  *
@@ -1024,14 +1083,9 @@
 		}
 	}
 
-	if (trans->cfg->base_params->apmg_wake_up_wa &&
-	    q->read_ptr == q->write_ptr) {
+	if (q->read_ptr == q->write_ptr) {
 		spin_lock_irqsave(&trans_pcie->reg_lock, flags);
-		WARN_ON(!trans_pcie->cmd_in_flight);
-		trans_pcie->cmd_in_flight = false;
-		__iwl_trans_pcie_clear_bit(trans,
-					   CSR_GP_CNTRL,
-					   CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+		iwl_pcie_clear_cmd_in_flight(trans);
 		spin_unlock_irqrestore(&trans_pcie->reg_lock, flags);
 	}
 
@@ -1419,32 +1473,11 @@
 		mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout);
 
 	spin_lock_irqsave(&trans_pcie->reg_lock, flags);
-
-	/*
-	 * wake up the NIC to make sure that the firmware will see the host
-	 * command - we will let the NIC sleep once all the host commands
-	 * returned. This needs to be done only on NICs that have
-	 * apmg_wake_up_wa set.
-	 */
-	if (trans->cfg->base_params->apmg_wake_up_wa &&
-	    !trans_pcie->cmd_in_flight) {
-		trans_pcie->cmd_in_flight = true;
-		__iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL,
-					 CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
-		ret = iwl_poll_bit(trans, CSR_GP_CNTRL,
-				   CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN,
-				   (CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY |
-				    CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP),
-				   15000);
-		if (ret < 0) {
-			__iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL,
-				   CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
-			spin_unlock_irqrestore(&trans_pcie->reg_lock, flags);
-			trans_pcie->cmd_in_flight = false;
-			IWL_ERR(trans, "Failed to wake NIC for hcmd\n");
-			idx = -EIO;
-			goto out;
-		}
+	ret = iwl_pcie_set_cmd_in_flight(trans);
+	if (ret < 0) {
+		idx = ret;
+		spin_unlock_irqrestore(&trans_pcie->reg_lock, flags);
+		goto out;
 	}
 
 	/* Increment and update queue's write index */
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index 756eea5..a71b9d5 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -2259,7 +2259,7 @@
 	if (err != 0) {
 		printk(KERN_DEBUG "mac80211_hwsim: device_bind_driver failed (%d)\n",
 		       err);
-		goto failed_hw;
+		goto failed_bind;
 	}
 
 	skb_queue_head_init(&data->pending);
@@ -2469,6 +2469,8 @@
 	return idx;
 
 failed_hw:
+	device_release_driver(data->dev);
+failed_bind:
 	device_unregister(data->dev);
 failed_drvdata:
 	ieee80211_free_hw(hw);
diff --git a/drivers/net/wireless/mwifiex/11n_rxreorder.c b/drivers/net/wireless/mwifiex/11n_rxreorder.c
index 4005707..5ef5a0e 100644
--- a/drivers/net/wireless/mwifiex/11n_rxreorder.c
+++ b/drivers/net/wireless/mwifiex/11n_rxreorder.c
@@ -196,6 +196,7 @@
 	mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, start_win);
 
 	del_timer_sync(&tbl->timer_context.timer);
+	tbl->timer_context.timer_is_set = false;
 
 	spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
 	list_del(&tbl->list);
@@ -297,6 +298,7 @@
 		(struct reorder_tmr_cnxt *) context;
 	int start_win, seq_num;
 
+	ctx->timer_is_set = false;
 	seq_num = mwifiex_11n_find_last_seq_num(ctx);
 
 	if (seq_num < 0)
@@ -385,6 +387,7 @@
 
 	new_node->timer_context.ptr = new_node;
 	new_node->timer_context.priv = priv;
+	new_node->timer_context.timer_is_set = false;
 
 	init_timer(&new_node->timer_context.timer);
 	new_node->timer_context.timer.function = mwifiex_flush_data;
@@ -399,6 +402,22 @@
 	spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
 }
 
+static void
+mwifiex_11n_rxreorder_timer_restart(struct mwifiex_rx_reorder_tbl *tbl)
+{
+	u32 min_flush_time;
+
+	if (tbl->win_size >= MWIFIEX_BA_WIN_SIZE_32)
+		min_flush_time = MIN_FLUSH_TIMER_15_MS;
+	else
+		min_flush_time = MIN_FLUSH_TIMER_MS;
+
+	mod_timer(&tbl->timer_context.timer,
+		  jiffies + msecs_to_jiffies(min_flush_time * tbl->win_size));
+
+	tbl->timer_context.timer_is_set = true;
+}
+
 /*
  * This function prepares command for adding a BA request.
  *
@@ -523,31 +542,31 @@
 				u8 *ta, u8 pkt_type, void *payload)
 {
 	struct mwifiex_rx_reorder_tbl *tbl;
-	int start_win, end_win, win_size;
+	int prev_start_win, start_win, end_win, win_size;
 	u16 pkt_index;
 	bool init_window_shift = false;
+	int ret = 0;
 
 	tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, ta);
 	if (!tbl) {
 		if (pkt_type != PKT_TYPE_BAR)
 			mwifiex_11n_dispatch_pkt(priv, payload);
-		return 0;
+		return ret;
 	}
 
 	if ((pkt_type == PKT_TYPE_AMSDU) && !tbl->amsdu) {
 		mwifiex_11n_dispatch_pkt(priv, payload);
-		return 0;
+		return ret;
 	}
 
 	start_win = tbl->start_win;
+	prev_start_win = start_win;
 	win_size = tbl->win_size;
 	end_win = ((start_win + win_size) - 1) & (MAX_TID_VALUE - 1);
 	if (tbl->flags & RXREOR_INIT_WINDOW_SHIFT) {
 		init_window_shift = true;
 		tbl->flags &= ~RXREOR_INIT_WINDOW_SHIFT;
 	}
-	mod_timer(&tbl->timer_context.timer,
-		  jiffies + msecs_to_jiffies(MIN_FLUSH_TIMER_MS * win_size));
 
 	if (tbl->flags & RXREOR_FORCE_NO_DROP) {
 		dev_dbg(priv->adapter->dev,
@@ -568,11 +587,14 @@
 		if ((start_win + TWOPOW11) > (MAX_TID_VALUE - 1)) {
 			if (seq_num >= ((start_win + TWOPOW11) &
 					(MAX_TID_VALUE - 1)) &&
-			    seq_num < start_win)
-				return -1;
+			    seq_num < start_win) {
+				ret = -1;
+				goto done;
+			}
 		} else if ((seq_num < start_win) ||
-			   (seq_num > (start_win + TWOPOW11))) {
-			return -1;
+			   (seq_num >= (start_win + TWOPOW11))) {
+			ret = -1;
+			goto done;
 		}
 	}
 
@@ -601,8 +623,10 @@
 		else
 			pkt_index = (seq_num+MAX_TID_VALUE) - start_win;
 
-		if (tbl->rx_reorder_ptr[pkt_index])
-			return -1;
+		if (tbl->rx_reorder_ptr[pkt_index]) {
+			ret = -1;
+			goto done;
+		}
 
 		tbl->rx_reorder_ptr[pkt_index] = payload;
 	}
@@ -613,7 +637,11 @@
 	 */
 	mwifiex_11n_scan_and_dispatch(priv, tbl);
 
-	return 0;
+done:
+	if (!tbl->timer_context.timer_is_set ||
+	    prev_start_win != tbl->start_win)
+		mwifiex_11n_rxreorder_timer_restart(tbl);
+	return ret;
 }
 
 /*
diff --git a/drivers/net/wireless/mwifiex/11n_rxreorder.h b/drivers/net/wireless/mwifiex/11n_rxreorder.h
index 3a87bb0..63ecea8 100644
--- a/drivers/net/wireless/mwifiex/11n_rxreorder.h
+++ b/drivers/net/wireless/mwifiex/11n_rxreorder.h
@@ -21,6 +21,8 @@
 #define _MWIFIEX_11N_RXREORDER_H_
 
 #define MIN_FLUSH_TIMER_MS		50
+#define MIN_FLUSH_TIMER_15_MS		15
+#define MWIFIEX_BA_WIN_SIZE_32		32
 
 #define PKT_TYPE_BAR 0xE7
 #define MAX_TID_VALUE			(2 << 11)
diff --git a/drivers/net/wireless/mwifiex/Kconfig b/drivers/net/wireless/mwifiex/Kconfig
index e70d0df..aa01c9b 100644
--- a/drivers/net/wireless/mwifiex/Kconfig
+++ b/drivers/net/wireless/mwifiex/Kconfig
@@ -31,7 +31,7 @@
 	  mwifiex_pcie.
 
 config MWIFIEX_USB
-	tristate "Marvell WiFi-Ex Driver for USB8797/8897"
+	tristate "Marvell WiFi-Ex Driver for USB8766/8797/8897"
 	depends on MWIFIEX && USB
 	select FW_LOADER
 	---help---
diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c
index 17f0ee0..f881044 100644
--- a/drivers/net/wireless/mwifiex/cfg80211.c
+++ b/drivers/net/wireless/mwifiex/cfg80211.c
@@ -194,10 +194,17 @@
 	tx_info->pkt_len = pkt_len;
 
 	mwifiex_form_mgmt_frame(skb, buf, len);
-	mwifiex_queue_tx_pkt(priv, skb);
-
 	*cookie = prandom_u32() | 1;
-	cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true, GFP_ATOMIC);
+
+	if (ieee80211_is_action(mgmt->frame_control))
+		skb = mwifiex_clone_skb_for_tx_status(priv,
+						      skb,
+				MWIFIEX_BUF_FLAG_ACTION_TX_STATUS, cookie);
+	else
+		cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true,
+					GFP_ATOMIC);
+
+	mwifiex_queue_tx_pkt(priv, skb);
 
 	wiphy_dbg(wiphy, "info: management frame transmitted\n");
 	return 0;
@@ -2988,6 +2995,9 @@
 			   NL80211_FEATURE_INACTIVITY_TIMER |
 			   NL80211_FEATURE_NEED_OBSS_SCAN;
 
+	if (adapter->fw_api_ver == MWIFIEX_FW_V15)
+		wiphy->features |= NL80211_FEATURE_SK_TX_STATUS;
+
 	/* Reserve space for mwifiex specific private data for BSS */
 	wiphy->bss_priv_size = sizeof(struct mwifiex_bss_priv);
 
diff --git a/drivers/net/wireless/mwifiex/decl.h b/drivers/net/wireless/mwifiex/decl.h
index fc0b1ed..2269acf 100644
--- a/drivers/net/wireless/mwifiex/decl.h
+++ b/drivers/net/wireless/mwifiex/decl.h
@@ -76,6 +76,8 @@
 #define MWIFIEX_BUF_FLAG_REQUEUED_PKT      BIT(0)
 #define MWIFIEX_BUF_FLAG_BRIDGED_PKT	   BIT(1)
 #define MWIFIEX_BUF_FLAG_TDLS_PKT	   BIT(2)
+#define MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS   BIT(3)
+#define MWIFIEX_BUF_FLAG_ACTION_TX_STATUS  BIT(4)
 
 #define MWIFIEX_BRIDGED_PKTS_THR_HIGH      1024
 #define MWIFIEX_BRIDGED_PKTS_THR_LOW        128
@@ -159,6 +161,8 @@
 	u8 bss_num;
 	u8 bss_type;
 	u32 pkt_len;
+	u8 ack_frame_id;
+	u64 cookie;
 };
 
 enum mwifiex_wmm_ac_e {
diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h
index e095f37..fb5936e 100644
--- a/drivers/net/wireless/mwifiex/fw.h
+++ b/drivers/net/wireless/mwifiex/fw.h
@@ -494,6 +494,7 @@
 #define EVENT_TDLS_GENERIC_EVENT        0x00000052
 #define EVENT_EXT_SCAN_REPORT           0x00000058
 #define EVENT_REMAIN_ON_CHAN_EXPIRED    0x0000005f
+#define EVENT_TX_STATUS_REPORT		0x00000074
 
 #define EVENT_ID_MASK                   0xffff
 #define BSS_NUM_MASK                    0xf
@@ -542,6 +543,7 @@
 #define MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET 0x08
 #define MWIFIEX_TXPD_FLAGS_TDLS_PACKET      0x10
 #define MWIFIEX_RXPD_FLAGS_TDLS_PACKET      0x01
+#define MWIFIEX_TXPD_FLAGS_REQ_TX_STATUS    0x20
 
 struct txpd {
 	u8 bss_type;
@@ -553,7 +555,9 @@
 	u8 priority;
 	u8 flags;
 	u8 pkt_delay_2ms;
-	u8 reserved1;
+	u8 reserved1[2];
+	u8 tx_token_id;
+	u8 reserved[2];
 } __packed;
 
 struct rxpd {
@@ -598,8 +602,9 @@
 	u8 priority;
 	u8 flags;
 	u8 pkt_delay_2ms;
-	u8 reserved1;
-	__le32 reserved2;
+	u8 reserved1[2];
+	u8 tx_token_id;
+	u8 reserved[2];
 };
 
 struct uap_rxpd {
@@ -1224,6 +1229,12 @@
 	u8 num_of_set;
 } __packed;
 
+struct tx_status_event {
+	u8 packet_type;
+	u8 tx_token_id;
+	u8 status;
+} __packed;
+
 #define MWIFIEX_USER_SCAN_CHAN_MAX             50
 
 #define MWIFIEX_MAX_SSID_LIST_LENGTH         10
diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c
index cc15ab8..520ad4a 100644
--- a/drivers/net/wireless/mwifiex/init.c
+++ b/drivers/net/wireless/mwifiex/init.c
@@ -473,6 +473,9 @@
 
 		spin_lock_init(&priv->tx_ba_stream_tbl_lock);
 		spin_lock_init(&priv->rx_reorder_tbl_lock);
+
+		spin_lock_init(&priv->ack_status_lock);
+		idr_init(&priv->ack_status_frames);
 	}
 
 	return 0;
diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c
index 0e50120..d4d2223 100644
--- a/drivers/net/wireless/mwifiex/main.c
+++ b/drivers/net/wireless/mwifiex/main.c
@@ -149,7 +149,8 @@
 	/* Check for Rx data */
 	while ((skb = skb_dequeue(&adapter->rx_data_q))) {
 		atomic_dec(&adapter->rx_pending);
-		if (adapter->delay_main_work &&
+		if ((adapter->delay_main_work ||
+		     adapter->iface_type == MWIFIEX_USB) &&
 		    (atomic_read(&adapter->rx_pending) < LOW_RX_PENDING)) {
 			if (adapter->if_ops.submit_rem_rx_urbs)
 				adapter->if_ops.submit_rem_rx_urbs(adapter);
@@ -202,12 +203,15 @@
 		    (adapter->hw_status == MWIFIEX_HW_STATUS_NOT_READY))
 			break;
 
-		/* If we process interrupts first, it would increase RX pending
-		 * even further. Avoid this by checking if rx_pending has
-		 * crossed high threshold and schedule rx work queue
-		 * and then process interrupts
+		/* For non-USB interfaces, If we process interrupts first, it
+		 * would increase RX pending even further. Avoid this by
+		 * checking if rx_pending has crossed high threshold and
+		 * schedule rx work queue and then process interrupts.
+		 * For USB interface, there are no interrupts. We already have
+		 * HIGH_RX_PENDING check in usb.c
 		 */
-		if (atomic_read(&adapter->rx_pending) >= HIGH_RX_PENDING) {
+		if (atomic_read(&adapter->rx_pending) >= HIGH_RX_PENDING &&
+		    adapter->iface_type != MWIFIEX_USB) {
 			adapter->delay_main_work = true;
 			if (!adapter->rx_processing)
 				queue_work(adapter->rx_workqueue,
@@ -604,6 +608,48 @@
 	return 0;
 }
 
+struct sk_buff *
+mwifiex_clone_skb_for_tx_status(struct mwifiex_private *priv,
+				struct sk_buff *skb, u8 flag, u64 *cookie)
+{
+	struct sk_buff *orig_skb = skb;
+	struct mwifiex_txinfo *tx_info, *orig_tx_info;
+
+	skb = skb_clone(skb, GFP_ATOMIC);
+	if (skb) {
+		unsigned long flags;
+		int id;
+
+		spin_lock_irqsave(&priv->ack_status_lock, flags);
+		id = idr_alloc(&priv->ack_status_frames, orig_skb,
+			       1, 0xff, GFP_ATOMIC);
+		spin_unlock_irqrestore(&priv->ack_status_lock, flags);
+
+		if (id >= 0) {
+			tx_info = MWIFIEX_SKB_TXCB(skb);
+			tx_info->ack_frame_id = id;
+			tx_info->flags |= flag;
+			orig_tx_info = MWIFIEX_SKB_TXCB(orig_skb);
+			orig_tx_info->ack_frame_id = id;
+			orig_tx_info->flags |= flag;
+
+			if (flag == MWIFIEX_BUF_FLAG_ACTION_TX_STATUS && cookie)
+				orig_tx_info->cookie = *cookie;
+
+		} else if (skb_shared(skb)) {
+			kfree_skb(orig_skb);
+		} else {
+			kfree_skb(skb);
+			skb = orig_skb;
+		}
+	} else {
+		/* couldn't clone -- lose tx status ... */
+		skb = orig_skb;
+	}
+
+	return skb;
+}
+
 /*
  * CFG802.11 network device handler for data transmission.
  */
@@ -613,6 +659,7 @@
 	struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
 	struct sk_buff *new_skb;
 	struct mwifiex_txinfo *tx_info;
+	bool multicast;
 
 	dev_dbg(priv->adapter->dev, "data: %lu BSS(%d-%d): Data <= kernel\n",
 		jiffies, priv->bss_type, priv->bss_num);
@@ -653,6 +700,15 @@
 	tx_info->bss_type = priv->bss_type;
 	tx_info->pkt_len = skb->len;
 
+	multicast = is_multicast_ether_addr(skb->data);
+
+	if (unlikely(!multicast && skb->sk &&
+		     skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS &&
+		     priv->adapter->fw_api_ver == MWIFIEX_FW_V15))
+		skb = mwifiex_clone_skb_for_tx_status(priv,
+						      skb,
+					MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS, NULL);
+
 	/* Record the current time the packet was queued; used to
 	 * determine the amount of time the packet was queued in
 	 * the driver before it was sent to the firmware.
diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h
index 51a67f3..bdba37b 100644
--- a/drivers/net/wireless/mwifiex/main.h
+++ b/drivers/net/wireless/mwifiex/main.h
@@ -34,6 +34,7 @@
 #include <linux/firmware.h>
 #include <linux/ctype.h>
 #include <linux/of.h>
+#include <linux/idr.h>
 
 #include "decl.h"
 #include "ioctl.h"
@@ -578,6 +579,9 @@
 	u8 check_tdls_tx;
 	struct timer_list auto_tdls_timer;
 	bool auto_tdls_timer_active;
+	struct idr ack_status_frames;
+	/* spin lock for ack status */
+	spinlock_t ack_status_lock;
 };
 
 enum mwifiex_ba_status {
@@ -600,6 +604,7 @@
 	struct timer_list timer;
 	struct mwifiex_rx_reorder_tbl *ptr;
 	struct mwifiex_private *priv;
+	u8 timer_is_set;
 };
 
 struct mwifiex_rx_reorder_tbl {
@@ -1334,6 +1339,13 @@
 void mwifiex_setup_auto_tdls_timer(struct mwifiex_private *priv);
 void mwifiex_clean_auto_tdls(struct mwifiex_private *priv);
 
+void mwifiex_parse_tx_status_event(struct mwifiex_private *priv,
+				   void *event_body);
+
+struct sk_buff *
+mwifiex_clone_skb_for_tx_status(struct mwifiex_private *priv,
+				struct sk_buff *skb, u8 flag, u64 *cookie);
+
 #ifdef CONFIG_DEBUG_FS
 void mwifiex_debugfs_init(void);
 void mwifiex_debugfs_remove(void);
diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c
index 3a17821..984a7a4 100644
--- a/drivers/net/wireless/mwifiex/scan.c
+++ b/drivers/net/wireless/mwifiex/scan.c
@@ -1623,7 +1623,7 @@
 
 	if (*bytes_left >= sizeof(beacon_size)) {
 		/* Extract & convert beacon size from command buffer */
-		memcpy(&beacon_size, *bss_info, sizeof(beacon_size));
+		beacon_size = le16_to_cpu(*(__le16 *)(*bss_info));
 		*bytes_left -= sizeof(beacon_size);
 		*bss_info += sizeof(beacon_size);
 	}
diff --git a/drivers/net/wireless/mwifiex/sta_event.c b/drivers/net/wireless/mwifiex/sta_event.c
index 204ecc8..b8c171d 100644
--- a/drivers/net/wireless/mwifiex/sta_event.c
+++ b/drivers/net/wireless/mwifiex/sta_event.c
@@ -504,6 +504,11 @@
 		ret = mwifiex_parse_tdls_event(priv, adapter->event_skb);
 		break;
 
+	case EVENT_TX_STATUS_REPORT:
+		dev_dbg(adapter->dev, "event: TX_STATUS Report\n");
+		mwifiex_parse_tx_status_event(priv, adapter->event_body);
+		break;
+
 	default:
 		dev_dbg(adapter->dev, "event: unknown event id: %#x\n",
 			eventcause);
diff --git a/drivers/net/wireless/mwifiex/sta_tx.c b/drivers/net/wireless/mwifiex/sta_tx.c
index dab7b33..b896d73 100644
--- a/drivers/net/wireless/mwifiex/sta_tx.c
+++ b/drivers/net/wireless/mwifiex/sta_tx.c
@@ -77,6 +77,12 @@
 	local_tx_pd->pkt_delay_2ms =
 				mwifiex_wmm_compute_drv_pkt_delay(priv, skb);
 
+	if (tx_info->flags & MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS ||
+	    tx_info->flags & MWIFIEX_BUF_FLAG_ACTION_TX_STATUS) {
+		local_tx_pd->tx_token_id = tx_info->ack_frame_id;
+		local_tx_pd->flags |= MWIFIEX_TXPD_FLAGS_REQ_TX_STATUS;
+	}
+
 	if (local_tx_pd->priority <
 	    ARRAY_SIZE(priv->wmm.user_pri_pkt_tx_ctrl))
 		/*
diff --git a/drivers/net/wireless/mwifiex/txrx.c b/drivers/net/wireless/mwifiex/txrx.c
index a5983fc..6ae1333 100644
--- a/drivers/net/wireless/mwifiex/txrx.c
+++ b/drivers/net/wireless/mwifiex/txrx.c
@@ -203,3 +203,34 @@
 }
 EXPORT_SYMBOL_GPL(mwifiex_write_data_complete);
 
+void mwifiex_parse_tx_status_event(struct mwifiex_private *priv,
+				   void *event_body)
+{
+	struct tx_status_event *tx_status = (void *)priv->adapter->event_body;
+	struct sk_buff *ack_skb;
+	unsigned long flags;
+	struct mwifiex_txinfo *tx_info;
+
+	if (!tx_status->tx_token_id)
+		return;
+
+	spin_lock_irqsave(&priv->ack_status_lock, flags);
+	ack_skb = idr_find(&priv->ack_status_frames, tx_status->tx_token_id);
+	if (ack_skb)
+		idr_remove(&priv->ack_status_frames, tx_status->tx_token_id);
+	spin_unlock_irqrestore(&priv->ack_status_lock, flags);
+
+	if (ack_skb) {
+		tx_info = MWIFIEX_SKB_TXCB(ack_skb);
+
+		if (tx_info->flags & MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS) {
+			/* consumes ack_skb */
+			skb_complete_wifi_ack(ack_skb, !tx_status->status);
+		} else {
+			cfg80211_mgmt_tx_status(priv->wdev, tx_info->cookie,
+						ack_skb->data, ack_skb->len,
+						!tx_status->status, GFP_ATOMIC);
+			dev_kfree_skb_any(ack_skb);
+		}
+	}
+}
diff --git a/drivers/net/wireless/mwifiex/uap_event.c b/drivers/net/wireless/mwifiex/uap_event.c
index 7c2b9766..38390cb 100644
--- a/drivers/net/wireless/mwifiex/uap_event.c
+++ b/drivers/net/wireless/mwifiex/uap_event.c
@@ -172,6 +172,10 @@
 			return mwifiex_handle_event_ext_scan_report(priv,
 						adapter->event_skb->data);
 		break;
+	case EVENT_TX_STATUS_REPORT:
+		dev_dbg(adapter->dev, "event: TX_STATUS Report\n");
+		mwifiex_parse_tx_status_event(priv, adapter->event_body);
+		break;
 	default:
 		dev_dbg(adapter->dev, "event: unknown event id: %#x\n",
 			eventcause);
diff --git a/drivers/net/wireless/mwifiex/uap_txrx.c b/drivers/net/wireless/mwifiex/uap_txrx.c
index ec7309d..e7d326f 100644
--- a/drivers/net/wireless/mwifiex/uap_txrx.c
+++ b/drivers/net/wireless/mwifiex/uap_txrx.c
@@ -370,10 +370,16 @@
 	txpd->bss_num = priv->bss_num;
 	txpd->bss_type = priv->bss_type;
 	txpd->tx_pkt_length = cpu_to_le16((u16)(skb->len - len));
-
 	txpd->priority = (u8)skb->priority;
+
 	txpd->pkt_delay_2ms = mwifiex_wmm_compute_drv_pkt_delay(priv, skb);
 
+	if (tx_info->flags & MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS ||
+	    tx_info->flags & MWIFIEX_BUF_FLAG_ACTION_TX_STATUS) {
+		txpd->tx_token_id = tx_info->ack_frame_id;
+		txpd->flags |= MWIFIEX_TXPD_FLAGS_REQ_TX_STATUS;
+	}
+
 	if (txpd->priority < ARRAY_SIZE(priv->wmm.user_pri_pkt_tx_ctrl))
 		/*
 		 * Set the priority specific tx_control field, setting of 0 will
diff --git a/drivers/net/wireless/mwifiex/usb.c b/drivers/net/wireless/mwifiex/usb.c
index 6cc8519..1b56495 100644
--- a/drivers/net/wireless/mwifiex/usb.c
+++ b/drivers/net/wireless/mwifiex/usb.c
@@ -27,6 +27,11 @@
 static struct semaphore add_remove_card_sem;
 
 static struct usb_device_id mwifiex_usb_table[] = {
+	/* 8766 */
+	{USB_DEVICE(USB8XXX_VID, USB8766_PID_1)},
+	{USB_DEVICE_AND_INTERFACE_INFO(USB8XXX_VID, USB8766_PID_2,
+				       USB_CLASS_VENDOR_SPEC,
+				       USB_SUBCLASS_VENDOR_SPEC, 0xff)},
 	/* 8797 */
 	{USB_DEVICE(USB8XXX_VID, USB8797_PID_1)},
 	{USB_DEVICE_AND_INTERFACE_INFO(USB8XXX_VID, USB8797_PID_2,
@@ -354,10 +359,12 @@
 
 	/* PID_1 is used for firmware downloading only */
 	switch (id_product) {
+	case USB8766_PID_1:
 	case USB8797_PID_1:
 	case USB8897_PID_1:
 		card->usb_boot_state = USB8XXX_FW_DNLD;
 		break;
+	case USB8766_PID_2:
 	case USB8797_PID_2:
 	case USB8897_PID_2:
 		card->usb_boot_state = USB8XXX_FW_READY;
@@ -786,6 +793,11 @@
 		adapter->tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K;
 		strcpy(adapter->fw_name, USB8897_DEFAULT_FW_NAME);
 		break;
+	case USB8766_PID_1:
+	case USB8766_PID_2:
+		adapter->tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K;
+		strcpy(adapter->fw_name, USB8766_DEFAULT_FW_NAME);
+		break;
 	case USB8797_PID_1:
 	case USB8797_PID_2:
 	default:
@@ -1060,5 +1072,6 @@
 MODULE_DESCRIPTION("Marvell WiFi-Ex USB Driver version" USB_VERSION);
 MODULE_VERSION(USB_VERSION);
 MODULE_LICENSE("GPL v2");
+MODULE_FIRMWARE(USB8766_DEFAULT_FW_NAME);
 MODULE_FIRMWARE(USB8797_DEFAULT_FW_NAME);
 MODULE_FIRMWARE(USB8897_DEFAULT_FW_NAME);
diff --git a/drivers/net/wireless/mwifiex/usb.h b/drivers/net/wireless/mwifiex/usb.h
index 4c41c2a..a7cbba1 100644
--- a/drivers/net/wireless/mwifiex/usb.h
+++ b/drivers/net/wireless/mwifiex/usb.h
@@ -24,6 +24,8 @@
 
 #define USB8XXX_VID		0x1286
 
+#define USB8766_PID_1		0x2041
+#define USB8766_PID_2		0x2042
 #define USB8797_PID_1		0x2043
 #define USB8797_PID_2		0x2044
 #define USB8897_PID_1		0x2045
@@ -37,6 +39,7 @@
 #define MWIFIEX_RX_DATA_URB	6
 #define MWIFIEX_USB_TIMEOUT	100
 
+#define USB8766_DEFAULT_FW_NAME	"mrvl/usb8766_uapsta.bin"
 #define USB8797_DEFAULT_FW_NAME	"mrvl/usb8797_uapsta.bin"
 #define USB8897_DEFAULT_FW_NAME	"mrvl/usb8897_uapsta.bin"
 
diff --git a/drivers/net/wireless/mwifiex/util.c b/drivers/net/wireless/mwifiex/util.c
index a113ef8..b1768fb 100644
--- a/drivers/net/wireless/mwifiex/util.c
+++ b/drivers/net/wireless/mwifiex/util.c
@@ -149,7 +149,7 @@
 	u8 category, action_code;
 	struct ieee80211_hdr *ieee_hdr = (void *)payload;
 
-	stype =  (cpu_to_le16(ieee_hdr->frame_control) & IEEE80211_FCTL_STYPE);
+	stype = (le16_to_cpu(ieee_hdr->frame_control) & IEEE80211_FCTL_STYPE);
 
 	switch (stype) {
 	case IEEE80211_STYPE_ACTION:
diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c
index 94c98a8..dc1f2ad 100644
--- a/drivers/net/wireless/mwifiex/wmm.c
+++ b/drivers/net/wireless/mwifiex/wmm.c
@@ -523,6 +523,13 @@
 	}
 }
 
+static int mwifiex_free_ack_frame(int id, void *p, void *data)
+{
+	pr_warn("Have pending ack frames!\n");
+	kfree_skb(p);
+	return 0;
+}
+
 /*
  * This function cleans up the Tx and Rx queues.
  *
@@ -558,6 +565,9 @@
 
 	skb_queue_walk_safe(&priv->tdls_txq, skb, tmp)
 		mwifiex_write_data_complete(priv->adapter, skb, 0, -1);
+
+	idr_for_each(&priv->ack_status_frames, mwifiex_free_ack_frame, NULL);
+	idr_destroy(&priv->ack_status_frames);
 }
 
 /*
diff --git a/drivers/net/wireless/p54/net2280.h b/drivers/net/wireless/p54/net2280.h
deleted file mode 100644
index aedfaf2..0000000
--- a/drivers/net/wireless/p54/net2280.h
+++ /dev/null
@@ -1,451 +0,0 @@
-#ifndef NET2280_H
-#define NET2280_H
-/*
- * NetChip 2280 high/full speed USB device controller.
- * Unlike many such controllers, this one talks PCI.
- */
-
-/*
- * Copyright (C) 2002 NetChip Technology, Inc. (http://www.netchip.com)
- * Copyright (C) 2003 David Brownell
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-/*-------------------------------------------------------------------------*/
-
-/* NET2280 MEMORY MAPPED REGISTERS
- *
- * The register layout came from the chip documentation, and the bit
- * number definitions were extracted from chip specification.
- *
- * Use the shift operator ('<<') to build bit masks, with readl/writel
- * to access the registers through PCI.
- */
-
-/* main registers, BAR0 + 0x0000 */
-struct net2280_regs {
-	/* offset 0x0000 */
-	__le32			devinit;
-#define LOCAL_CLOCK_FREQUENCY					8
-#define FORCE_PCI_RESET						7
-#define PCI_ID							6
-#define PCI_ENABLE						5
-#define FIFO_SOFT_RESET						4
-#define CFG_SOFT_RESET						3
-#define PCI_SOFT_RESET						2
-#define USB_SOFT_RESET						1
-#define M8051_RESET						0
-	__le32			eectl;
-#define EEPROM_ADDRESS_WIDTH					23
-#define EEPROM_CHIP_SELECT_ACTIVE				22
-#define EEPROM_PRESENT						21
-#define EEPROM_VALID						20
-#define EEPROM_BUSY						19
-#define EEPROM_CHIP_SELECT_ENABLE				18
-#define EEPROM_BYTE_READ_START					17
-#define EEPROM_BYTE_WRITE_START					16
-#define EEPROM_READ_DATA					8
-#define EEPROM_WRITE_DATA					0
-	__le32			eeclkfreq;
-	u32			_unused0;
-	/* offset 0x0010 */
-
-	__le32			pciirqenb0;	/* interrupt PCI master ... */
-#define SETUP_PACKET_INTERRUPT_ENABLE				7
-#define ENDPOINT_F_INTERRUPT_ENABLE				6
-#define ENDPOINT_E_INTERRUPT_ENABLE				5
-#define ENDPOINT_D_INTERRUPT_ENABLE				4
-#define ENDPOINT_C_INTERRUPT_ENABLE				3
-#define ENDPOINT_B_INTERRUPT_ENABLE				2
-#define ENDPOINT_A_INTERRUPT_ENABLE				1
-#define ENDPOINT_0_INTERRUPT_ENABLE				0
-	__le32			pciirqenb1;
-#define PCI_INTERRUPT_ENABLE					31
-#define POWER_STATE_CHANGE_INTERRUPT_ENABLE			27
-#define PCI_ARBITER_TIMEOUT_INTERRUPT_ENABLE			26
-#define PCI_PARITY_ERROR_INTERRUPT_ENABLE			25
-#define PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE		20
-#define PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE		19
-#define PCI_TARGET_ABORT_ASSERTED_INTERRUPT_ENABLE		18
-#define PCI_RETRY_ABORT_INTERRUPT_ENABLE			17
-#define PCI_MASTER_CYCLE_DONE_INTERRUPT_ENABLE			16
-#define GPIO_INTERRUPT_ENABLE					13
-#define DMA_D_INTERRUPT_ENABLE					12
-#define DMA_C_INTERRUPT_ENABLE					11
-#define DMA_B_INTERRUPT_ENABLE					10
-#define DMA_A_INTERRUPT_ENABLE					9
-#define EEPROM_DONE_INTERRUPT_ENABLE				8
-#define VBUS_INTERRUPT_ENABLE					7
-#define CONTROL_STATUS_INTERRUPT_ENABLE				6
-#define ROOT_PORT_RESET_INTERRUPT_ENABLE			4
-#define SUSPEND_REQUEST_INTERRUPT_ENABLE			3
-#define SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE			2
-#define RESUME_INTERRUPT_ENABLE					1
-#define SOF_INTERRUPT_ENABLE					0
-	__le32                  cpu_irqenb0;	/* ... or onboard 8051 */
-#define SETUP_PACKET_INTERRUPT_ENABLE				7
-#define ENDPOINT_F_INTERRUPT_ENABLE				6
-#define ENDPOINT_E_INTERRUPT_ENABLE				5
-#define ENDPOINT_D_INTERRUPT_ENABLE				4
-#define ENDPOINT_C_INTERRUPT_ENABLE				3
-#define ENDPOINT_B_INTERRUPT_ENABLE				2
-#define ENDPOINT_A_INTERRUPT_ENABLE				1
-#define ENDPOINT_0_INTERRUPT_ENABLE				0
-	__le32                  cpu_irqenb1;
-#define CPU_INTERRUPT_ENABLE					31
-#define POWER_STATE_CHANGE_INTERRUPT_ENABLE			27
-#define PCI_ARBITER_TIMEOUT_INTERRUPT_ENABLE			26
-#define PCI_PARITY_ERROR_INTERRUPT_ENABLE			25
-#define PCI_INTA_INTERRUPT_ENABLE				24
-#define PCI_PME_INTERRUPT_ENABLE				23
-#define PCI_SERR_INTERRUPT_ENABLE				22
-#define PCI_PERR_INTERRUPT_ENABLE				21
-#define PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE		20
-#define PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE		19
-#define PCI_RETRY_ABORT_INTERRUPT_ENABLE			17
-#define PCI_MASTER_CYCLE_DONE_INTERRUPT_ENABLE			16
-#define GPIO_INTERRUPT_ENABLE					13
-#define DMA_D_INTERRUPT_ENABLE					12
-#define DMA_C_INTERRUPT_ENABLE					11
-#define DMA_B_INTERRUPT_ENABLE					10
-#define DMA_A_INTERRUPT_ENABLE					9
-#define EEPROM_DONE_INTERRUPT_ENABLE				8
-#define VBUS_INTERRUPT_ENABLE					7
-#define CONTROL_STATUS_INTERRUPT_ENABLE				6
-#define ROOT_PORT_RESET_INTERRUPT_ENABLE			4
-#define SUSPEND_REQUEST_INTERRUPT_ENABLE			3
-#define SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE			2
-#define RESUME_INTERRUPT_ENABLE					1
-#define SOF_INTERRUPT_ENABLE					0
-
-	/* offset 0x0020 */
-	u32			_unused1;
-	__le32			usbirqenb1;
-#define USB_INTERRUPT_ENABLE					31
-#define POWER_STATE_CHANGE_INTERRUPT_ENABLE			27
-#define PCI_ARBITER_TIMEOUT_INTERRUPT_ENABLE			26
-#define PCI_PARITY_ERROR_INTERRUPT_ENABLE			25
-#define PCI_INTA_INTERRUPT_ENABLE				24
-#define PCI_PME_INTERRUPT_ENABLE				23
-#define PCI_SERR_INTERRUPT_ENABLE				22
-#define PCI_PERR_INTERRUPT_ENABLE				21
-#define PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE		20
-#define PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE		19
-#define PCI_RETRY_ABORT_INTERRUPT_ENABLE			17
-#define PCI_MASTER_CYCLE_DONE_INTERRUPT_ENABLE			16
-#define GPIO_INTERRUPT_ENABLE					13
-#define DMA_D_INTERRUPT_ENABLE					12
-#define DMA_C_INTERRUPT_ENABLE					11
-#define DMA_B_INTERRUPT_ENABLE					10
-#define DMA_A_INTERRUPT_ENABLE					9
-#define EEPROM_DONE_INTERRUPT_ENABLE				8
-#define VBUS_INTERRUPT_ENABLE					7
-#define CONTROL_STATUS_INTERRUPT_ENABLE				6
-#define ROOT_PORT_RESET_INTERRUPT_ENABLE			4
-#define SUSPEND_REQUEST_INTERRUPT_ENABLE			3
-#define SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE			2
-#define RESUME_INTERRUPT_ENABLE					1
-#define SOF_INTERRUPT_ENABLE					0
-	__le32			irqstat0;
-#define INTA_ASSERTED						12
-#define SETUP_PACKET_INTERRUPT					7
-#define ENDPOINT_F_INTERRUPT					6
-#define ENDPOINT_E_INTERRUPT					5
-#define ENDPOINT_D_INTERRUPT					4
-#define ENDPOINT_C_INTERRUPT					3
-#define ENDPOINT_B_INTERRUPT					2
-#define ENDPOINT_A_INTERRUPT					1
-#define ENDPOINT_0_INTERRUPT					0
-	__le32			irqstat1;
-#define POWER_STATE_CHANGE_INTERRUPT				27
-#define PCI_ARBITER_TIMEOUT_INTERRUPT				26
-#define PCI_PARITY_ERROR_INTERRUPT				25
-#define PCI_INTA_INTERRUPT					24
-#define PCI_PME_INTERRUPT					23
-#define PCI_SERR_INTERRUPT					22
-#define PCI_PERR_INTERRUPT					21
-#define PCI_MASTER_ABORT_RECEIVED_INTERRUPT			20
-#define PCI_TARGET_ABORT_RECEIVED_INTERRUPT			19
-#define PCI_RETRY_ABORT_INTERRUPT				17
-#define PCI_MASTER_CYCLE_DONE_INTERRUPT				16
-#define GPIO_INTERRUPT						13
-#define DMA_D_INTERRUPT						12
-#define DMA_C_INTERRUPT						11
-#define DMA_B_INTERRUPT						10
-#define DMA_A_INTERRUPT						9
-#define EEPROM_DONE_INTERRUPT					8
-#define VBUS_INTERRUPT						7
-#define CONTROL_STATUS_INTERRUPT				6
-#define ROOT_PORT_RESET_INTERRUPT				4
-#define SUSPEND_REQUEST_INTERRUPT				3
-#define SUSPEND_REQUEST_CHANGE_INTERRUPT			2
-#define RESUME_INTERRUPT					1
-#define SOF_INTERRUPT						0
-	/* offset 0x0030 */
-	__le32			idxaddr;
-	__le32			idxdata;
-	__le32			fifoctl;
-#define PCI_BASE2_RANGE						16
-#define IGNORE_FIFO_AVAILABILITY				3
-#define PCI_BASE2_SELECT					2
-#define FIFO_CONFIGURATION_SELECT				0
-	u32			_unused2;
-	/* offset 0x0040 */
-	__le32			memaddr;
-#define START							28
-#define DIRECTION						27
-#define FIFO_DIAGNOSTIC_SELECT					24
-#define MEMORY_ADDRESS						0
-	__le32			memdata0;
-	__le32			memdata1;
-	u32			_unused3;
-	/* offset 0x0050 */
-	__le32			gpioctl;
-#define GPIO3_LED_SELECT					12
-#define GPIO3_INTERRUPT_ENABLE					11
-#define GPIO2_INTERRUPT_ENABLE					10
-#define GPIO1_INTERRUPT_ENABLE					9
-#define GPIO0_INTERRUPT_ENABLE					8
-#define GPIO3_OUTPUT_ENABLE					7
-#define GPIO2_OUTPUT_ENABLE					6
-#define GPIO1_OUTPUT_ENABLE					5
-#define GPIO0_OUTPUT_ENABLE					4
-#define GPIO3_DATA						3
-#define GPIO2_DATA						2
-#define GPIO1_DATA						1
-#define GPIO0_DATA						0
-	__le32			gpiostat;
-#define GPIO3_INTERRUPT						3
-#define GPIO2_INTERRUPT						2
-#define GPIO1_INTERRUPT						1
-#define GPIO0_INTERRUPT						0
-} __packed;
-
-/* usb control, BAR0 + 0x0080 */
-struct net2280_usb_regs {
-	/* offset 0x0080 */
-	__le32			stdrsp;
-#define STALL_UNSUPPORTED_REQUESTS				31
-#define SET_TEST_MODE						16
-#define GET_OTHER_SPEED_CONFIGURATION				15
-#define GET_DEVICE_QUALIFIER					14
-#define SET_ADDRESS						13
-#define ENDPOINT_SET_CLEAR_HALT					12
-#define DEVICE_SET_CLEAR_DEVICE_REMOTE_WAKEUP			11
-#define GET_STRING_DESCRIPTOR_2					10
-#define GET_STRING_DESCRIPTOR_1					9
-#define GET_STRING_DESCRIPTOR_0					8
-#define GET_SET_INTERFACE					6
-#define GET_SET_CONFIGURATION					5
-#define GET_CONFIGURATION_DESCRIPTOR				4
-#define GET_DEVICE_DESCRIPTOR					3
-#define GET_ENDPOINT_STATUS					2
-#define GET_INTERFACE_STATUS					1
-#define GET_DEVICE_STATUS					0
-	__le32			prodvendid;
-#define     PRODUCT_ID						16
-#define     VENDOR_ID						0
-	__le32			relnum;
-	__le32			usbctl;
-#define SERIAL_NUMBER_INDEX					16
-#define PRODUCT_ID_STRING_ENABLE				13
-#define VENDOR_ID_STRING_ENABLE					12
-#define USB_ROOT_PORT_WAKEUP_ENABLE				11
-#define VBUS_PIN						10
-#define TIMED_DISCONNECT					9
-#define SUSPEND_IMMEDIATELY					7
-#define SELF_POWERED_USB_DEVICE					6
-#define REMOTE_WAKEUP_SUPPORT					5
-#define PME_POLARITY						4
-#define USB_DETECT_ENABLE					3
-#define PME_WAKEUP_ENABLE					2
-#define DEVICE_REMOTE_WAKEUP_ENABLE				1
-#define SELF_POWERED_STATUS					0
-	/* offset 0x0090 */
-	__le32			usbstat;
-#define HIGH_SPEED						7
-#define FULL_SPEED						6
-#define GENERATE_RESUME						5
-#define GENERATE_DEVICE_REMOTE_WAKEUP				4
-	__le32			xcvrdiag;
-#define FORCE_HIGH_SPEED_MODE					31
-#define FORCE_FULL_SPEED_MODE					30
-#define USB_TEST_MODE						24
-#define LINE_STATE						16
-#define TRANSCEIVER_OPERATION_MODE				2
-#define TRANSCEIVER_SELECT					1
-#define TERMINATION_SELECT					0
-	__le32			setup0123;
-	__le32			setup4567;
-	/* offset 0x0090 */
-	u32			_unused0;
-	__le32			ouraddr;
-#define FORCE_IMMEDIATE						7
-#define OUR_USB_ADDRESS						0
-	__le32			ourconfig;
-} __packed;
-
-/* pci control, BAR0 + 0x0100 */
-struct net2280_pci_regs {
-	/* offset 0x0100 */
-	__le32			pcimstctl;
-#define PCI_ARBITER_PARK_SELECT					13
-#define PCI_MULTI LEVEL_ARBITER					12
-#define PCI_RETRY_ABORT_ENABLE					11
-#define DMA_MEMORY_WRITE_AND_INVALIDATE_ENABLE			10
-#define DMA_READ_MULTIPLE_ENABLE				9
-#define DMA_READ_LINE_ENABLE					8
-#define PCI_MASTER_COMMAND_SELECT				6
-#define		MEM_READ_OR_WRITE				0
-#define		IO_READ_OR_WRITE				1
-#define		CFG_READ_OR_WRITE				2
-#define PCI_MASTER_START					5
-#define PCI_MASTER_READ_WRITE					4
-#define		PCI_MASTER_WRITE				0
-#define		PCI_MASTER_READ					1
-#define PCI_MASTER_BYTE_WRITE_ENABLES				0
-	__le32			pcimstaddr;
-	__le32			pcimstdata;
-	__le32			pcimststat;
-#define PCI_ARBITER_CLEAR					2
-#define PCI_EXTERNAL_ARBITER					1
-#define PCI_HOST_MODE						0
-} __packed;
-
-/* dma control, BAR0 + 0x0180 ... array of four structs like this,
- * for channels 0..3.  see also struct net2280_dma:  descriptor
- * that can be loaded into some of these registers.
- */
-struct net2280_dma_regs {	/* [11.7] */
-	/* offset 0x0180, 0x01a0, 0x01c0, 0x01e0, */
-	__le32			dmactl;
-#define DMA_SCATTER_GATHER_DONE_INTERRUPT_ENABLE		25
-#define DMA_CLEAR_COUNT_ENABLE					21
-#define DESCRIPTOR_POLLING_RATE					19
-#define		POLL_CONTINUOUS					0
-#define		POLL_1_USEC					1
-#define		POLL_100_USEC					2
-#define		POLL_1_MSEC					3
-#define DMA_VALID_BIT_POLLING_ENABLE				18
-#define DMA_VALID_BIT_ENABLE					17
-#define DMA_SCATTER_GATHER_ENABLE				16
-#define DMA_OUT_AUTO_START_ENABLE				4
-#define DMA_PREEMPT_ENABLE					3
-#define DMA_FIFO_VALIDATE					2
-#define DMA_ENABLE						1
-#define DMA_ADDRESS_HOLD					0
-	__le32			dmastat;
-#define DMA_SCATTER_GATHER_DONE_INTERRUPT			25
-#define DMA_TRANSACTION_DONE_INTERRUPT				24
-#define DMA_ABORT						1
-#define DMA_START						0
-	u32			_unused0[2];
-	/* offset 0x0190, 0x01b0, 0x01d0, 0x01f0, */
-	__le32                  dmacount;
-#define VALID_BIT						31
-#define DMA_DIRECTION						30
-#define DMA_DONE_INTERRUPT_ENABLE				29
-#define END_OF_CHAIN						28
-#define DMA_BYTE_COUNT_MASK					((1<<24)-1)
-#define DMA_BYTE_COUNT						0
-	__le32			dmaaddr;
-	__le32			dmadesc;
-	u32			_unused1;
-} __packed;
-
-/* dedicated endpoint registers, BAR0 + 0x0200 */
-
-struct net2280_dep_regs {	/* [11.8] */
-	/* offset 0x0200, 0x0210, 0x220, 0x230, 0x240 */
-	__le32			dep_cfg;
-	/* offset 0x0204, 0x0214, 0x224, 0x234, 0x244 */
-	__le32			dep_rsp;
-	u32			_unused[2];
-} __packed;
-
-/* configurable endpoint registers, BAR0 + 0x0300 ... array of seven structs
- * like this, for ep0 then the configurable endpoints A..F
- * ep0 reserved for control; E and F have only 64 bytes of fifo
- */
-struct net2280_ep_regs {	/* [11.9] */
-	/* offset 0x0300, 0x0320, 0x0340, 0x0360, 0x0380, 0x03a0, 0x03c0 */
-	__le32			ep_cfg;
-#define ENDPOINT_BYTE_COUNT					16
-#define ENDPOINT_ENABLE						10
-#define ENDPOINT_TYPE						8
-#define ENDPOINT_DIRECTION					7
-#define ENDPOINT_NUMBER						0
-	__le32			ep_rsp;
-#define SET_NAK_OUT_PACKETS					15
-#define SET_EP_HIDE_STATUS_PHASE				14
-#define SET_EP_FORCE_CRC_ERROR					13
-#define SET_INTERRUPT_MODE					12
-#define SET_CONTROL_STATUS_PHASE_HANDSHAKE			11
-#define SET_NAK_OUT_PACKETS_MODE				10
-#define SET_ENDPOINT_TOGGLE					9
-#define SET_ENDPOINT_HALT					8
-#define CLEAR_NAK_OUT_PACKETS					7
-#define CLEAR_EP_HIDE_STATUS_PHASE				6
-#define CLEAR_EP_FORCE_CRC_ERROR				5
-#define CLEAR_INTERRUPT_MODE					4
-#define CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE			3
-#define CLEAR_NAK_OUT_PACKETS_MODE				2
-#define CLEAR_ENDPOINT_TOGGLE					1
-#define CLEAR_ENDPOINT_HALT					0
-	__le32			ep_irqenb;
-#define SHORT_PACKET_OUT_DONE_INTERRUPT_ENABLE			6
-#define SHORT_PACKET_TRANSFERRED_INTERRUPT_ENABLE		5
-#define DATA_PACKET_RECEIVED_INTERRUPT_ENABLE			3
-#define DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE		2
-#define DATA_OUT_PING_TOKEN_INTERRUPT_ENABLE			1
-#define DATA_IN_TOKEN_INTERRUPT_ENABLE				0
-	__le32			ep_stat;
-#define FIFO_VALID_COUNT					24
-#define HIGH_BANDWIDTH_OUT_TRANSACTION_PID			22
-#define TIMEOUT							21
-#define USB_STALL_SENT						20
-#define USB_IN_NAK_SENT						19
-#define USB_IN_ACK_RCVD						18
-#define USB_OUT_PING_NAK_SENT					17
-#define USB_OUT_ACK_SENT					16
-#define FIFO_OVERFLOW						13
-#define FIFO_UNDERFLOW						12
-#define FIFO_FULL						11
-#define FIFO_EMPTY						10
-#define FIFO_FLUSH						9
-#define SHORT_PACKET_OUT_DONE_INTERRUPT				6
-#define SHORT_PACKET_TRANSFERRED_INTERRUPT			5
-#define NAK_OUT_PACKETS						4
-#define DATA_PACKET_RECEIVED_INTERRUPT				3
-#define DATA_PACKET_TRANSMITTED_INTERRUPT			2
-#define DATA_OUT_PING_TOKEN_INTERRUPT				1
-#define DATA_IN_TOKEN_INTERRUPT					0
-	/* offset 0x0310, 0x0330, 0x0350, 0x0370, 0x0390, 0x03b0, 0x03d0 */
-	__le32			ep_avail;
-	__le32			ep_data;
-	u32			_unused0[2];
-} __packed;
-
-struct net2280_reg_write {
-	__le16 port;
-	__le32 addr;
-	__le32 val;
-} __packed;
-
-struct net2280_reg_read {
-	__le16 port;
-	__le32 addr;
-} __packed;
-#endif /* NET2280_H */
diff --git a/drivers/net/wireless/p54/p54usb.h b/drivers/net/wireless/p54/p54usb.h
index d273be7..a5f5f0f 100644
--- a/drivers/net/wireless/p54/p54usb.h
+++ b/drivers/net/wireless/p54/p54usb.h
@@ -16,7 +16,7 @@
 
 /* for isl3886 register definitions used on ver 1 devices */
 #include "p54pci.h"
-#include "net2280.h"
+#include <linux/usb/net2280.h>
 
 /* pci */
 #define NET2280_BASE		0x10000000
@@ -93,6 +93,17 @@
 	NET2280_DEV_CFG_U16	= 0x0883
 };
 
+struct net2280_reg_write {
+	__le16 port;
+	__le32 addr;
+	__le32 val;
+} __packed;
+
+struct net2280_reg_read {
+	__le16 port;
+	__le32 addr;
+} __packed;
+
 #define P54U_FW_BLOCK 2048
 
 #define X2_SIGNATURE "x2  "
diff --git a/drivers/net/wireless/rt2x00/rt2500usb.c b/drivers/net/wireless/rt2x00/rt2500usb.c
index c878e3f..05c6459 100644
--- a/drivers/net/wireless/rt2x00/rt2500usb.c
+++ b/drivers/net/wireless/rt2x00/rt2500usb.c
@@ -47,7 +47,7 @@
  * BBP and RF register require indirect register access,
  * and use the CSR registers BBPCSR and RFCSR to achieve this.
  * These indirect registers work with busy bits,
- * and we will try maximal REGISTER_BUSY_COUNT times to access
+ * and we will try maximal REGISTER_USB_BUSY_COUNT times to access
  * the register while taking a REGISTER_BUSY_DELAY us delay
  * between each attampt. When the busy bit is still set at that time,
  * the access attempt is considered to have failed,
@@ -122,7 +122,7 @@
 {
 	unsigned int i;
 
-	for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
+	for (i = 0; i < REGISTER_USB_BUSY_COUNT; i++) {
 		rt2500usb_register_read_lock(rt2x00dev, offset, reg);
 		if (!rt2x00_get_field16(*reg, field))
 			return 1;
@@ -904,7 +904,7 @@
 	unsigned int i;
 	u8 value;
 
-	for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
+	for (i = 0; i < REGISTER_USB_BUSY_COUNT; i++) {
 		rt2500usb_bbp_read(rt2x00dev, 0, &value);
 		if ((value != 0xff) && (value != 0x00))
 			return 0;
@@ -1023,7 +1023,7 @@
 	 * We must wait until the register indicates that the
 	 * device has entered the correct state.
 	 */
-	for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
+	for (i = 0; i < REGISTER_USB_BUSY_COUNT; i++) {
 		rt2500usb_register_read(rt2x00dev, MAC_CSR17, &reg2);
 		bbp_state = rt2x00_get_field16(reg2, MAC_CSR17_BBP_CURR_STATE);
 		rf_state = rt2x00_get_field16(reg2, MAC_CSR17_RF_CURR_STATE);
diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c
index 9f57a2d..81ee481 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/rt2x00/rt2800lib.c
@@ -4119,7 +4119,20 @@
 	 * expected. We adjust it, based on TSSI reference and boundaries values
 	 * provided in EEPROM.
 	 */
-	delta += rt2800_get_gain_calibration_delta(rt2x00dev);
+	switch (rt2x00dev->chip.rt) {
+	case RT2860:
+	case RT2872:
+	case RT2883:
+	case RT3070:
+	case RT3071:
+	case RT3090:
+	case RT3572:
+		delta += rt2800_get_gain_calibration_delta(rt2x00dev);
+		break;
+	default:
+		/* TODO: temperature compensation code for other chips. */
+		break;
+	}
 
 	/*
 	 * Decrease power according to user settings, on devices with unknown
@@ -4136,25 +4149,19 @@
 	 * TODO: we do not use +6 dBm option to do not increase power beyond
 	 * regulatory limit, however this could be utilized for devices with
 	 * CAPABILITY_POWER_LIMIT.
-	 *
-	 * TODO: add different temperature compensation code for RT3290 & RT5390
-	 * to allow to use BBP_R1 for those chips.
 	 */
-	if (!rt2x00_rt(rt2x00dev, RT3290) &&
-	    !rt2x00_rt(rt2x00dev, RT5390)) {
-		rt2800_bbp_read(rt2x00dev, 1, &r1);
-		if (delta <= -12) {
-			power_ctrl = 2;
-			delta += 12;
-		} else if (delta <= -6) {
-			power_ctrl = 1;
-			delta += 6;
-		} else {
-			power_ctrl = 0;
-		}
-		rt2x00_set_field8(&r1, BBP1_TX_POWER_CTRL, power_ctrl);
-		rt2800_bbp_write(rt2x00dev, 1, r1);
+	if (delta <= -12) {
+		power_ctrl = 2;
+		delta += 12;
+	} else if (delta <= -6) {
+		power_ctrl = 1;
+		delta += 6;
+	} else {
+		power_ctrl = 0;
 	}
+	rt2800_bbp_read(rt2x00dev, 1, &r1);
+	rt2x00_set_field8(&r1, BBP1_TX_POWER_CTRL, power_ctrl);
+	rt2800_bbp_write(rt2x00dev, 1, r1);
 
 	offset = TX_PWR_CFG_0;
 
diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c
index 573897b..8444313 100644
--- a/drivers/net/wireless/rt2x00/rt2800usb.c
+++ b/drivers/net/wireless/rt2x00/rt2800usb.c
@@ -1111,6 +1111,7 @@
 	/* Ovislink */
 	{ USB_DEVICE(0x1b75, 0x3071) },
 	{ USB_DEVICE(0x1b75, 0x3072) },
+	{ USB_DEVICE(0x1b75, 0xa200) },
 	/* Para */
 	{ USB_DEVICE(0x20b8, 0x8888) },
 	/* Pegatron */
diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h
index 1ff81af..9bb398b 100644
--- a/drivers/net/wireless/rt2x00/rt2x00.h
+++ b/drivers/net/wireless/rt2x00/rt2x00.h
@@ -1019,9 +1019,12 @@
  * Register defines.
  * Some registers require multiple attempts before success,
  * in those cases REGISTER_BUSY_COUNT attempts should be
- * taken with a REGISTER_BUSY_DELAY interval.
+ * taken with a REGISTER_BUSY_DELAY interval. Due to USB
+ * bus delays, we do not have to loop so many times to wait
+ * for valid register value on that bus.
  */
 #define REGISTER_BUSY_COUNT	100
+#define REGISTER_USB_BUSY_COUNT 20
 #define REGISTER_BUSY_DELAY	100
 
 /*
diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c
index 8e68f87..66ff364 100644
--- a/drivers/net/wireless/rt2x00/rt2x00queue.c
+++ b/drivers/net/wireless/rt2x00/rt2x00queue.c
@@ -158,55 +158,29 @@
 	skb_trim(skb, frame_length);
 }
 
-void rt2x00queue_insert_l2pad(struct sk_buff *skb, unsigned int header_length)
+/*
+ * H/W needs L2 padding between the header and the paylod if header size
+ * is not 4 bytes aligned.
+ */
+void rt2x00queue_insert_l2pad(struct sk_buff *skb, unsigned int hdr_len)
 {
-	unsigned int payload_length = skb->len - header_length;
-	unsigned int header_align = ALIGN_SIZE(skb, 0);
-	unsigned int payload_align = ALIGN_SIZE(skb, header_length);
-	unsigned int l2pad = payload_length ? L2PAD_SIZE(header_length) : 0;
-
-	/*
-	 * Adjust the header alignment if the payload needs to be moved more
-	 * than the header.
-	 */
-	if (payload_align > header_align)
-		header_align += 4;
-
-	/* There is nothing to do if no alignment is needed */
-	if (!header_align)
-		return;
-
-	/* Reserve the amount of space needed in front of the frame */
-	skb_push(skb, header_align);
-
-	/*
-	 * Move the header.
-	 */
-	memmove(skb->data, skb->data + header_align, header_length);
-
-	/* Move the payload, if present and if required */
-	if (payload_length && payload_align)
-		memmove(skb->data + header_length + l2pad,
-			skb->data + header_length + l2pad + payload_align,
-			payload_length);
-
-	/* Trim the skb to the correct size */
-	skb_trim(skb, header_length + l2pad + payload_length);
-}
-
-void rt2x00queue_remove_l2pad(struct sk_buff *skb, unsigned int header_length)
-{
-	/*
-	 * L2 padding is only present if the skb contains more than just the
-	 * IEEE 802.11 header.
-	 */
-	unsigned int l2pad = (skb->len > header_length) ?
-				L2PAD_SIZE(header_length) : 0;
+	unsigned int l2pad = (skb->len > hdr_len) ? L2PAD_SIZE(hdr_len) : 0;
 
 	if (!l2pad)
 		return;
 
-	memmove(skb->data + l2pad, skb->data, header_length);
+	skb_push(skb, l2pad);
+	memmove(skb->data, skb->data + l2pad, hdr_len);
+}
+
+void rt2x00queue_remove_l2pad(struct sk_buff *skb, unsigned int hdr_len)
+{
+	unsigned int l2pad = (skb->len > hdr_len) ? L2PAD_SIZE(hdr_len) : 0;
+
+	if (!l2pad)
+		return;
+
+	memmove(skb->data + l2pad, skb->data, hdr_len);
 	skb_pull(skb, l2pad);
 }
 
diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.c b/drivers/net/wireless/rt2x00/rt2x00usb.c
index dc85d3e..892270d 100644
--- a/drivers/net/wireless/rt2x00/rt2x00usb.c
+++ b/drivers/net/wireless/rt2x00/rt2x00usb.c
@@ -42,37 +42,27 @@
 {
 	struct usb_device *usb_dev = to_usb_device_intf(rt2x00dev->dev);
 	int status;
-	unsigned int i;
 	unsigned int pipe =
 	    (requesttype == USB_VENDOR_REQUEST_IN) ?
 	    usb_rcvctrlpipe(usb_dev, 0) : usb_sndctrlpipe(usb_dev, 0);
+	unsigned long expire = jiffies + msecs_to_jiffies(timeout);
 
 	if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags))
 		return -ENODEV;
 
-	for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
+	do {
 		status = usb_control_msg(usb_dev, pipe, request, requesttype,
 					 value, offset, buffer, buffer_length,
-					 timeout);
+					 timeout / 2);
 		if (status >= 0)
 			return 0;
 
-		/*
-		 * Check for errors
-		 * -ENODEV: Device has disappeared, no point continuing.
-		 * All other errors: Try again.
-		 */
-		else if (status == -ENODEV) {
+		if (status == -ENODEV) {
+			/* Device has disappeared. */
 			clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags);
 			break;
 		}
-	}
-
-	/* If the port is powered down, we get a -EPROTO error, and this
-	 * leads to a endless loop. So just say that the device is gone.
-	 */
-	if (status == -EPROTO)
-		clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags);
+	} while (time_before(jiffies, expire));
 
 	rt2x00_err(rt2x00dev,
 		   "Vendor Request 0x%02x failed for offset 0x%04x with error %d\n",
@@ -154,7 +144,7 @@
 	if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags))
 		return -ENODEV;
 
-	for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
+	for (i = 0; i < REGISTER_USB_BUSY_COUNT; i++) {
 		rt2x00usb_register_read_lock(rt2x00dev, offset, reg);
 		if (!rt2x00_get_field32(*reg, field))
 			return 1;
diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.h b/drivers/net/wireless/rt2x00/rt2x00usb.h
index 819690e..8f85fbd 100644
--- a/drivers/net/wireless/rt2x00/rt2x00usb.h
+++ b/drivers/net/wireless/rt2x00/rt2x00usb.h
@@ -38,7 +38,7 @@
  * a higher value is required. In that case we use the REGISTER_TIMEOUT_FIRMWARE
  * and EEPROM_TIMEOUT.
  */
-#define REGISTER_TIMEOUT		500
+#define REGISTER_TIMEOUT		100
 #define REGISTER_TIMEOUT_FIRMWARE	1000
 #define EEPROM_TIMEOUT			2000
 
diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c
index 95724ff..a5458cf 100644
--- a/drivers/net/wireless/rt2x00/rt73usb.c
+++ b/drivers/net/wireless/rt2x00/rt73usb.c
@@ -1295,7 +1295,7 @@
 	unsigned int i;
 	u8 value;
 
-	for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
+	for (i = 0; i < REGISTER_USB_BUSY_COUNT; i++) {
 		rt73usb_bbp_read(rt2x00dev, 0, &value);
 		if ((value != 0xff) && (value != 0x00))
 			return 0;
diff --git a/drivers/net/wireless/rtlwifi/base.c b/drivers/net/wireless/rtlwifi/base.c
index 58ba718..40b6d1d 100644
--- a/drivers/net/wireless/rtlwifi/base.c
+++ b/drivers/net/wireless/rtlwifi/base.c
@@ -467,7 +467,7 @@
 		    rtl_easy_concurrent_retrytimer_callback, (unsigned long)hw);
 	/* <2> work queue */
 	rtlpriv->works.hw = hw;
-	rtlpriv->works.rtl_wq = alloc_workqueue(rtlpriv->cfg->name, 0, 0);
+	rtlpriv->works.rtl_wq = alloc_workqueue("%s", 0, 0, rtlpriv->cfg->name);
 	INIT_DELAYED_WORK(&rtlpriv->works.watchdog_wq,
 			  (void *)rtl_watchdog_wq_callback);
 	INIT_DELAYED_WORK(&rtlpriv->works.ips_nic_off_wq,
diff --git a/drivers/net/wireless/rtlwifi/core.c b/drivers/net/wireless/rtlwifi/core.c
index 884d905..5fc6f52 100644
--- a/drivers/net/wireless/rtlwifi/core.c
+++ b/drivers/net/wireless/rtlwifi/core.c
@@ -786,6 +786,7 @@
 				    unsigned int changed_flags,
 				    unsigned int *new_flags, u64 multicast)
 {
+	bool update_rcr = false;
 	struct rtl_priv *rtlpriv = rtl_priv(hw);
 	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
 
@@ -806,6 +807,7 @@
 			RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD,
 				 "Disable receive multicast frame\n");
 		}
+		update_rcr = true;
 	}
 
 	if (changed_flags & FIF_FCSFAIL) {
@@ -818,6 +820,8 @@
 			RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD,
 				 "Disable receive FCS error frame\n");
 		}
+		if (!update_rcr)
+			update_rcr = true;
 	}
 
 	/* if ssid not set to hw don't check bssid
@@ -832,6 +836,8 @@
 				rtlpriv->cfg->ops->set_chk_bssid(hw, false);
 			else
 				rtlpriv->cfg->ops->set_chk_bssid(hw, true);
+			if (update_rcr)
+				update_rcr = false;
 		}
 	}
 
@@ -846,6 +852,8 @@
 			RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD,
 				 "Disable receive control frame.\n");
 		}
+		if (!update_rcr)
+			update_rcr = true;
 	}
 
 	if (changed_flags & FIF_OTHER_BSS) {
@@ -858,7 +866,13 @@
 			RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD,
 				 "Disable receive other BSS's frame.\n");
 		}
+		if (!update_rcr)
+			update_rcr = true;
 	}
+
+	if (update_rcr)
+		rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RCR,
+					      (u8 *)(&mac->rx_conf));
 }
 static int rtl_op_sta_add(struct ieee80211_hw *hw,
 			 struct ieee80211_vif *vif,
@@ -1831,3 +1845,9 @@
 	.flush = rtl_op_flush,
 };
 EXPORT_SYMBOL_GPL(rtl_ops);
+
+bool rtl_btc_status_false(void)
+{
+	return false;
+}
+EXPORT_SYMBOL_GPL(rtl_btc_status_false);
diff --git a/drivers/net/wireless/rtlwifi/core.h b/drivers/net/wireless/rtlwifi/core.h
index 59cd3b9..624e1dc 100644
--- a/drivers/net/wireless/rtlwifi/core.h
+++ b/drivers/net/wireless/rtlwifi/core.h
@@ -42,5 +42,6 @@
 		     u32 mask, u32 data);
 void rtl_bb_delay(struct ieee80211_hw *hw, u32 addr, u32 data);
 bool rtl_cmd_send_packet(struct ieee80211_hw *hw, struct sk_buff *skb);
+bool rtl_btc_status_false(void);
 
 #endif
diff --git a/drivers/net/wireless/rtlwifi/pci.c b/drivers/net/wireless/rtlwifi/pci.c
index 667aba8..61f5d36 100644
--- a/drivers/net/wireless/rtlwifi/pci.c
+++ b/drivers/net/wireless/rtlwifi/pci.c
@@ -842,7 +842,8 @@
 			break;
 		}
 		/* handle command packet here */
-		if (rtlpriv->cfg->ops->rx_command_packet(hw, stats, skb)) {
+		if (rtlpriv->cfg->ops->rx_command_packet &&
+		    rtlpriv->cfg->ops->rx_command_packet(hw, stats, skb)) {
 				dev_kfree_skb_any(skb);
 				goto end;
 		}
@@ -1127,9 +1128,14 @@
 
 	__skb_queue_tail(&ring->queue, pskb);
 
-	rtlpriv->cfg->ops->set_desc(hw, (u8 *)pdesc, true, HW_DESC_OWN,
-				    &temp_one);
-
+	if (rtlpriv->use_new_trx_flow) {
+		temp_one = 4;
+		rtlpriv->cfg->ops->set_desc(hw, (u8 *)pbuffer_desc, true,
+					    HW_DESC_OWN, (u8 *)&temp_one);
+	} else {
+		rtlpriv->cfg->ops->set_desc(hw, (u8 *)pdesc, true, HW_DESC_OWN,
+					    &temp_one);
+	}
 	return;
 }
 
@@ -1370,9 +1376,9 @@
 	ring->desc = NULL;
 	if (rtlpriv->use_new_trx_flow) {
 		pci_free_consistent(rtlpci->pdev,
-				    sizeof(*ring->desc) * ring->entries,
+				    sizeof(*ring->buffer_desc) * ring->entries,
 				    ring->buffer_desc, ring->buffer_desc_dma);
-		ring->desc = NULL;
+		ring->buffer_desc = NULL;
 	}
 }
 
@@ -1543,7 +1549,6 @@
 							 true,
 							 HW_DESC_TXBUFF_ADDR),
 						 skb->len, PCI_DMA_TODEVICE);
-				ring->idx = (ring->idx + 1) % ring->entries;
 				kfree_skb(skb);
 				ring->idx = (ring->idx + 1) % ring->entries;
 			}
@@ -1796,7 +1801,8 @@
 	rtl_pci_reset_trx_ring(hw);
 
 	rtlpci->driver_is_goingto_unload = false;
-	if (rtlpriv->cfg->ops->get_btc_status()) {
+	if (rtlpriv->cfg->ops->get_btc_status &&
+	    rtlpriv->cfg->ops->get_btc_status()) {
 		rtlpriv->btcoexist.btc_ops->btc_init_variables(rtlpriv);
 		rtlpriv->btcoexist.btc_ops->btc_init_hal_vars(rtlpriv);
 	}
diff --git a/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.c b/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.c
index a00861b..29983bc 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.c
@@ -656,7 +656,8 @@
 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 };
 
-void rtl92c_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, bool b_dl_finished)
+void rtl92c_set_fw_rsvdpagepkt(struct ieee80211_hw *hw,
+	 bool (*cmd_send_packet)(struct ieee80211_hw *, struct sk_buff *))
 {
 	struct rtl_priv *rtlpriv = rtl_priv(hw);
 	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
@@ -722,7 +723,10 @@
 	memcpy((u8 *)skb_put(skb, totalpacketlen),
 	       &reserved_page_packet, totalpacketlen);
 
-	rtstatus = rtl_cmd_send_packet(hw, skb);
+	if (cmd_send_packet)
+		rtstatus = cmd_send_packet(hw, skb);
+	else
+		rtstatus = rtl_cmd_send_packet(hw, skb);
 
 	if (rtstatus)
 		b_dlok = true;
diff --git a/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.h b/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.h
index a815bd6..b64ae45 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.h
@@ -109,7 +109,9 @@
 			 u32 cmd_len, u8 *p_cmdbuffer);
 void rtl92c_firmware_selfreset(struct ieee80211_hw *hw);
 void rtl92c_set_fw_pwrmode_cmd(struct ieee80211_hw *hw, u8 mode);
-void rtl92c_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, bool b_dl_finished);
+void rtl92c_set_fw_rsvdpagepkt
+	(struct ieee80211_hw *hw,
+	 bool (*cmd_send_packet)(struct ieee80211_hw *, struct sk_buff *));
 void rtl92c_set_fw_joinbss_report_cmd(struct ieee80211_hw *hw, u8 mstatus);
 void usb_writeN_async(struct rtl_priv *rtlpriv, u32 addr, void *data, u16 len);
 void rtl92c_set_p2p_ps_offload_cmd(struct ieee80211_hw *hw, u8 p2p_ps_state);
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/def.h b/drivers/net/wireless/rtlwifi/rtl8192ce/def.h
index 831df10..9b660df 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ce/def.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192ce/def.h
@@ -114,6 +114,8 @@
 	LE_BITS_TO_4BYTE(((__pcmdfbhdr) + 4), 16, 4)
 #define	GET_C2H_CMD_FEEDBACK_CCX_SEQ(__pcmdfbhdr)	\
 	LE_BITS_TO_4BYTE(((__pcmdfbhdr) + 4), 20, 12)
+#define GET_RX_STATUS_DESC_BUFF_ADDR(__pdesc)			\
+	SHIFT_AND_MASK_LE(__pdesc + 24, 0, 32)
 
 #define CHIP_VER_B			BIT(4)
 #define CHIP_BONDING_IDENTIFIER(_value) (((_value) >> 22) & 0x3)
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c b/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c
index 8ec0f03..55357d6 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c
@@ -459,7 +459,7 @@
 				rtl_write_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2,
 					       tmp_reg422 & (~BIT(6)));
 
-				rtl92c_set_fw_rsvdpagepkt(hw, 0);
+				rtl92c_set_fw_rsvdpagepkt(hw, NULL);
 
 				_rtl92ce_set_bcn_ctrl_reg(hw, BIT(3), 0);
 				_rtl92ce_set_bcn_ctrl_reg(hw, 0, BIT(4));
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c b/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c
index d86b5b5..46ea076 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c
@@ -244,6 +244,7 @@
 	.phy_lc_calibrate = _rtl92ce_phy_lc_calibrate,
 	.phy_set_bw_mode_callback = rtl92ce_phy_set_bw_mode_callback,
 	.dm_dynamic_txpower = rtl92ce_dm_dynamic_txpower,
+	.get_btc_status = rtl_btc_status_false,
 };
 
 static struct rtl_mod_params rtl92ce_mod_params = {
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c
index 2fb9c7a..dc3d20b 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c
@@ -728,6 +728,9 @@
 		case HW_DESC_RXPKT_LEN:
 			ret = GET_RX_DESC_PKT_LEN(pdesc);
 			break;
+		case HW_DESC_RXBUFF_ADDR:
+			ret = GET_RX_STATUS_DESC_BUFF_ADDR(pdesc);
+			break;
 		default:
 			RT_ASSERT(false, "ERR rxdesc :%d not process\n",
 				  desc_name);
diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c b/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c
index 04aa0b5..873363a 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c
@@ -1592,6 +1592,20 @@
 	}
 }
 
+bool usb_cmd_send_packet(struct ieee80211_hw *hw, struct sk_buff *skb)
+{
+  /* Currently nothing happens here.
+   * Traffic stops after some seconds in WPA2 802.11n mode.
+   * Maybe because rtl8192cu chip should be set from here?
+   * If I understand correctly, the realtek vendor driver sends some urbs
+   * if its "here".
+   *
+   * This is maybe necessary:
+   * rtlpriv->cfg->ops->fill_tx_cmddesc(hw, buffer, 1, 1, skb);
+   */
+	return true;
+}
+
 void rtl92cu_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
 {
 	struct rtl_priv *rtlpriv = rtl_priv(hw);
@@ -1939,7 +1953,8 @@
 					recover = true;
 				rtl_write_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2,
 					       tmp_reg422 & (~BIT(6)));
-				rtl92c_set_fw_rsvdpagepkt(hw, 0);
+				rtl92c_set_fw_rsvdpagepkt(hw,
+							  &usb_cmd_send_packet);
 				_rtl92cu_set_bcn_ctrl_reg(hw, BIT(3), 0);
 				_rtl92cu_set_bcn_ctrl_reg(hw, 0, BIT(4));
 				if (recover)
diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/hw.h b/drivers/net/wireless/rtlwifi/rtl8192cu/hw.h
index 0f7812e..c1e33b0 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192cu/hw.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192cu/hw.h
@@ -104,7 +104,6 @@
 void rtl92cu_set_check_bssid(struct ieee80211_hw *hw, bool check_bssid);
 int rtl92c_download_fw(struct ieee80211_hw *hw);
 void rtl92c_set_fw_pwrmode_cmd(struct ieee80211_hw *hw, u8 mode);
-void rtl92c_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, bool dl_finished);
 void rtl92c_set_fw_joinbss_report_cmd(struct ieee80211_hw *hw, u8 mstatus);
 void rtl92c_fill_h2c_cmd(struct ieee80211_hw *hw,
 			 u8 element_id, u32 cmd_len, u8 *p_cmdbuffer);
diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c b/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c
index 7c5fbaf..e06bafe 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c
@@ -101,6 +101,12 @@
 	}
 }
 
+/* get bt coexist status */
+static bool rtl92cu_get_btc_status(void)
+{
+	return false;
+}
+
 static struct rtl_hal_ops rtl8192cu_hal_ops = {
 	.init_sw_vars = rtl92cu_init_sw_vars,
 	.deinit_sw_vars = rtl92cu_deinit_sw_vars,
@@ -148,6 +154,7 @@
 	.phy_set_bw_mode_callback = rtl92cu_phy_set_bw_mode_callback,
 	.dm_dynamic_txpower = rtl92cu_dm_dynamic_txpower,
 	.fill_h2c_cmd = rtl92c_fill_h2c_cmd,
+	.get_btc_status = rtl92cu_get_btc_status,
 };
 
 static struct rtl_mod_params rtl92cu_mod_params = {
diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/sw.c b/drivers/net/wireless/rtlwifi/rtl8192de/sw.c
index edab5a5..a0aba08 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192de/sw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192de/sw.c
@@ -251,6 +251,7 @@
 	.get_rfreg = rtl92d_phy_query_rf_reg,
 	.set_rfreg = rtl92d_phy_set_rf_reg,
 	.linked_set_reg = rtl92d_linked_set_reg,
+	.get_btc_status = rtl_btc_status_false,
 };
 
 static struct rtl_mod_params rtl92de_mod_params = {
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ee/Makefile b/drivers/net/wireless/rtlwifi/rtl8192ee/Makefile
index 11952b9..0315eed 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ee/Makefile
+++ b/drivers/net/wireless/rtlwifi/rtl8192ee/Makefile
@@ -1,6 +1,3 @@
-obj-m := rtl8192ee.o
-
-
 rtl8192ee-objs :=		\
 		dm.o		\
 		fw.o		\
@@ -14,6 +11,6 @@
 		trx.o		\
 
 
-obj-$(CONFIG_RTL8821AE) += rtl8192ee.o
+obj-$(CONFIG_RTL8192EE) += rtl8192ee.o
 
 ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ee/hw.c b/drivers/net/wireless/rtlwifi/rtl8192ee/hw.c
index dfdc9b2..1a87edc 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ee/hw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192ee/hw.c
@@ -362,7 +362,7 @@
 		}
 		break;
 	default:
-		RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+		RT_TRACE(rtlpriv, COMP_ERR, DBG_DMESG,
 			 "switch case not process %x\n", variable);
 		break;
 	}
@@ -591,7 +591,7 @@
 				acm_ctrl &= (~ACMHW_BEQEN);
 				break;
 			default:
-				RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+				RT_TRACE(rtlpriv, COMP_ERR, DBG_DMESG,
 					 "switch case not process\n");
 				break;
 			}
@@ -710,7 +710,7 @@
 		}
 		break;
 	default:
-		RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+		RT_TRACE(rtlpriv, COMP_ERR, DBG_DMESG,
 			 "switch case not process %x\n", variable);
 		break;
 	}
@@ -2424,7 +2424,7 @@
 			enc_algo = CAM_AES;
 			break;
 		default:
-			RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+			RT_TRACE(rtlpriv, COMP_ERR, DBG_DMESG,
 				 "switch case not process\n");
 			enc_algo = CAM_TKIP;
 			break;
diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/def.h b/drivers/net/wireless/rtlwifi/rtl8192se/def.h
index 83c9867..6e7a70b4 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192se/def.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192se/def.h
@@ -446,6 +446,8 @@
 /* DWORD 6 */
 #define SET_RX_STATUS__DESC_BUFF_ADDR(__pdesc, __val)	\
 	SET_BITS_OFFSET_LE(__pdesc + 24, 0, 32, __val)
+#define GET_RX_STATUS_DESC_BUFF_ADDR(__pdesc)			\
+	SHIFT_AND_MASK_LE(__pdesc + 24, 0, 32)
 
 #define SE_RX_HAL_IS_CCK_RATE(_pdesc)\
 	(GET_RX_STATUS_DESC_RX_MCS(_pdesc) == DESC92_RATE1M ||	\
diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/hw.c b/drivers/net/wireless/rtlwifi/rtl8192se/hw.c
index 00e0670..5761d5b 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192se/hw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192se/hw.c
@@ -1201,6 +1201,9 @@
 
 	}
 
+	if (type != NL80211_IFTYPE_AP &&
+	    rtlpriv->mac80211.link_state < MAC80211_LINKED)
+		bt_msr = rtl_read_byte(rtlpriv, MSR) & ~MSR_LINK_MASK;
 	rtl_write_byte(rtlpriv, (MSR), bt_msr);
 
 	temp = rtl_read_dword(rtlpriv, TCR);
@@ -1262,6 +1265,7 @@
 	rtl_write_dword(rtlpriv, INTA_MASK, rtlpci->irq_mask[0]);
 	/* Support Bit 32-37(Assign as Bit 0-5) interrupt setting now */
 	rtl_write_dword(rtlpriv, INTA_MASK + 4, rtlpci->irq_mask[1] & 0x3F);
+	rtlpci->irq_enabled = true;
 }
 
 void rtl92se_disable_interrupt(struct ieee80211_hw *hw)
@@ -1276,8 +1280,7 @@
 	rtlpci = rtl_pcidev(rtl_pcipriv(hw));
 	rtl_write_dword(rtlpriv, INTA_MASK, 0);
 	rtl_write_dword(rtlpriv, INTA_MASK + 4, 0);
-
-	synchronize_irq(rtlpci->pdev->irq);
+	rtlpci->irq_enabled = false;
 }
 
 static u8 _rtl92s_set_sysclk(struct ieee80211_hw *hw, u8 data)
diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/phy.c b/drivers/net/wireless/rtlwifi/rtl8192se/phy.c
index 77c5b5f..4b4612f 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192se/phy.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192se/phy.c
@@ -399,6 +399,8 @@
 		case 2:
 			currentcmd = &postcommoncmd[*step];
 			break;
+		default:
+			return true;
 		}
 
 		if (currentcmd->cmdid == CMDID_END) {
diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/sw.c b/drivers/net/wireless/rtlwifi/rtl8192se/sw.c
index 1bff2a0..fb00386 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192se/sw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192se/sw.c
@@ -87,11 +87,8 @@
 static void rtl92se_fw_cb(const struct firmware *firmware, void *context)
 {
 	struct ieee80211_hw *hw = context;
-	struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
 	struct rtl_priv *rtlpriv = rtl_priv(hw);
-	struct rtl_pci *rtlpci = rtl_pcidev(pcipriv);
 	struct rt_firmware *pfirmware = NULL;
-	int err;
 
 	RT_TRACE(rtlpriv, COMP_ERR, DBG_LOUD,
 			 "Firmware callback routine entered!\n");
@@ -112,20 +109,6 @@
 	memcpy(pfirmware->sz_fw_tmpbuffer, firmware->data, firmware->size);
 	pfirmware->sz_fw_tmpbufferlen = firmware->size;
 	release_firmware(firmware);
-
-	err = ieee80211_register_hw(hw);
-	if (err) {
-		RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
-			 "Can't register mac80211 hw\n");
-		return;
-	} else {
-		rtlpriv->mac80211.mac80211_registered = 1;
-	}
-	rtlpci->irq_alloc = 1;
-	set_bit(RTL_STATUS_INTERFACE_START, &rtlpriv->status);
-
-	/*init rfkill */
-	rtl_init_rfkill(hw);
 }
 
 static int rtl92s_init_sw_vars(struct ieee80211_hw *hw)
@@ -226,8 +209,8 @@
 	if (!rtlpriv->rtlhal.pfirmware)
 		return 1;
 
-	rtlpriv->max_fw_size = RTL8190_MAX_RAW_FIRMWARE_CODE_SIZE;
-
+	rtlpriv->max_fw_size = RTL8190_MAX_FIRMWARE_CODE_SIZE*2 +
+			       sizeof(struct fw_hdr);
 	pr_info("Driver for Realtek RTL8192SE/RTL8191SE\n"
 		"Loading firmware %s\n", rtlpriv->cfg->fw_name);
 	/* request fw */
@@ -253,6 +236,19 @@
 	}
 }
 
+static bool rtl92se_is_tx_desc_closed(struct ieee80211_hw *hw, u8 hw_queue,
+				      u16 index)
+{
+	struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+	struct rtl8192_tx_ring *ring = &rtlpci->tx_ring[hw_queue];
+	u8 *entry = (u8 *)(&ring->desc[ring->idx]);
+	u8 own = (u8)rtl92se_get_desc(entry, true, HW_DESC_OWN);
+
+	if (own)
+		return false;
+	return true;
+}
+
 static struct rtl_hal_ops rtl8192se_hal_ops = {
 	.init_sw_vars = rtl92s_init_sw_vars,
 	.deinit_sw_vars = rtl92s_deinit_sw_vars,
@@ -286,6 +282,7 @@
 	.led_control = rtl92se_led_control,
 	.set_desc = rtl92se_set_desc,
 	.get_desc = rtl92se_get_desc,
+	.is_tx_desc_closed = rtl92se_is_tx_desc_closed,
 	.tx_polling = rtl92se_tx_polling,
 	.enable_hw_sec = rtl92se_enable_hw_security_config,
 	.set_key = rtl92se_set_key,
@@ -294,6 +291,7 @@
 	.set_bbreg = rtl92s_phy_set_bb_reg,
 	.get_rfreg = rtl92s_phy_query_rf_reg,
 	.set_rfreg = rtl92s_phy_set_rf_reg,
+	.get_btc_status = rtl_btc_status_false,
 };
 
 static struct rtl_mod_params rtl92se_mod_params = {
@@ -322,6 +320,8 @@
 	.maps[MAC_RCR_ACRC32] = RCR_ACRC32,
 	.maps[MAC_RCR_ACF] = RCR_ACF,
 	.maps[MAC_RCR_AAP] = RCR_AAP,
+	.maps[MAC_HIMR] = INTA_MASK,
+	.maps[MAC_HIMRE] = INTA_MASK + 4,
 
 	.maps[EFUSE_TEST] = REG_EFUSE_TEST,
 	.maps[EFUSE_CTRL] = REG_EFUSE_CTRL,
diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/trx.c b/drivers/net/wireless/rtlwifi/rtl8192se/trx.c
index b358ebc..672fd3b 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192se/trx.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192se/trx.c
@@ -640,6 +640,9 @@
 		case HW_DESC_RXPKT_LEN:
 			ret = GET_RX_STATUS_DESC_PKT_LEN(desc);
 			break;
+		case HW_DESC_RXBUFF_ADDR:
+			ret = GET_RX_STATUS_DESC_BUFF_ADDR(desc);
+			break;
 		default:
 			RT_ASSERT(false, "ERR rxdesc :%d not process\n",
 				  desc_name);
diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/Makefile b/drivers/net/wireless/rtlwifi/rtl8723ae/Makefile
index 9c34a85..6220672 100644
--- a/drivers/net/wireless/rtlwifi/rtl8723ae/Makefile
+++ b/drivers/net/wireless/rtlwifi/rtl8723ae/Makefile
@@ -1,6 +1,3 @@
-obj-m := rtl8723ae.o
-
-
 rtl8723ae-objs :=		\
 		dm.o		\
 		fw.o		\
diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/Makefile b/drivers/net/wireless/rtlwifi/rtl8723be/Makefile
index 59e416a..a77c341 100644
--- a/drivers/net/wireless/rtlwifi/rtl8723be/Makefile
+++ b/drivers/net/wireless/rtlwifi/rtl8723be/Makefile
@@ -1,6 +1,3 @@
-obj-m := rtl8723be.o
-
-
 rtl8723be-objs :=		\
 		dm.o		\
 		fw.o		\
diff --git a/drivers/net/wireless/rtlwifi/rtl8821ae/Makefile b/drivers/net/wireless/rtlwifi/rtl8821ae/Makefile
index 87ad604..f7a26f7 100644
--- a/drivers/net/wireless/rtlwifi/rtl8821ae/Makefile
+++ b/drivers/net/wireless/rtlwifi/rtl8821ae/Makefile
@@ -1,6 +1,3 @@
-obj-m := rtl8821ae.o
-
-
 rtl8821ae-objs :=		\
 		dm.o		\
 		fw.o		\
diff --git a/drivers/net/wireless/rtlwifi/rtl8821ae/phy.c b/drivers/net/wireless/rtlwifi/rtl8821ae/phy.c
index 4be278f..9b4d8a6 100644
--- a/drivers/net/wireless/rtlwifi/rtl8821ae/phy.c
+++ b/drivers/net/wireless/rtlwifi/rtl8821ae/phy.c
@@ -1889,15 +1889,18 @@
 	struct rtl_phy *rtlphy = &rtlpriv->phy;
 	u8 rate_section = _rtl8821ae_get_rate_section_index(regaddr);
 
-	if (band != BAND_ON_2_4G && band != BAND_ON_5G)
+	if (band != BAND_ON_2_4G && band != BAND_ON_5G) {
 		RT_TRACE(rtlpriv, COMP_INIT, DBG_WARNING, "Invalid Band %d\n", band);
-
-	if (rfpath >= MAX_RF_PATH)
+		band = BAND_ON_2_4G;
+	}
+	if (rfpath >= MAX_RF_PATH) {
 		RT_TRACE(rtlpriv, COMP_INIT, DBG_WARNING, "Invalid RfPath %d\n", rfpath);
-
-	if (txnum >= MAX_RF_PATH)
+		rfpath = MAX_RF_PATH - 1;
+	}
+	if (txnum >= MAX_RF_PATH) {
 		RT_TRACE(rtlpriv, COMP_INIT, DBG_WARNING, "Invalid TxNum %d\n", txnum);
-
+		txnum = MAX_RF_PATH - 1;
+	}
 	rtlphy->tx_power_by_rate_offset[band][rfpath][txnum][rate_section] = data;
 	RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
 		 "TxPwrByRateOffset[Band %d][RfPath %d][TxNum %d][RateSection %d] = 0x%x\n",
diff --git a/drivers/net/wireless/rtlwifi/usb.c b/drivers/net/wireless/rtlwifi/usb.c
index 10cf69c..46ee956 100644
--- a/drivers/net/wireless/rtlwifi/usb.c
+++ b/drivers/net/wireless/rtlwifi/usb.c
@@ -1117,7 +1117,18 @@
 	}
 	rtlpriv->cfg->ops->init_sw_leds(hw);
 
+	err = ieee80211_register_hw(hw);
+	if (err) {
+		RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+			 "Can't register mac80211 hw.\n");
+		err = -ENODEV;
+		goto error_out;
+	}
+	rtlpriv->mac80211.mac80211_registered = 1;
+
+	set_bit(RTL_STATUS_INTERFACE_START, &rtlpriv->status);
 	return 0;
+
 error_out:
 	rtl_deinit_core(hw);
 	_rtl_usb_io_handler_release(hw);
diff --git a/drivers/nfc/pn544/i2c.c b/drivers/nfc/pn544/i2c.c
index 440291a..fc02e8d 100644
--- a/drivers/nfc/pn544/i2c.c
+++ b/drivers/nfc/pn544/i2c.c
@@ -29,8 +29,8 @@
 #include <linux/delay.h>
 #include <linux/nfc.h>
 #include <linux/firmware.h>
-#include <linux/unaligned/access_ok.h>
 #include <linux/platform_data/pn544.h>
+#include <asm/unaligned.h>
 
 #include <net/nfc/hci.h>
 #include <net/nfc/llc.h>
diff --git a/drivers/nfc/st21nfca/i2c.c b/drivers/nfc/st21nfca/i2c.c
index 0ea756b..0572208 100644
--- a/drivers/nfc/st21nfca/i2c.c
+++ b/drivers/nfc/st21nfca/i2c.c
@@ -28,8 +28,8 @@
 #include <linux/delay.h>
 #include <linux/nfc.h>
 #include <linux/firmware.h>
-#include <linux/unaligned/access_ok.h>
 #include <linux/platform_data/st21nfca.h>
+#include <asm/unaligned.h>
 
 #include <net/nfc/hci.h>
 #include <net/nfc/llc.h>
@@ -72,7 +72,6 @@
 	struct nfc_hci_dev *hdev;
 
 	unsigned int gpio_ena;
-	unsigned int gpio_irq;
 	unsigned int irq_polarity;
 
 	struct sk_buff *pending_skb;
@@ -531,20 +530,12 @@
 				  "clf_enable");
 	if (r) {
 		nfc_err(&client->dev, "Failed to request enable pin\n");
-		return -ENODEV;
+		return r;
 	}
 
 	phy->gpio_ena = gpio;
 
-	/* IRQ */
-	r = irq_of_parse_and_map(pp, 0);
-	if (r < 0) {
-		nfc_err(&client->dev, "Unable to get irq, error: %d\n", r);
-		return r;
-	}
-
-	phy->irq_polarity = irq_get_trigger_type(r);
-	client->irq = r;
+	phy->irq_polarity = irq_get_trigger_type(client->irq);
 
 	return 0;
 }
@@ -560,7 +551,6 @@
 	struct st21nfca_nfc_platform_data *pdata;
 	struct st21nfca_i2c_phy *phy = i2c_get_clientdata(client);
 	int r;
-	int irq;
 
 	pdata = client->dev.platform_data;
 	if (pdata == NULL) {
@@ -569,36 +559,18 @@
 	}
 
 	/* store for later use */
-	phy->gpio_irq = pdata->gpio_irq;
 	phy->gpio_ena = pdata->gpio_ena;
 	phy->irq_polarity = pdata->irq_polarity;
 
-	r = devm_gpio_request_one(&client->dev, phy->gpio_irq, GPIOF_IN,
-				  "wake_up");
-	if (r) {
-		pr_err("%s : gpio_request failed\n", __FILE__);
-		return -ENODEV;
-	}
-
 	if (phy->gpio_ena > 0) {
 		r = devm_gpio_request_one(&client->dev, phy->gpio_ena,
 					  GPIOF_OUT_INIT_HIGH, "clf_enable");
 		if (r) {
 			pr_err("%s : ena gpio_request failed\n", __FILE__);
-			return -ENODEV;
+			return r;
 		}
 	}
 
-	/* IRQ */
-	irq = gpio_to_irq(phy->gpio_irq);
-	if (irq < 0) {
-		nfc_err(&client->dev,
-				"Unable to get irq number for GPIO %d error %d\n",
-				phy->gpio_irq, r);
-		return -ENODEV;
-	}
-	client->irq = irq;
-
 	return 0;
 }
 
@@ -656,7 +628,7 @@
 	r = st21nfca_hci_platform_init(phy);
 	if (r < 0) {
 		nfc_err(&client->dev, "Unable to reboot st21nfca\n");
-		return -ENODEV;
+		return r;
 	}
 
 	r = devm_request_threaded_irq(&client->dev, client->irq, NULL,
@@ -687,10 +659,13 @@
 	return 0;
 }
 
+#ifdef CONFIG_OF
 static const struct of_device_id of_st21nfca_i2c_match[] = {
 	{ .compatible = "st,st21nfca_i2c", },
 	{}
 };
+MODULE_DEVICE_TABLE(of, of_st21nfca_i2c_match);
+#endif
 
 static struct i2c_driver st21nfca_hci_i2c_driver = {
 	.driver = {
diff --git a/drivers/nfc/st21nfca/st21nfca.c b/drivers/nfc/st21nfca/st21nfca.c
index a89e56c..f2596c8 100644
--- a/drivers/nfc/st21nfca/st21nfca.c
+++ b/drivers/nfc/st21nfca/st21nfca.c
@@ -77,10 +77,6 @@
 	((p & 0x0f) == (ST21NFCA_DM_PIPE_CREATED | ST21NFCA_DM_PIPE_OPEN))
 
 #define ST21NFCA_NFC_MODE			0x03	/* NFC_MODE parameter*/
-#define ST21NFCA_EVT_FIELD_ON			0x11
-#define ST21NFCA_EVT_CARD_DEACTIVATED		0x12
-#define ST21NFCA_EVT_CARD_ACTIVATED		0x13
-#define ST21NFCA_EVT_FIELD_OFF			0x14
 
 static DECLARE_BITMAP(dev_mask, ST21NFCA_NUM_DEVICES);
 
@@ -841,31 +837,11 @@
 static int st21nfca_hci_event_received(struct nfc_hci_dev *hdev, u8 gate,
 				       u8 event, struct sk_buff *skb)
 {
-	int r;
-	struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+	pr_debug("hci event: %d gate: %x\n", event, gate);
 
-	pr_debug("hci event: %d\n", event);
-
-	switch (event) {
-	case ST21NFCA_EVT_CARD_ACTIVATED:
-		if (gate == ST21NFCA_RF_CARD_F_GATE)
-			info->dep_info.curr_nfc_dep_pni = 0;
-		break;
-	case ST21NFCA_EVT_CARD_DEACTIVATED:
-		break;
-	case ST21NFCA_EVT_FIELD_ON:
-		break;
-	case ST21NFCA_EVT_FIELD_OFF:
-		break;
-	case ST21NFCA_EVT_SEND_DATA:
-		if (gate == ST21NFCA_RF_CARD_F_GATE) {
-			r = st21nfca_tm_event_send_data(hdev, skb, gate);
-			if (r < 0)
-				return r;
-			return 0;
-		}
-		info->dep_info.curr_nfc_dep_pni = 0;
-		return 1;
+	switch (gate) {
+	case ST21NFCA_RF_CARD_F_GATE:
+		return st21nfca_dep_event_received(hdev, event, skb);
 	default:
 		return 1;
 	}
diff --git a/drivers/nfc/st21nfca/st21nfca.h b/drivers/nfc/st21nfca/st21nfca.h
index a0b77f1..7c2a852 100644
--- a/drivers/nfc/st21nfca/st21nfca.h
+++ b/drivers/nfc/st21nfca/st21nfca.h
@@ -85,6 +85,4 @@
 
 #define ST21NFCA_RF_CARD_F_GATE 0x24
 
-#define ST21NFCA_EVT_SEND_DATA 0x10
-
 #endif /* __LOCAL_ST21NFCA_H_ */
diff --git a/drivers/nfc/st21nfca/st21nfca_dep.c b/drivers/nfc/st21nfca/st21nfca_dep.c
index bfb6df5..8882181 100644
--- a/drivers/nfc/st21nfca/st21nfca_dep.c
+++ b/drivers/nfc/st21nfca/st21nfca_dep.c
@@ -49,6 +49,12 @@
 #define ST21NFCA_LR_BITS_PAYLOAD_SIZE_254B 0x30
 #define ST21NFCA_GB_BIT  0x02
 
+#define ST21NFCA_EVT_SEND_DATA		0x10
+#define ST21NFCA_EVT_FIELD_ON           0x11
+#define ST21NFCA_EVT_CARD_DEACTIVATED   0x12
+#define ST21NFCA_EVT_CARD_ACTIVATED     0x13
+#define ST21NFCA_EVT_FIELD_OFF          0x14
+
 #define ST21NFCA_EVT_CARD_F_BITRATE 0x16
 #define ST21NFCA_EVT_READER_F_BITRATE 0x13
 #define	ST21NFCA_PSL_REQ_SEND_SPEED(brs) (brs & 0x38)
@@ -372,8 +378,8 @@
 	return r;
 }
 
-int st21nfca_tm_event_send_data(struct nfc_hci_dev *hdev, struct sk_buff *skb,
-				u8 gate)
+static int st21nfca_tm_event_send_data(struct nfc_hci_dev *hdev,
+				struct sk_buff *skb)
 {
 	u8 cmd0, cmd1;
 	int r;
@@ -400,7 +406,42 @@
 	}
 	return r;
 }
-EXPORT_SYMBOL(st21nfca_tm_event_send_data);
+
+/*
+ * Returns:
+ * <= 0: driver handled the event, skb consumed
+ *    1: driver does not handle the event, please do standard processing
+ */
+int st21nfca_dep_event_received(struct nfc_hci_dev *hdev,
+				u8 event, struct sk_buff *skb)
+{
+	int r = 0;
+	struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+	pr_debug("dep event: %d\n", event);
+
+	switch (event) {
+	case ST21NFCA_EVT_CARD_ACTIVATED:
+		info->dep_info.curr_nfc_dep_pni = 0;
+		break;
+	case ST21NFCA_EVT_CARD_DEACTIVATED:
+		break;
+	case ST21NFCA_EVT_FIELD_ON:
+		break;
+	case ST21NFCA_EVT_FIELD_OFF:
+		break;
+	case ST21NFCA_EVT_SEND_DATA:
+		r = st21nfca_tm_event_send_data(hdev, skb);
+		if (r < 0)
+			return r;
+		return 0;
+	default:
+		return 1;
+	}
+	kfree_skb(skb);
+	return r;
+}
+EXPORT_SYMBOL(st21nfca_dep_event_received);
 
 static void st21nfca_im_send_psl_req(struct nfc_hci_dev *hdev, u8 did, u8 bsi,
 				     u8 bri, u8 lri)
diff --git a/drivers/nfc/st21nfca/st21nfca_dep.h b/drivers/nfc/st21nfca/st21nfca_dep.h
index ca213de..baf4664 100644
--- a/drivers/nfc/st21nfca/st21nfca_dep.h
+++ b/drivers/nfc/st21nfca/st21nfca_dep.h
@@ -32,8 +32,8 @@
 	u8 lri;
 } __packed;
 
-int st21nfca_tm_event_send_data(struct nfc_hci_dev *hdev, struct sk_buff *skb,
-				u8 gate);
+int st21nfca_dep_event_received(struct nfc_hci_dev *hdev,
+				u8 event, struct sk_buff *skb);
 int st21nfca_tm_send_dep_res(struct nfc_hci_dev *hdev, struct sk_buff *skb);
 
 int st21nfca_im_send_atr_req(struct nfc_hci_dev *hdev, u8 *gb, size_t gb_len);
diff --git a/drivers/nfc/st21nfcb/i2c.c b/drivers/nfc/st21nfcb/i2c.c
index c5d2427..01ba865 100644
--- a/drivers/nfc/st21nfcb/i2c.c
+++ b/drivers/nfc/st21nfcb/i2c.c
@@ -50,7 +50,6 @@
 	struct i2c_client *i2c_dev;
 	struct llt_ndlc *ndlc;
 
-	unsigned int gpio_irq;
 	unsigned int gpio_reset;
 	unsigned int irq_polarity;
 
@@ -81,8 +80,6 @@
 {
 	struct st21nfcb_i2c_phy *phy = phy_id;
 
-	pr_info("\n");
-
 	phy->powered = 0;
 	/* reset chip in order to flush clf */
 	gpio_set_value(phy->gpio_reset, 0);
@@ -258,19 +255,11 @@
 				GPIOF_OUT_INIT_HIGH, "clf_reset");
 	if (r) {
 		nfc_err(&client->dev, "Failed to request reset pin\n");
-		return -ENODEV;
+		return r;
 	}
 	phy->gpio_reset = gpio;
 
-	/* IRQ */
-	r = irq_of_parse_and_map(pp, 0);
-	if (r < 0) {
-		nfc_err(&client->dev, "Unable to get irq, error: %d\n", r);
-		return r;
-	}
-
-	phy->irq_polarity = irq_get_trigger_type(r);
-	client->irq = r;
+	phy->irq_polarity = irq_get_trigger_type(client->irq);
 
 	return 0;
 }
@@ -286,7 +275,6 @@
 	struct st21nfcb_nfc_platform_data *pdata;
 	struct st21nfcb_i2c_phy *phy = i2c_get_clientdata(client);
 	int r;
-	int irq;
 
 	pdata = client->dev.platform_data;
 	if (pdata == NULL) {
@@ -295,34 +283,16 @@
 	}
 
 	/* store for later use */
-	phy->gpio_irq = pdata->gpio_irq;
 	phy->gpio_reset = pdata->gpio_reset;
 	phy->irq_polarity = pdata->irq_polarity;
 
-	r = devm_gpio_request_one(&client->dev, phy->gpio_irq,
-				GPIOF_IN, "clf_irq");
-	if (r) {
-		pr_err("%s : gpio_request failed\n", __FILE__);
-		return -ENODEV;
-	}
-
 	r = devm_gpio_request_one(&client->dev,
 			phy->gpio_reset, GPIOF_OUT_INIT_HIGH, "clf_reset");
 	if (r) {
 		pr_err("%s : reset gpio_request failed\n", __FILE__);
-		return -ENODEV;
+		return r;
 	}
 
-	/* IRQ */
-	irq = gpio_to_irq(phy->gpio_irq);
-	if (irq < 0) {
-		nfc_err(&client->dev,
-			"Unable to get irq number for GPIO %d error %d\n",
-			phy->gpio_irq, r);
-		return -ENODEV;
-	}
-	client->irq = irq;
-
 	return 0;
 }
 
@@ -401,10 +371,13 @@
 	return 0;
 }
 
+#ifdef CONFIG_OF
 static const struct of_device_id of_st21nfcb_i2c_match[] = {
 	{ .compatible = "st,st21nfcb_i2c", },
 	{}
 };
+MODULE_DEVICE_TABLE(of, of_st21nfcb_i2c_match);
+#endif
 
 static struct i2c_driver st21nfcb_nci_i2c_driver = {
 	.driver = {
diff --git a/drivers/nfc/st21nfcb/ndlc.c b/drivers/nfc/st21nfcb/ndlc.c
index e7bff89..bac50e8 100644
--- a/drivers/nfc/st21nfcb/ndlc.c
+++ b/drivers/nfc/st21nfcb/ndlc.c
@@ -266,7 +266,7 @@
 
 	*ndlc_id = ndlc;
 
-	/* start timers */
+	/* initialize timers */
 	init_timer(&ndlc->t1_timer);
 	ndlc->t1_timer.data = (unsigned long)ndlc;
 	ndlc->t1_timer.function = ndlc_t1_timeout;
diff --git a/include/linux/ieee802154.h b/include/linux/ieee802154.h
index d043449..6e82d88 100644
--- a/include/linux/ieee802154.h
+++ b/include/linux/ieee802154.h
@@ -30,8 +30,18 @@
 #define IEEE802154_MTU			127
 #define IEEE802154_MIN_PSDU_LEN		5
 
+#define IEEE802154_PAN_ID_BROADCAST	0xffff
+#define IEEE802154_ADDR_SHORT_BROADCAST	0xffff
+#define IEEE802154_ADDR_SHORT_UNSPEC	0xfffe
+
 #define IEEE802154_EXTENDED_ADDR_LEN	8
 
+#define IEEE802154_LIFS_PERIOD		40
+#define IEEE802154_SIFS_PERIOD		12
+
+#define IEEE802154_MAX_CHANNEL		26
+#define IEEE802154_MAX_PAGE		31
+
 #define IEEE802154_FC_TYPE_BEACON	0x0	/* Frame is beacon */
 #define	IEEE802154_FC_TYPE_DATA		0x1	/* Frame is data */
 #define IEEE802154_FC_TYPE_ACK		0x2	/* Frame is acknowledgment */
diff --git a/include/linux/platform_data/st21nfca.h b/include/linux/platform_data/st21nfca.h
index 1730312..5087fff 100644
--- a/include/linux/platform_data/st21nfca.h
+++ b/include/linux/platform_data/st21nfca.h
@@ -24,7 +24,6 @@
 #define ST21NFCA_HCI_DRIVER_NAME "st21nfca_hci"
 
 struct st21nfca_nfc_platform_data {
-	unsigned int gpio_irq;
 	unsigned int gpio_ena;
 	unsigned int irq_polarity;
 };
diff --git a/include/linux/platform_data/st21nfcb.h b/include/linux/platform_data/st21nfcb.h
index 2d11f1f..c3b432f 100644
--- a/include/linux/platform_data/st21nfcb.h
+++ b/include/linux/platform_data/st21nfcb.h
@@ -24,7 +24,6 @@
 #define ST21NFCB_NCI_DRIVER_NAME "st21nfcb_nci"
 
 struct st21nfcb_nfc_platform_data {
-	unsigned int gpio_irq;
 	unsigned int gpio_reset;
 	unsigned int irq_polarity;
 };
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index d5f85d7..e56f909 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -639,7 +639,7 @@
 struct hci_cp_remote_oob_data_reply {
 	bdaddr_t bdaddr;
 	__u8     hash[16];
-	__u8     randomizer[16];
+	__u8     rand[16];
 } __packed;
 
 #define HCI_OP_REMOTE_OOB_DATA_NEG_REPLY	0x0433
@@ -731,9 +731,9 @@
 struct hci_cp_remote_oob_ext_data_reply {
 	bdaddr_t bdaddr;
 	__u8     hash192[16];
-	__u8     randomizer192[16];
+	__u8     rand192[16];
 	__u8     hash256[16];
-	__u8     randomizer256[16];
+	__u8     rand256[16];
 } __packed;
 
 #define HCI_OP_SNIFF_MODE		0x0803
@@ -940,7 +940,7 @@
 struct hci_rp_read_local_oob_data {
 	__u8     status;
 	__u8     hash[16];
-	__u8     randomizer[16];
+	__u8     rand[16];
 } __packed;
 
 #define HCI_OP_READ_INQ_RSP_TX_POWER	0x0c58
@@ -1024,9 +1024,9 @@
 struct hci_rp_read_local_oob_ext_data {
 	__u8     status;
 	__u8     hash192[16];
-	__u8     randomizer192[16];
+	__u8     rand192[16];
 	__u8     hash256[16];
-	__u8     randomizer256[16];
+	__u8     rand256[16];
 } __packed;
 
 #define HCI_OP_READ_LOCAL_VERSION	0x1001
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 4e39a5a..396c098 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -108,6 +108,7 @@
 
 struct smp_ltk {
 	struct list_head list;
+	struct rcu_head rcu;
 	bdaddr_t bdaddr;
 	u8 bdaddr_type;
 	u8 authenticated;
@@ -120,6 +121,7 @@
 
 struct smp_irk {
 	struct list_head list;
+	struct rcu_head rcu;
 	bdaddr_t rpa;
 	bdaddr_t bdaddr;
 	u8 addr_type;
@@ -128,6 +130,7 @@
 
 struct link_key {
 	struct list_head list;
+	struct rcu_head rcu;
 	bdaddr_t bdaddr;
 	u8 type;
 	u8 val[HCI_LINK_KEY_SIZE];
@@ -138,9 +141,9 @@
 	struct list_head list;
 	bdaddr_t bdaddr;
 	u8 hash192[16];
-	u8 randomizer192[16];
+	u8 rand192[16];
 	u8 hash256[16];
-	u8 randomizer256[16];
+	u8 rand256[16];
 };
 
 #define HCI_MAX_SHORT_NAME_LENGTH	10
@@ -941,10 +944,10 @@
 struct oob_data *hci_find_remote_oob_data(struct hci_dev *hdev,
 					  bdaddr_t *bdaddr);
 int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
-			    u8 *hash, u8 *randomizer);
+			    u8 *hash, u8 *rand);
 int hci_add_remote_oob_ext_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
-				u8 *hash192, u8 *randomizer192,
-				u8 *hash256, u8 *randomizer256);
+				u8 *hash192, u8 *rand192,
+				u8 *hash256, u8 *rand256);
 int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr);
 
 void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb);
@@ -1372,8 +1375,8 @@
 				    u8 status);
 void mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status);
 void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192,
-				       u8 *randomizer192, u8 *hash256,
-				       u8 *randomizer256, u8 status);
+				       u8 *rand192, u8 *hash256, u8 *rand256,
+				       u8 status);
 void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
 		       u8 addr_type, u8 *dev_class, s8 rssi, u32 flags,
 		       u8 *eir, u16 eir_len, u8 *scan_rsp, u8 scan_rsp_len);
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index ead99f0..061e648 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -28,6 +28,7 @@
 #define __L2CAP_H
 
 #include <asm/unaligned.h>
+#include <linux/atomic.h>
 
 /* L2CAP defaults */
 #define L2CAP_DEFAULT_MTU		672
@@ -481,6 +482,7 @@
 	struct hci_conn		*hs_hcon;
 	struct hci_chan		*hs_hchan;
 	struct kref	kref;
+	atomic_t	nesting;
 
 	__u8		state;
 
@@ -713,6 +715,17 @@
 	FLAG_HOLD_HCI_CONN,
 };
 
+/* Lock nesting levels for L2CAP channels. We need these because lockdep
+ * otherwise considers all channels equal and will e.g. complain about a
+ * connection oriented channel triggering SMP procedures or a listening
+ * channel creating and locking a child channel.
+ */
+enum {
+	L2CAP_NESTING_SMP,
+	L2CAP_NESTING_NORMAL,
+	L2CAP_NESTING_PARENT,
+};
+
 enum {
 	L2CAP_TX_STATE_XMIT,
 	L2CAP_TX_STATE_WAIT_F,
@@ -778,7 +791,7 @@
 
 static inline void l2cap_chan_lock(struct l2cap_chan *chan)
 {
-	mutex_lock(&chan->lock);
+	mutex_lock_nested(&chan->lock, atomic_read(&chan->nesting));
 }
 
 static inline void l2cap_chan_unlock(struct l2cap_chan *chan)
diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
index 414cd2f..b391fd6 100644
--- a/include/net/bluetooth/mgmt.h
+++ b/include/net/bluetooth/mgmt.h
@@ -299,28 +299,28 @@
 #define MGMT_READ_LOCAL_OOB_DATA_SIZE	0
 struct mgmt_rp_read_local_oob_data {
 	__u8	hash[16];
-	__u8	randomizer[16];
+	__u8	rand[16];
 } __packed;
 struct mgmt_rp_read_local_oob_ext_data {
 	__u8	hash192[16];
-	__u8	randomizer192[16];
+	__u8	rand192[16];
 	__u8	hash256[16];
-	__u8	randomizer256[16];
+	__u8	rand256[16];
 } __packed;
 
 #define MGMT_OP_ADD_REMOTE_OOB_DATA	0x0021
 struct mgmt_cp_add_remote_oob_data {
 	struct mgmt_addr_info addr;
 	__u8	hash[16];
-	__u8	randomizer[16];
+	__u8	rand[16];
 } __packed;
 #define MGMT_ADD_REMOTE_OOB_DATA_SIZE	(MGMT_ADDR_INFO_SIZE + 32)
 struct mgmt_cp_add_remote_oob_ext_data {
 	struct mgmt_addr_info addr;
 	__u8	hash192[16];
-	__u8	randomizer192[16];
+	__u8	rand192[16];
 	__u8	hash256[16];
-	__u8	randomizer256[16];
+	__u8	rand256[16];
 } __packed;
 #define MGMT_ADD_REMOTE_OOB_EXT_DATA_SIZE (MGMT_ADDR_INFO_SIZE + 64)
 
diff --git a/include/net/cfg802154.h b/include/net/cfg802154.h
index 9d99b96..7f713ac 100644
--- a/include/net/cfg802154.h
+++ b/include/net/cfg802154.h
@@ -17,17 +17,12 @@
 #ifndef __NET_CFG802154_H
 #define __NET_CFG802154_H
 
+#include <linux/ieee802154.h>
 #include <linux/netdevice.h>
 #include <linux/mutex.h>
 #include <linux/bug.h>
 
-/* According to the IEEE 802.15.4 stadard the upper most significant bits of
- * the 32-bit channel bitmaps shall be used as an integer value to specify 32
- * possible channel pages. The lower 27 bits of the channel bit map shall be
- * used as a bit mask to specify channel numbers within a channel page.
- */
-#define WPAN_NUM_CHANNELS	27
-#define WPAN_NUM_PAGES		32
+#include <net/nl802154.h>
 
 struct wpan_phy;
 
@@ -35,13 +30,43 @@
 	struct net_device * (*add_virtual_intf_deprecated)(struct wpan_phy *wpan_phy,
 							   const char *name,
 							   int type);
-	void (*del_virtual_intf_deprecated)(struct wpan_phy *wpan_phy,
-					    struct net_device *dev);
+	void	(*del_virtual_intf_deprecated)(struct wpan_phy *wpan_phy,
+					       struct net_device *dev);
+	int	(*add_virtual_intf)(struct wpan_phy *wpan_phy,
+				    const char *name,
+				    enum nl802154_iftype type,
+				    __le64 extended_addr);
+	int	(*del_virtual_intf)(struct wpan_phy *wpan_phy,
+				    struct wpan_dev *wpan_dev);
+	int	(*set_channel)(struct wpan_phy *wpan_phy, u8 page, u8 channel);
+	int	(*set_pan_id)(struct wpan_phy *wpan_phy,
+			      struct wpan_dev *wpan_dev, __le16 pan_id);
+	int	(*set_short_addr)(struct wpan_phy *wpan_phy,
+				  struct wpan_dev *wpan_dev, __le16 short_addr);
+	int	(*set_backoff_exponent)(struct wpan_phy *wpan_phy,
+					struct wpan_dev *wpan_dev, u8 min_be,
+					u8 max_be);
+	int	(*set_max_csma_backoffs)(struct wpan_phy *wpan_phy,
+					 struct wpan_dev *wpan_dev,
+					 u8 max_csma_backoffs);
+	int	(*set_max_frame_retries)(struct wpan_phy *wpan_phy,
+					 struct wpan_dev *wpan_dev,
+					 s8 max_frame_retries);
+	int	(*set_lbt_mode)(struct wpan_phy *wpan_phy,
+				struct wpan_dev *wpan_dev, bool mode);
 };
 
 struct wpan_phy {
 	struct mutex pib_lock;
 
+	/* If multiple wpan_phys are registered and you're handed e.g.
+	 * a regular netdev with assigned ieee802154_ptr, you won't
+	 * know whether it points to a wpan_phy your driver has registered
+	 * or not. Assign this to something global to your driver to
+	 * help determine whether you own this wpan_phy or not.
+	 */
+	const void *privid;
+
 	/*
 	 * This is a PIB according to 802.15.4-2011.
 	 * We do not provide timing-related variables, as they
@@ -49,19 +74,22 @@
 	 */
 	u8 current_channel;
 	u8 current_page;
-	u32 channels_supported[32];
+	u32 channels_supported[IEEE802154_MAX_PAGE + 1];
 	s8 transmit_power;
 	u8 cca_mode;
-	u8 min_be;
-	u8 max_be;
-	u8 csma_retries;
-	s8 frame_retries;
 
 	__le64 perm_extended_addr;
 
-	bool lbt;
 	s32 cca_ed_level;
 
+	/* PHY depended MAC PIB values */
+
+	/* 802.15.4 acronym: Tdsym in usec */
+	u8 symbol_duration;
+	/* lifs and sifs periods timing */
+	u16 lifs_period;
+	u16 sifs_period;
+
 	struct device dev;
 
 	char priv[0] __aligned(NETDEV_ALIGN);
@@ -69,12 +97,38 @@
 
 struct wpan_dev {
 	struct wpan_phy *wpan_phy;
+	int iftype;
+
+	/* the remainder of this struct should be private to cfg802154 */
+	struct list_head list;
+	struct net_device *netdev;
+
+	u32 identifier;
+
+	/* MAC PIB */
+	__le16 pan_id;
+	__le16 short_addr;
+	__le64 extended_addr;
+
+	/* MAC BSN field */
+	u8 bsn;
+	/* MAC DSN field */
+	u8 dsn;
+
+	u8 min_be;
+	u8 max_be;
+	u8 csma_retries;
+	s8 frame_retries;
+
+	bool lbt;
+
+	bool promiscuous_mode;
 };
 
 #define to_phy(_dev)	container_of(_dev, struct wpan_phy, dev)
 
 struct wpan_phy *
-wpan_phy_alloc(const struct cfg802154_ops *ops, size_t priv_size);
+wpan_phy_new(const struct cfg802154_ops *ops, size_t priv_size);
 static inline void wpan_phy_set_dev(struct wpan_phy *phy, struct device *dev)
 {
 	phy->dev.parent = dev;
diff --git a/include/net/mac802154.h b/include/net/mac802154.h
index 632f656..c823d91 100644
--- a/include/net/mac802154.h
+++ b/include/net/mac802154.h
@@ -260,6 +260,7 @@
 
 void ieee802154_wake_queue(struct ieee802154_hw *hw);
 void ieee802154_stop_queue(struct ieee802154_hw *hw);
-void ieee802154_xmit_complete(struct ieee802154_hw *hw, struct sk_buff *skb);
+void ieee802154_xmit_complete(struct ieee802154_hw *hw, struct sk_buff *skb,
+			      bool ifs_handling);
 
 #endif /* NET_MAC802154_H */
diff --git a/include/net/nfc/digital.h b/include/net/nfc/digital.h
index d9a5cf7..0ae101e 100644
--- a/include/net/nfc/digital.h
+++ b/include/net/nfc/digital.h
@@ -225,6 +225,19 @@
 	u8 curr_protocol;
 	u8 curr_rf_tech;
 	u8 curr_nfc_dep_pni;
+	u8 did;
+
+	u8 local_payload_max;
+	u8 remote_payload_max;
+
+	struct sk_buff *chaining_skb;
+	struct digital_data_exch *data_exch;
+
+	int atn_count;
+	int nack_count;
+
+	struct sk_buff *saved_skb;
+	unsigned int saved_skb_len;
 
 	u16 target_fsc;
 
diff --git a/include/net/nfc/hci.h b/include/net/nfc/hci.h
index 7ee8f4c..14bd0e1 100644
--- a/include/net/nfc/hci.h
+++ b/include/net/nfc/hci.h
@@ -57,10 +57,14 @@
 	int (*discover_se)(struct nfc_hci_dev *dev);
 	int (*enable_se)(struct nfc_hci_dev *dev, u32 se_idx);
 	int (*disable_se)(struct nfc_hci_dev *dev, u32 se_idx);
+	int (*se_io)(struct nfc_hci_dev *dev, u32 se_idx,
+		      u8 *apdu, size_t apdu_length,
+		      se_io_cb_t cb, void *cb_context);
 };
 
 /* Pipes */
 #define NFC_HCI_INVALID_PIPE	0x80
+#define NFC_HCI_DO_NOT_CREATE_PIPE	0x81
 #define NFC_HCI_LINK_MGMT_PIPE	0x00
 #define NFC_HCI_ADMIN_PIPE	0x01
 
diff --git a/include/net/nfc/nci.h b/include/net/nfc/nci.h
index 9eca9ae..e7257a4 100644
--- a/include/net/nfc/nci.h
+++ b/include/net/nfc/nci.h
@@ -28,6 +28,8 @@
 #ifndef __NCI_H
 #define __NCI_H
 
+#include <net/nfc/nfc.h>
+
 /* NCI constants */
 #define NCI_MAX_NUM_MAPPING_CONFIGS				10
 #define NCI_MAX_NUM_RF_CONFIGS					10
@@ -73,6 +75,8 @@
 #define NCI_NFC_A_ACTIVE_LISTEN_MODE				0x83
 #define NCI_NFC_F_ACTIVE_LISTEN_MODE				0x85
 
+#define NCI_RF_TECH_MODE_LISTEN_MASK				0x80
+
 /* NCI RF Technologies */
 #define NCI_NFC_RF_TECHNOLOGY_A					0x00
 #define NCI_NFC_RF_TECHNOLOGY_B					0x01
@@ -106,6 +110,17 @@
 
 /* NCI Configuration Parameter Tags */
 #define NCI_PN_ATR_REQ_GEN_BYTES				0x29
+#define NCI_LN_ATR_RES_GEN_BYTES				0x61
+#define NCI_LA_SEL_INFO						0x32
+#define NCI_LF_PROTOCOL_TYPE					0x50
+#define NCI_LF_CON_BITR_F					0x54
+
+/* NCI Configuration Parameters masks */
+#define NCI_LA_SEL_INFO_ISO_DEP_MASK				0x20
+#define NCI_LA_SEL_INFO_NFC_DEP_MASK				0x40
+#define NCI_LF_PROTOCOL_TYPE_NFC_DEP_MASK			0x02
+#define NCI_LF_CON_BITR_F_212					0x02
+#define NCI_LF_CON_BITR_F_424					0x04
 
 /* NCI Reset types */
 #define NCI_RESET_TYPE_KEEP_CONFIG				0x00
@@ -314,26 +329,31 @@
 struct rf_tech_specific_params_nfca_poll {
 	__u16	sens_res;
 	__u8	nfcid1_len;	/* 0, 4, 7, or 10 Bytes */
-	__u8	nfcid1[10];
+	__u8	nfcid1[NFC_NFCID1_MAXSIZE];
 	__u8	sel_res_len;	/* 0 or 1 Bytes */
 	__u8	sel_res;
 } __packed;
 
 struct rf_tech_specific_params_nfcb_poll {
 	__u8	sensb_res_len;
-	__u8	sensb_res[12];	/* 11 or 12 Bytes */
+	__u8	sensb_res[NFC_SENSB_RES_MAXSIZE];	/* 11 or 12 Bytes */
 } __packed;
 
 struct rf_tech_specific_params_nfcf_poll {
 	__u8	bit_rate;
 	__u8	sensf_res_len;
-	__u8	sensf_res[18];	/* 16 or 18 Bytes */
+	__u8	sensf_res[NFC_SENSF_RES_MAXSIZE];	/* 16 or 18 Bytes */
 } __packed;
 
 struct rf_tech_specific_params_nfcv_poll {
 	__u8	res_flags;
 	__u8	dsfid;
-	__u8	uid[8];	/* 8 Bytes */
+	__u8	uid[NFC_ISO15693_UID_MAXSIZE];	/* 8 Bytes */
+} __packed;
+
+struct rf_tech_specific_params_nfcf_listen {
+	__u8	local_nfcid2_len;
+	__u8	local_nfcid2[NFC_NFCID2_MAXSIZE];	/* 0 or 8 Bytes */
 } __packed;
 
 struct nci_rf_discover_ntf {
@@ -365,7 +385,12 @@
 
 struct activation_params_poll_nfc_dep {
 	__u8	atr_res_len;
-	__u8	atr_res[63];
+	__u8	atr_res[NFC_ATR_RES_MAXSIZE - 2]; /* ATR_RES from byte 3 */
+};
+
+struct activation_params_listen_nfc_dep {
+	__u8	atr_req_len;
+	__u8	atr_req[NFC_ATR_REQ_MAXSIZE - 2]; /* ATR_REQ from byte 3 */
 };
 
 struct nci_rf_intf_activated_ntf {
@@ -382,6 +407,7 @@
 		struct rf_tech_specific_params_nfcb_poll nfcb_poll;
 		struct rf_tech_specific_params_nfcf_poll nfcf_poll;
 		struct rf_tech_specific_params_nfcv_poll nfcv_poll;
+		struct rf_tech_specific_params_nfcf_listen nfcf_listen;
 	} rf_tech_specific_params;
 
 	__u8	data_exch_rf_tech_and_mode;
@@ -393,6 +419,7 @@
 		struct activation_params_nfca_poll_iso_dep nfca_poll_iso_dep;
 		struct activation_params_nfcb_poll_iso_dep nfcb_poll_iso_dep;
 		struct activation_params_poll_nfc_dep poll_nfc_dep;
+		struct activation_params_listen_nfc_dep listen_nfc_dep;
 	} activation_params;
 
 } __packed;
diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h
index 75d10e6..9e51bb4 100644
--- a/include/net/nfc/nci_core.h
+++ b/include/net/nfc/nci_core.h
@@ -4,6 +4,7 @@
  *
  *  Copyright (C) 2011 Texas Instruments, Inc.
  *  Copyright (C) 2013 Intel Corporation. All rights reserved.
+ *  Copyright (C) 2014 Marvell International Ltd.
  *
  *  Written by Ilan Elias <ilane@ti.com>
  *
@@ -49,6 +50,8 @@
 	NCI_W4_ALL_DISCOVERIES,
 	NCI_W4_HOST_SELECT,
 	NCI_POLL_ACTIVE,
+	NCI_LISTEN_ACTIVE,
+	NCI_LISTEN_SLEEP,
 };
 
 /* NCI timeouts */
@@ -69,6 +72,12 @@
 	int   (*send)(struct nci_dev *ndev, struct sk_buff *skb);
 	int   (*setup)(struct nci_dev *ndev);
 	__u32 (*get_rfprotocol)(struct nci_dev *ndev, __u8 rf_protocol);
+	int   (*discover_se)(struct nci_dev *ndev);
+	int   (*disable_se)(struct nci_dev *ndev, u32 se_idx);
+	int   (*enable_se)(struct nci_dev *ndev, u32 se_idx);
+	int   (*se_io)(struct nci_dev *ndev, u32 se_idx,
+				u8 *apdu, size_t apdu_length,
+				se_io_cb_t cb, void *cb_context);
 };
 
 #define NCI_MAX_SUPPORTED_RF_INTERFACES		4
diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h
index 6c583e2..12adb81 100644
--- a/include/net/nfc/nfc.h
+++ b/include/net/nfc/nfc.h
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2011 Instituto Nokia de Tecnologia
+ * Copyright (C) 2014 Marvell International Ltd.
  *
  * Authors:
  *    Lauro Ramos Venancio <lauro.venancio@openbossa.org>
@@ -87,6 +88,7 @@
 #define NFC_TARGET_IDX_ANY -1
 #define NFC_MAX_GT_LEN 48
 #define NFC_ATR_RES_GT_OFFSET 15
+#define NFC_ATR_REQ_GT_OFFSET 14
 
 /**
  * struct nfc_target - NFC target descriptiom
diff --git a/include/net/nl802154.h b/include/net/nl802154.h
new file mode 100644
index 0000000..6dbd406
--- /dev/null
+++ b/include/net/nl802154.h
@@ -0,0 +1,122 @@
+#ifndef __NL802154_H
+#define __NL802154_H
+/*
+ * 802.15.4 netlink interface public header
+ *
+ * Copyright 2014 Alexander Aring <aar@pengutronix.de>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#define NL802154_GENL_NAME "nl802154"
+
+enum nl802154_commands {
+/* don't change the order or add anything between, this is ABI! */
+/* currently we don't shipping this file via uapi, ignore the above one */
+	NL802154_CMD_UNSPEC,
+
+	NL802154_CMD_GET_WPAN_PHY,		/* can dump */
+	NL802154_CMD_SET_WPAN_PHY,
+	NL802154_CMD_NEW_WPAN_PHY,
+	NL802154_CMD_DEL_WPAN_PHY,
+
+	NL802154_CMD_GET_INTERFACE,		/* can dump */
+	NL802154_CMD_SET_INTERFACE,
+	NL802154_CMD_NEW_INTERFACE,
+	NL802154_CMD_DEL_INTERFACE,
+
+	NL802154_CMD_SET_CHANNEL,
+
+	NL802154_CMD_SET_PAN_ID,
+	NL802154_CMD_SET_SHORT_ADDR,
+
+	NL802154_CMD_SET_TX_POWER,
+	NL802154_CMD_SET_CCA_MODE,
+	NL802154_CMD_SET_CCA_ED_LEVEL,
+
+	NL802154_CMD_SET_MAX_FRAME_RETRIES,
+
+	NL802154_CMD_SET_BACKOFF_EXPONENT,
+	NL802154_CMD_SET_MAX_CSMA_BACKOFFS,
+
+	NL802154_CMD_SET_LBT_MODE,
+
+	/* add new commands above here */
+
+	/* used to define NL802154_CMD_MAX below */
+	__NL802154_CMD_AFTER_LAST,
+	NL802154_CMD_MAX = __NL802154_CMD_AFTER_LAST - 1
+};
+
+enum nl802154_attrs {
+/* don't change the order or add anything between, this is ABI! */
+/* currently we don't shipping this file via uapi, ignore the above one */
+	NL802154_ATTR_UNSPEC,
+
+	NL802154_ATTR_WPAN_PHY,
+	NL802154_ATTR_WPAN_PHY_NAME,
+
+	NL802154_ATTR_IFINDEX,
+	NL802154_ATTR_IFNAME,
+	NL802154_ATTR_IFTYPE,
+
+	NL802154_ATTR_WPAN_DEV,
+
+	NL802154_ATTR_PAGE,
+	NL802154_ATTR_CHANNEL,
+
+	NL802154_ATTR_PAN_ID,
+	NL802154_ATTR_SHORT_ADDR,
+
+	NL802154_ATTR_TX_POWER,
+
+	NL802154_ATTR_CCA_MODE,
+	NL802154_ATTR_CCA_MODE3_AND,
+	NL802154_ATTR_CCA_ED_LEVEL,
+
+	NL802154_ATTR_MAX_FRAME_RETRIES,
+
+	NL802154_ATTR_MAX_BE,
+	NL802154_ATTR_MIN_BE,
+	NL802154_ATTR_MAX_CSMA_BACKOFFS,
+
+	NL802154_ATTR_LBT_MODE,
+
+	NL802154_ATTR_GENERATION,
+
+	NL802154_ATTR_CHANNELS_SUPPORTED,
+	NL802154_ATTR_SUPPORTED_CHANNEL,
+
+	NL802154_ATTR_EXTENDED_ADDR,
+
+	/* add attributes here, update the policy in nl802154.c */
+
+	__NL802154_ATTR_AFTER_LAST,
+	NL802154_ATTR_MAX = __NL802154_ATTR_AFTER_LAST - 1
+};
+
+enum nl802154_iftype {
+	/* for backwards compatibility TODO */
+	NL802154_IFTYPE_UNSPEC = -1,
+
+	NL802154_IFTYPE_NODE,
+	NL802154_IFTYPE_MONITOR,
+	NL802154_IFTYPE_COORD,
+
+	/* keep last */
+	NUM_NL802154_IFTYPES,
+	NL802154_IFTYPE_MAX = NUM_NL802154_IFTYPES - 1
+};
+
+#endif /* __NL802154_H */
diff --git a/include/uapi/linux/nfc.h b/include/uapi/linux/nfc.h
index 9b19b44..8119255 100644
--- a/include/uapi/linux/nfc.h
+++ b/include/uapi/linux/nfc.h
@@ -116,6 +116,7 @@
 	NFC_EVENT_SE_TRANSACTION,
 	NFC_CMD_GET_SE,
 	NFC_CMD_SE_IO,
+	NFC_CMD_ACTIVATE_TARGET,
 /* private: internal use only */
 	__NFC_CMD_AFTER_LAST
 };
@@ -196,15 +197,19 @@
 };
 #define NFC_SDP_ATTR_MAX (__NFC_SDP_ATTR_AFTER_LAST - 1)
 
-#define NFC_DEVICE_NAME_MAXSIZE 8
-#define NFC_NFCID1_MAXSIZE 10
-#define NFC_NFCID2_MAXSIZE 8
-#define NFC_NFCID3_MAXSIZE 10
-#define NFC_SENSB_RES_MAXSIZE 12
-#define NFC_SENSF_RES_MAXSIZE 18
-#define NFC_GB_MAXSIZE        48
-#define NFC_FIRMWARE_NAME_MAXSIZE 32
-#define NFC_ISO15693_UID_MAXSIZE 8
+#define NFC_DEVICE_NAME_MAXSIZE		8
+#define NFC_NFCID1_MAXSIZE		10
+#define NFC_NFCID2_MAXSIZE		8
+#define NFC_NFCID3_MAXSIZE		10
+#define NFC_SENSB_RES_MAXSIZE		12
+#define NFC_SENSF_RES_MAXSIZE		18
+#define NFC_ATR_REQ_MAXSIZE		64
+#define NFC_ATR_RES_MAXSIZE		64
+#define NFC_ATR_REQ_GB_MAXSIZE		48
+#define NFC_ATR_RES_GB_MAXSIZE		47
+#define NFC_GB_MAXSIZE			48
+#define NFC_FIRMWARE_NAME_MAXSIZE	32
+#define NFC_ISO15693_UID_MAXSIZE	8
 
 /* NFC protocols */
 #define NFC_PROTO_JEWEL		1
diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
index dc23c55..bdcaefd 100644
--- a/net/bluetooth/6lowpan.c
+++ b/net/bluetooth/6lowpan.c
@@ -87,13 +87,6 @@
 	struct delayed_work notify_peers;
 };
 
-static inline void peer_free(struct rcu_head *head)
-{
-	struct lowpan_peer *e = container_of(head, struct lowpan_peer, rcu);
-
-	kfree(e);
-}
-
 static inline struct lowpan_dev *lowpan_dev(const struct net_device *netdev)
 {
 	return netdev_priv(netdev);
@@ -108,7 +101,7 @@
 static inline bool peer_del(struct lowpan_dev *dev, struct lowpan_peer *peer)
 {
 	list_del_rcu(&peer->list);
-	call_rcu(&peer->rcu, peer_free);
+	kfree_rcu(peer, rcu);
 
 	module_put(THIS_MODULE);
 
@@ -614,17 +607,13 @@
 	int err = 0;
 	bdaddr_t addr;
 	u8 addr_type;
-	struct sk_buff *tmpskb;
 
 	/* We must take a copy of the skb before we modify/replace the ipv6
 	 * header as the header could be used elsewhere
 	 */
-	tmpskb = skb_unshare(skb, GFP_ATOMIC);
-	if (!tmpskb) {
-		kfree_skb(skb);
+	skb = skb_unshare(skb, GFP_ATOMIC);
+	if (!skb)
 		return NET_XMIT_DROP;
-	}
-	skb = tmpskb;
 
 	/* Return values from setup_header()
 	 *  <0 - error, packet is dropped
@@ -1141,6 +1130,8 @@
 	pchan->state = BT_LISTEN;
 	pchan->src_type = BDADDR_LE_PUBLIC;
 
+	atomic_set(&pchan->nesting, L2CAP_NESTING_PARENT);
+
 	BT_DBG("psm 0x%04x chan %p src type %d", psm_6lowpan, pchan,
 	       pchan->src_type);
 
@@ -1223,7 +1214,7 @@
 		l2cap_chan_close(peer->chan, ENOENT);
 
 		list_del_rcu(&peer->list);
-		call_rcu(&peer->rcu, peer_free);
+		kfree_rcu(peer, rcu);
 
 		module_put(THIS_MODULE);
 	}
diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c
index 2640d78..ee016f0 100644
--- a/net/bluetooth/amp.c
+++ b/net/bluetooth/amp.c
@@ -134,6 +134,7 @@
 static int hmac_sha256(u8 *key, u8 ksize, char *plaintext, u8 psize, u8 *output)
 {
 	struct crypto_shash *tfm;
+	struct shash_desc *shash;
 	int ret;
 
 	if (!ksize)
@@ -148,18 +149,24 @@
 	ret = crypto_shash_setkey(tfm, key, ksize);
 	if (ret) {
 		BT_DBG("crypto_ahash_setkey failed: err %d", ret);
-	} else {
-		char desc[sizeof(struct shash_desc) +
-			crypto_shash_descsize(tfm)] CRYPTO_MINALIGN_ATTR;
-		struct shash_desc *shash = (struct shash_desc *)desc;
-
-		shash->tfm = tfm;
-		shash->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
-
-		ret = crypto_shash_digest(shash, plaintext, psize,
-					  output);
+		goto failed;
 	}
 
+	shash = kzalloc(sizeof(*shash) + crypto_shash_descsize(tfm),
+			GFP_KERNEL);
+	if (!shash) {
+		ret = -ENOMEM;
+		goto failed;
+	}
+
+	shash->tfm = tfm;
+	shash->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+
+	ret = crypto_shash_digest(shash, plaintext, psize, output);
+
+	kfree(shash);
+
+failed:
 	crypto_free_shash(tfm);
 	return ret;
 }
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 6c162c8..5c319a4 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -274,15 +274,13 @@
 static int link_keys_show(struct seq_file *f, void *ptr)
 {
 	struct hci_dev *hdev = f->private;
-	struct list_head *p, *n;
+	struct link_key *key;
 
-	hci_dev_lock(hdev);
-	list_for_each_safe(p, n, &hdev->link_keys) {
-		struct link_key *key = list_entry(p, struct link_key, list);
+	rcu_read_lock();
+	list_for_each_entry_rcu(key, &hdev->link_keys, list)
 		seq_printf(f, "%pMR %u %*phN %u\n", &key->bdaddr, key->type,
 			   HCI_LINK_KEY_SIZE, key->val, key->pin_len);
-	}
-	hci_dev_unlock(hdev);
+	rcu_read_unlock();
 
 	return 0;
 }
@@ -748,16 +746,15 @@
 static int identity_resolving_keys_show(struct seq_file *f, void *ptr)
 {
 	struct hci_dev *hdev = f->private;
-	struct list_head *p, *n;
+	struct smp_irk *irk;
 
-	hci_dev_lock(hdev);
-	list_for_each_safe(p, n, &hdev->identity_resolving_keys) {
-		struct smp_irk *irk = list_entry(p, struct smp_irk, list);
+	rcu_read_lock();
+	list_for_each_entry_rcu(irk, &hdev->identity_resolving_keys, list) {
 		seq_printf(f, "%pMR (type %u) %*phN %pMR\n",
 			   &irk->bdaddr, irk->addr_type,
 			   16, irk->val, &irk->rpa);
 	}
-	hci_dev_unlock(hdev);
+	rcu_read_unlock();
 
 	return 0;
 }
@@ -778,17 +775,15 @@
 static int long_term_keys_show(struct seq_file *f, void *ptr)
 {
 	struct hci_dev *hdev = f->private;
-	struct list_head *p, *n;
+	struct smp_ltk *ltk;
 
-	hci_dev_lock(hdev);
-	list_for_each_safe(p, n, &hdev->long_term_keys) {
-		struct smp_ltk *ltk = list_entry(p, struct smp_ltk, list);
+	rcu_read_lock();
+	list_for_each_entry_rcu(ltk, &hdev->long_term_keys, list)
 		seq_printf(f, "%pMR (type %u) %u 0x%02x %u %.4x %.16llx %*phN\n",
 			   &ltk->bdaddr, ltk->bdaddr_type, ltk->authenticated,
 			   ltk->type, ltk->enc_size, __le16_to_cpu(ltk->ediv),
 			   __le64_to_cpu(ltk->rand), 16, ltk->val);
-	}
-	hci_dev_unlock(hdev);
+	rcu_read_unlock();
 
 	return 0;
 }
@@ -1131,6 +1126,7 @@
 	err = hci_req_run(&req, hci_req_sync_complete);
 	if (err < 0) {
 		remove_wait_queue(&hdev->req_wait_q, &wait);
+		set_current_state(TASK_RUNNING);
 		return ERR_PTR(err);
 	}
 
@@ -1199,6 +1195,7 @@
 		hdev->req_status = 0;
 
 		remove_wait_queue(&hdev->req_wait_q, &wait);
+		set_current_state(TASK_RUNNING);
 
 		/* ENODATA means the HCI request command queue is empty.
 		 * This can happen when a request with conditionals doesn't
@@ -2564,6 +2561,11 @@
 	if (test_bit(HCI_MGMT, &hdev->dev_flags))
 		cancel_delayed_work_sync(&hdev->rpa_expired);
 
+	/* Avoid potential lockdep warnings from the *_flush() calls by
+	 * ensuring the workqueue is empty up front.
+	 */
+	drain_workqueue(hdev->workqueue);
+
 	hci_dev_lock(hdev);
 	hci_inquiry_cache_flush(hdev);
 	hci_pend_le_actions_clear(hdev);
@@ -2687,6 +2689,11 @@
 	skb_queue_purge(&hdev->rx_q);
 	skb_queue_purge(&hdev->cmd_q);
 
+	/* Avoid potential lockdep warnings from the *_flush() calls by
+	 * ensuring the workqueue is empty up front.
+	 */
+	drain_workqueue(hdev->workqueue);
+
 	hci_dev_lock(hdev);
 	hci_inquiry_cache_flush(hdev);
 	hci_conn_hash_flush(hdev);
@@ -3092,35 +3099,31 @@
 
 void hci_link_keys_clear(struct hci_dev *hdev)
 {
-	struct list_head *p, *n;
+	struct link_key *key;
 
-	list_for_each_safe(p, n, &hdev->link_keys) {
-		struct link_key *key;
-
-		key = list_entry(p, struct link_key, list);
-
-		list_del(p);
-		kfree(key);
+	list_for_each_entry_rcu(key, &hdev->link_keys, list) {
+		list_del_rcu(&key->list);
+		kfree_rcu(key, rcu);
 	}
 }
 
 void hci_smp_ltks_clear(struct hci_dev *hdev)
 {
-	struct smp_ltk *k, *tmp;
+	struct smp_ltk *k;
 
-	list_for_each_entry_safe(k, tmp, &hdev->long_term_keys, list) {
-		list_del(&k->list);
-		kfree(k);
+	list_for_each_entry_rcu(k, &hdev->long_term_keys, list) {
+		list_del_rcu(&k->list);
+		kfree_rcu(k, rcu);
 	}
 }
 
 void hci_smp_irks_clear(struct hci_dev *hdev)
 {
-	struct smp_irk *k, *tmp;
+	struct smp_irk *k;
 
-	list_for_each_entry_safe(k, tmp, &hdev->identity_resolving_keys, list) {
-		list_del(&k->list);
-		kfree(k);
+	list_for_each_entry_rcu(k, &hdev->identity_resolving_keys, list) {
+		list_del_rcu(&k->list);
+		kfree_rcu(k, rcu);
 	}
 }
 
@@ -3128,9 +3131,14 @@
 {
 	struct link_key *k;
 
-	list_for_each_entry(k, &hdev->link_keys, list)
-		if (bacmp(bdaddr, &k->bdaddr) == 0)
+	rcu_read_lock();
+	list_for_each_entry_rcu(k, &hdev->link_keys, list) {
+		if (bacmp(bdaddr, &k->bdaddr) == 0) {
+			rcu_read_unlock();
 			return k;
+		}
+	}
+	rcu_read_unlock();
 
 	return NULL;
 }
@@ -3184,15 +3192,18 @@
 {
 	struct smp_ltk *k;
 
-	list_for_each_entry(k, &hdev->long_term_keys, list) {
+	rcu_read_lock();
+	list_for_each_entry_rcu(k, &hdev->long_term_keys, list) {
 		if (k->ediv != ediv || k->rand != rand)
 			continue;
 
 		if (ltk_role(k->type) != role)
 			continue;
 
+		rcu_read_unlock();
 		return k;
 	}
+	rcu_read_unlock();
 
 	return NULL;
 }
@@ -3202,11 +3213,16 @@
 {
 	struct smp_ltk *k;
 
-	list_for_each_entry(k, &hdev->long_term_keys, list)
+	rcu_read_lock();
+	list_for_each_entry_rcu(k, &hdev->long_term_keys, list) {
 		if (addr_type == k->bdaddr_type &&
 		    bacmp(bdaddr, &k->bdaddr) == 0 &&
-		    ltk_role(k->type) == role)
+		    ltk_role(k->type) == role) {
+			rcu_read_unlock();
 			return k;
+		}
+	}
+	rcu_read_unlock();
 
 	return NULL;
 }
@@ -3215,18 +3231,23 @@
 {
 	struct smp_irk *irk;
 
-	list_for_each_entry(irk, &hdev->identity_resolving_keys, list) {
-		if (!bacmp(&irk->rpa, rpa))
-			return irk;
-	}
-
-	list_for_each_entry(irk, &hdev->identity_resolving_keys, list) {
-		if (smp_irk_matches(hdev, irk->val, rpa)) {
-			bacpy(&irk->rpa, rpa);
+	rcu_read_lock();
+	list_for_each_entry_rcu(irk, &hdev->identity_resolving_keys, list) {
+		if (!bacmp(&irk->rpa, rpa)) {
+			rcu_read_unlock();
 			return irk;
 		}
 	}
 
+	list_for_each_entry_rcu(irk, &hdev->identity_resolving_keys, list) {
+		if (smp_irk_matches(hdev, irk->val, rpa)) {
+			bacpy(&irk->rpa, rpa);
+			rcu_read_unlock();
+			return irk;
+		}
+	}
+	rcu_read_unlock();
+
 	return NULL;
 }
 
@@ -3239,11 +3260,15 @@
 	if (addr_type == ADDR_LE_DEV_RANDOM && (bdaddr->b[5] & 0xc0) != 0xc0)
 		return NULL;
 
-	list_for_each_entry(irk, &hdev->identity_resolving_keys, list) {
+	rcu_read_lock();
+	list_for_each_entry_rcu(irk, &hdev->identity_resolving_keys, list) {
 		if (addr_type == irk->addr_type &&
-		    bacmp(bdaddr, &irk->bdaddr) == 0)
+		    bacmp(bdaddr, &irk->bdaddr) == 0) {
+			rcu_read_unlock();
 			return irk;
+		}
 	}
+	rcu_read_unlock();
 
 	return NULL;
 }
@@ -3264,7 +3289,7 @@
 		key = kzalloc(sizeof(*key), GFP_KERNEL);
 		if (!key)
 			return NULL;
-		list_add(&key->list, &hdev->link_keys);
+		list_add_rcu(&key->list, &hdev->link_keys);
 	}
 
 	BT_DBG("%s key for %pMR type %u", hdev->name, bdaddr, type);
@@ -3309,7 +3334,7 @@
 		key = kzalloc(sizeof(*key), GFP_KERNEL);
 		if (!key)
 			return NULL;
-		list_add(&key->list, &hdev->long_term_keys);
+		list_add_rcu(&key->list, &hdev->long_term_keys);
 	}
 
 	bacpy(&key->bdaddr, bdaddr);
@@ -3338,7 +3363,7 @@
 		bacpy(&irk->bdaddr, bdaddr);
 		irk->addr_type = addr_type;
 
-		list_add(&irk->list, &hdev->identity_resolving_keys);
+		list_add_rcu(&irk->list, &hdev->identity_resolving_keys);
 	}
 
 	memcpy(irk->val, val, 16);
@@ -3357,25 +3382,25 @@
 
 	BT_DBG("%s removing %pMR", hdev->name, bdaddr);
 
-	list_del(&key->list);
-	kfree(key);
+	list_del_rcu(&key->list);
+	kfree_rcu(key, rcu);
 
 	return 0;
 }
 
 int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 bdaddr_type)
 {
-	struct smp_ltk *k, *tmp;
+	struct smp_ltk *k;
 	int removed = 0;
 
-	list_for_each_entry_safe(k, tmp, &hdev->long_term_keys, list) {
+	list_for_each_entry_rcu(k, &hdev->long_term_keys, list) {
 		if (bacmp(bdaddr, &k->bdaddr) || k->bdaddr_type != bdaddr_type)
 			continue;
 
 		BT_DBG("%s removing %pMR", hdev->name, bdaddr);
 
-		list_del(&k->list);
-		kfree(k);
+		list_del_rcu(&k->list);
+		kfree_rcu(k, rcu);
 		removed++;
 	}
 
@@ -3384,16 +3409,16 @@
 
 void hci_remove_irk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type)
 {
-	struct smp_irk *k, *tmp;
+	struct smp_irk *k;
 
-	list_for_each_entry_safe(k, tmp, &hdev->identity_resolving_keys, list) {
+	list_for_each_entry_rcu(k, &hdev->identity_resolving_keys, list) {
 		if (bacmp(bdaddr, &k->bdaddr) || k->addr_type != addr_type)
 			continue;
 
 		BT_DBG("%s removing %pMR", hdev->name, bdaddr);
 
-		list_del(&k->list);
-		kfree(k);
+		list_del_rcu(&k->list);
+		kfree_rcu(k, rcu);
 	}
 }
 
@@ -3455,7 +3480,7 @@
 }
 
 int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
-			    u8 *hash, u8 *randomizer)
+			    u8 *hash, u8 *rand)
 {
 	struct oob_data *data;
 
@@ -3470,10 +3495,10 @@
 	}
 
 	memcpy(data->hash192, hash, sizeof(data->hash192));
-	memcpy(data->randomizer192, randomizer, sizeof(data->randomizer192));
+	memcpy(data->rand192, rand, sizeof(data->rand192));
 
 	memset(data->hash256, 0, sizeof(data->hash256));
-	memset(data->randomizer256, 0, sizeof(data->randomizer256));
+	memset(data->rand256, 0, sizeof(data->rand256));
 
 	BT_DBG("%s for %pMR", hdev->name, bdaddr);
 
@@ -3481,8 +3506,8 @@
 }
 
 int hci_add_remote_oob_ext_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
-				u8 *hash192, u8 *randomizer192,
-				u8 *hash256, u8 *randomizer256)
+				u8 *hash192, u8 *rand192,
+				u8 *hash256, u8 *rand256)
 {
 	struct oob_data *data;
 
@@ -3497,10 +3522,10 @@
 	}
 
 	memcpy(data->hash192, hash192, sizeof(data->hash192));
-	memcpy(data->randomizer192, randomizer192, sizeof(data->randomizer192));
+	memcpy(data->rand192, rand192, sizeof(data->rand192));
 
 	memcpy(data->hash256, hash256, sizeof(data->hash256));
-	memcpy(data->randomizer256, randomizer256, sizeof(data->randomizer256));
+	memcpy(data->rand256, rand256, sizeof(data->rand256));
 
 	BT_DBG("%s for %pMR", hdev->name, bdaddr);
 
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 5e7be80..bd0a801 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -994,8 +994,8 @@
 	BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
 
 	hci_dev_lock(hdev);
-	mgmt_read_local_oob_data_complete(hdev, rp->hash, rp->randomizer,
-					  NULL, NULL, rp->status);
+	mgmt_read_local_oob_data_complete(hdev, rp->hash, rp->rand, NULL, NULL,
+					  rp->status);
 	hci_dev_unlock(hdev);
 }
 
@@ -1007,8 +1007,8 @@
 	BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
 
 	hci_dev_lock(hdev);
-	mgmt_read_local_oob_data_complete(hdev, rp->hash192, rp->randomizer192,
-					  rp->hash256, rp->randomizer256,
+	mgmt_read_local_oob_data_complete(hdev, rp->hash192, rp->rand192,
+					  rp->hash256, rp->rand256,
 					  rp->status);
 	hci_dev_unlock(hdev);
 }
@@ -1581,7 +1581,14 @@
 	struct discovery_state *discov = &hdev->discovery;
 	struct inquiry_entry *e;
 
-	if (conn && !test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
+	/* Update the mgmt connected state if necessary. Be careful with
+	 * conn objects that exist but are not (yet) connected however.
+	 * Only those in BT_CONFIG or BT_CONNECTED states can be
+	 * considered connected.
+	 */
+	if (conn &&
+	    (conn->state == BT_CONFIG || conn->state == BT_CONNECTED) &&
+	    !test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
 		mgmt_device_connected(hdev, conn, 0, name, name_len);
 
 	if (discov->state == DISCOVERY_STOPPED)
@@ -3184,6 +3191,38 @@
 	hci_dev_unlock(hdev);
 }
 
+static void conn_set_key(struct hci_conn *conn, u8 key_type, u8 pin_len)
+{
+	if (key_type == HCI_LK_CHANGED_COMBINATION)
+		return;
+
+	conn->pin_length = pin_len;
+	conn->key_type = key_type;
+
+	switch (key_type) {
+	case HCI_LK_LOCAL_UNIT:
+	case HCI_LK_REMOTE_UNIT:
+	case HCI_LK_DEBUG_COMBINATION:
+		return;
+	case HCI_LK_COMBINATION:
+		if (pin_len == 16)
+			conn->pending_sec_level = BT_SECURITY_HIGH;
+		else
+			conn->pending_sec_level = BT_SECURITY_MEDIUM;
+		break;
+	case HCI_LK_UNAUTH_COMBINATION_P192:
+	case HCI_LK_UNAUTH_COMBINATION_P256:
+		conn->pending_sec_level = BT_SECURITY_MEDIUM;
+		break;
+	case HCI_LK_AUTH_COMBINATION_P192:
+		conn->pending_sec_level = BT_SECURITY_HIGH;
+		break;
+	case HCI_LK_AUTH_COMBINATION_P256:
+		conn->pending_sec_level = BT_SECURITY_FIPS;
+		break;
+	}
+}
+
 static void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
 {
 	struct hci_ev_link_key_req *ev = (void *) skb->data;
@@ -3225,8 +3264,7 @@
 			goto not_found;
 		}
 
-		conn->key_type = key->type;
-		conn->pin_length = key->pin_len;
+		conn_set_key(conn, key->type, key->pin_len);
 	}
 
 	bacpy(&cp.bdaddr, &ev->bdaddr);
@@ -3259,12 +3297,8 @@
 	if (conn) {
 		hci_conn_hold(conn);
 		conn->disc_timeout = HCI_DISCONN_TIMEOUT;
-		pin_len = conn->pin_length;
-
-		if (ev->key_type != HCI_LK_CHANGED_COMBINATION)
-			conn->key_type = ev->key_type;
-
 		hci_conn_drop(conn);
+		conn_set_key(conn, ev->key_type, conn->pin_length);
 	}
 
 	if (!test_bit(HCI_MGMT, &hdev->dev_flags))
@@ -3275,6 +3309,12 @@
 	if (!key)
 		goto unlock;
 
+	/* Update connection information since adding the key will have
+	 * fixed up the type in the case of changed combination keys.
+	 */
+	if (ev->key_type == HCI_LK_CHANGED_COMBINATION)
+		conn_set_key(conn, key->type, key->pin_len);
+
 	mgmt_new_link_key(hdev, key, persistent);
 
 	/* Keep debug keys around only if the HCI_KEEP_DEBUG_KEYS flag
@@ -3284,8 +3324,8 @@
 	 */
 	if (key->type == HCI_LK_DEBUG_COMBINATION &&
 	    !test_bit(HCI_KEEP_DEBUG_KEYS, &hdev->dev_flags)) {
-		list_del(&key->list);
-		kfree(key);
+		list_del_rcu(&key->list);
+		kfree_rcu(key, rcu);
 	} else if (conn) {
 		if (persistent)
 			clear_bit(HCI_CONN_FLUSH_KEY, &conn->flags);
@@ -3989,11 +4029,9 @@
 
 			bacpy(&cp.bdaddr, &ev->bdaddr);
 			memcpy(cp.hash192, data->hash192, sizeof(cp.hash192));
-			memcpy(cp.randomizer192, data->randomizer192,
-			       sizeof(cp.randomizer192));
+			memcpy(cp.rand192, data->rand192, sizeof(cp.rand192));
 			memcpy(cp.hash256, data->hash256, sizeof(cp.hash256));
-			memcpy(cp.randomizer256, data->randomizer256,
-			       sizeof(cp.randomizer256));
+			memcpy(cp.rand256, data->rand256, sizeof(cp.rand256));
 
 			hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_EXT_DATA_REPLY,
 				     sizeof(cp), &cp);
@@ -4002,8 +4040,7 @@
 
 			bacpy(&cp.bdaddr, &ev->bdaddr);
 			memcpy(cp.hash, data->hash192, sizeof(cp.hash));
-			memcpy(cp.randomizer, data->randomizer192,
-			       sizeof(cp.randomizer));
+			memcpy(cp.rand, data->rand192, sizeof(cp.rand));
 
 			hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_DATA_REPLY,
 				     sizeof(cp), &cp);
@@ -4571,8 +4608,8 @@
 	 */
 	if (ltk->type == SMP_STK) {
 		set_bit(HCI_CONN_STK_ENCRYPT, &conn->flags);
-		list_del(&ltk->list);
-		kfree(ltk);
+		list_del_rcu(&ltk->list);
+		kfree_rcu(ltk, rcu);
 	} else {
 		clear_bit(HCI_CONN_STK_ENCRYPT, &conn->flags);
 	}
diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c
index 1b7d605..cc25d0b 100644
--- a/net/bluetooth/hidp/core.c
+++ b/net/bluetooth/hidp/core.c
@@ -736,14 +736,10 @@
 	struct hid_device *hid;
 	int err;
 
-	session->rd_data = kzalloc(req->rd_size, GFP_KERNEL);
-	if (!session->rd_data)
-		return -ENOMEM;
+	session->rd_data = memdup_user(req->rd_data, req->rd_size);
+	if (IS_ERR(session->rd_data))
+		return PTR_ERR(session->rd_data);
 
-	if (copy_from_user(session->rd_data, req->rd_data, req->rd_size)) {
-		err = -EFAULT;
-		goto fault;
-	}
 	session->rd_size = req->rd_size;
 
 	hid = hid_allocate_device();
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index fc15174..c2d1489 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -424,6 +424,9 @@
 
 	mutex_init(&chan->lock);
 
+	/* Set default lock nesting level */
+	atomic_set(&chan->nesting, L2CAP_NESTING_NORMAL);
+
 	write_lock(&chan_list_lock);
 	list_add(&chan->global_l, &chan_list);
 	write_unlock(&chan_list_lock);
@@ -567,7 +570,8 @@
 
 	__clear_chan_timer(chan);
 
-	BT_DBG("chan %p, conn %p, err %d", chan, conn, err);
+	BT_DBG("chan %p, conn %p, err %d, state %s", chan, conn, err,
+	       state_to_string(chan->state));
 
 	chan->ops->teardown(chan, err);
 
@@ -836,7 +840,10 @@
 	if (!skb)
 		return;
 
-	if (lmp_no_flush_capable(conn->hcon->hdev))
+	/* Use NO_FLUSH if supported or we have an LE link (which does
+	 * not support auto-flushing packets) */
+	if (lmp_no_flush_capable(conn->hcon->hdev) ||
+	    conn->hcon->type == LE_LINK)
 		flags = ACL_START_NO_FLUSH;
 	else
 		flags = ACL_START;
@@ -870,8 +877,13 @@
 		return;
 	}
 
-	if (!test_bit(FLAG_FLUSHABLE, &chan->flags) &&
-	    lmp_no_flush_capable(hcon->hdev))
+	/* Use NO_FLUSH for LE links (where this is the only option) or
+	 * if the BR/EDR link supports it and flushing has not been
+	 * explicitly requested (through FLAG_FLUSHABLE).
+	 */
+	if (hcon->type == LE_LINK ||
+	    (!test_bit(FLAG_FLUSHABLE, &chan->flags) &&
+	     lmp_no_flush_capable(hcon->hdev)))
 		flags = ACL_START_NO_FLUSH;
 	else
 		flags = ACL_START;
@@ -5215,9 +5227,10 @@
 				u8 *data)
 {
 	struct l2cap_le_conn_rsp *rsp = (struct l2cap_le_conn_rsp *) data;
+	struct hci_conn *hcon = conn->hcon;
 	u16 dcid, mtu, mps, credits, result;
 	struct l2cap_chan *chan;
-	int err;
+	int err, sec_level;
 
 	if (cmd_len < sizeof(*rsp))
 		return -EPROTO;
@@ -5256,6 +5269,26 @@
 		l2cap_chan_ready(chan);
 		break;
 
+	case L2CAP_CR_AUTHENTICATION:
+	case L2CAP_CR_ENCRYPTION:
+		/* If we already have MITM protection we can't do
+		 * anything.
+		 */
+		if (hcon->sec_level > BT_SECURITY_MEDIUM) {
+			l2cap_chan_del(chan, ECONNREFUSED);
+			break;
+		}
+
+		sec_level = hcon->sec_level + 1;
+		if (chan->sec_level < sec_level)
+			chan->sec_level = sec_level;
+
+		/* We'll need to send a new Connect Request */
+		clear_bit(FLAG_LE_CONN_REQ_SENT, &chan->flags);
+
+		smp_conn_security(hcon, chan->sec_level);
+		break;
+
 	default:
 		l2cap_chan_del(chan, ECONNREFUSED);
 		break;
@@ -5388,7 +5421,8 @@
 	mutex_lock(&conn->chan_lock);
 	l2cap_chan_lock(pchan);
 
-	if (!smp_sufficient_security(conn->hcon, pchan->sec_level)) {
+	if (!smp_sufficient_security(conn->hcon, pchan->sec_level,
+				     SMP_ALLOW_STK)) {
 		result = L2CAP_CR_AUTHENTICATION;
 		chan = NULL;
 		goto response_unlock;
@@ -7329,7 +7363,8 @@
 				l2cap_start_connection(chan);
 			else
 				__set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
-		} else if (chan->state == BT_CONNECT2) {
+		} else if (chan->state == BT_CONNECT2 &&
+			   chan->mode != L2CAP_MODE_LE_FLOWCTL) {
 			struct l2cap_conn_rsp rsp;
 			__u16 res, stat;
 
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index 31f106e..b0efb72 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -285,6 +285,12 @@
 	sk->sk_max_ack_backlog = backlog;
 	sk->sk_ack_backlog = 0;
 
+	/* Listening channels need to use nested locking in order not to
+	 * cause lockdep warnings when the created child channels end up
+	 * being locked in the same thread as the parent channel.
+	 */
+	atomic_set(&chan->nesting, L2CAP_NESTING_PARENT);
+
 	chan->state = BT_LISTEN;
 	sk->sk_state = BT_LISTEN;
 
@@ -301,7 +307,7 @@
 	long timeo;
 	int err = 0;
 
-	lock_sock_nested(sk, SINGLE_DEPTH_NESTING);
+	lock_sock_nested(sk, L2CAP_NESTING_PARENT);
 
 	timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);
 
@@ -333,7 +339,7 @@
 
 		release_sock(sk);
 		timeo = schedule_timeout(timeo);
-		lock_sock_nested(sk, SINGLE_DEPTH_NESTING);
+		lock_sock_nested(sk, L2CAP_NESTING_PARENT);
 	}
 	__set_current_state(TASK_RUNNING);
 	remove_wait_queue(sk_sleep(sk), &wait);
@@ -1096,6 +1102,8 @@
 	chan = l2cap_pi(sk)->chan;
 	conn = chan->conn;
 
+	BT_DBG("chan %p state %s", chan, state_to_string(chan->state));
+
 	if (conn)
 		mutex_lock(&conn->chan_lock);
 
@@ -1153,12 +1161,16 @@
 {
 	struct sock *sk;
 
-	BT_DBG("parent %p", parent);
+	BT_DBG("parent %p state %s", parent,
+	       state_to_string(parent->sk_state));
 
 	/* Close not yet accepted channels */
 	while ((sk = bt_accept_dequeue(parent, NULL))) {
 		struct l2cap_chan *chan = l2cap_pi(sk)->chan;
 
+		BT_DBG("child chan %p state %s", chan,
+		       state_to_string(chan->state));
+
 		l2cap_chan_lock(chan);
 		__clear_chan_timer(chan);
 		l2cap_chan_close(chan, ECONNRESET);
@@ -1246,7 +1258,16 @@
 	struct sock *sk = chan->data;
 	struct sock *parent;
 
-	lock_sock(sk);
+	BT_DBG("chan %p state %s", chan, state_to_string(chan->state));
+
+	/* This callback can be called both for server (BT_LISTEN)
+	 * sockets as well as "normal" ones. To avoid lockdep warnings
+	 * with child socket locking (through l2cap_sock_cleanup_listen)
+	 * we need separation into separate nesting levels. The simplest
+	 * way to accomplish this is to inherit the nesting level used
+	 * for the channel.
+	 */
+	lock_sock_nested(sk, atomic_read(&chan->nesting));
 
 	parent = bt_sk(sk)->parent;
 
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index ce0272c..cbeef5f 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -3589,8 +3589,16 @@
 		struct mgmt_cp_add_remote_oob_data *cp = data;
 		u8 status;
 
+		if (cp->addr.type != BDADDR_BREDR) {
+			err = cmd_complete(sk, hdev->id,
+					   MGMT_OP_ADD_REMOTE_OOB_DATA,
+					   MGMT_STATUS_INVALID_PARAMS,
+					   &cp->addr, sizeof(cp->addr));
+			goto unlock;
+		}
+
 		err = hci_add_remote_oob_data(hdev, &cp->addr.bdaddr,
-					      cp->hash, cp->randomizer);
+					      cp->hash, cp->rand);
 		if (err < 0)
 			status = MGMT_STATUS_FAILED;
 		else
@@ -3602,11 +3610,17 @@
 		struct mgmt_cp_add_remote_oob_ext_data *cp = data;
 		u8 status;
 
+		if (cp->addr.type != BDADDR_BREDR) {
+			err = cmd_complete(sk, hdev->id,
+					   MGMT_OP_ADD_REMOTE_OOB_DATA,
+					   MGMT_STATUS_INVALID_PARAMS,
+					   &cp->addr, sizeof(cp->addr));
+			goto unlock;
+		}
+
 		err = hci_add_remote_oob_ext_data(hdev, &cp->addr.bdaddr,
-						  cp->hash192,
-						  cp->randomizer192,
-						  cp->hash256,
-						  cp->randomizer256);
+						  cp->hash192, cp->rand192,
+						  cp->hash256, cp->rand256);
 		if (err < 0)
 			status = MGMT_STATUS_FAILED;
 		else
@@ -3620,6 +3634,7 @@
 				 MGMT_STATUS_INVALID_PARAMS);
 	}
 
+unlock:
 	hci_dev_unlock(hdev);
 	return err;
 }
@@ -3633,14 +3648,26 @@
 
 	BT_DBG("%s", hdev->name);
 
+	if (cp->addr.type != BDADDR_BREDR)
+		return cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
+				    MGMT_STATUS_INVALID_PARAMS,
+				    &cp->addr, sizeof(cp->addr));
+
 	hci_dev_lock(hdev);
 
+	if (!bacmp(&cp->addr.bdaddr, BDADDR_ANY)) {
+		hci_remote_oob_data_clear(hdev);
+		status = MGMT_STATUS_SUCCESS;
+		goto done;
+	}
+
 	err = hci_remove_remote_oob_data(hdev, &cp->addr.bdaddr);
 	if (err < 0)
 		status = MGMT_STATUS_INVALID_PARAMS;
 	else
 		status = MGMT_STATUS_SUCCESS;
 
+done:
 	err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
 			   status, &cp->addr, sizeof(cp->addr));
 
@@ -6742,8 +6769,8 @@
 }
 
 void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192,
-				       u8 *randomizer192, u8 *hash256,
-				       u8 *randomizer256, u8 status)
+				       u8 *rand192, u8 *hash256, u8 *rand256,
+				       u8 status)
 {
 	struct pending_cmd *cmd;
 
@@ -6758,16 +6785,14 @@
 			   mgmt_status(status));
 	} else {
 		if (test_bit(HCI_SC_ENABLED, &hdev->dev_flags) &&
-		    hash256 && randomizer256) {
+		    hash256 && rand256) {
 			struct mgmt_rp_read_local_oob_ext_data rp;
 
 			memcpy(rp.hash192, hash192, sizeof(rp.hash192));
-			memcpy(rp.randomizer192, randomizer192,
-			       sizeof(rp.randomizer192));
+			memcpy(rp.rand192, rand192, sizeof(rp.rand192));
 
 			memcpy(rp.hash256, hash256, sizeof(rp.hash256));
-			memcpy(rp.randomizer256, randomizer256,
-			       sizeof(rp.randomizer256));
+			memcpy(rp.rand256, rand256, sizeof(rp.rand256));
 
 			cmd_complete(cmd->sk, hdev->id,
 				     MGMT_OP_READ_LOCAL_OOB_DATA, 0,
@@ -6776,8 +6801,7 @@
 			struct mgmt_rp_read_local_oob_data rp;
 
 			memcpy(rp.hash, hash192, sizeof(rp.hash));
-			memcpy(rp.randomizer, randomizer192,
-			       sizeof(rp.randomizer));
+			memcpy(rp.rand, rand192, sizeof(rp.rand));
 
 			cmd_complete(cmd->sk, hdev->id,
 				     MGMT_OP_READ_LOCAL_OOB_DATA, 0,
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index 3ebf65b..069b76e 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -383,18 +383,18 @@
 	/* If pairing failed clean up any keys we might have */
 	if (!complete) {
 		if (smp->ltk) {
-			list_del(&smp->ltk->list);
-			kfree(smp->ltk);
+			list_del_rcu(&smp->ltk->list);
+			kfree_rcu(smp->ltk, rcu);
 		}
 
 		if (smp->slave_ltk) {
-			list_del(&smp->slave_ltk->list);
-			kfree(smp->slave_ltk);
+			list_del_rcu(&smp->slave_ltk->list);
+			kfree_rcu(smp->slave_ltk, rcu);
 		}
 
 		if (smp->remote_irk) {
-			list_del(&smp->remote_irk->list);
-			kfree(smp->remote_irk);
+			list_del_rcu(&smp->remote_irk->list);
+			kfree_rcu(smp->remote_irk, rcu);
 		}
 	}
 
@@ -514,8 +514,6 @@
 		set_bit(SMP_FLAG_TK_VALID, &smp->flags);
 	}
 
-	hci_dev_lock(hcon->hdev);
-
 	if (method == REQ_PASSKEY)
 		ret = mgmt_user_passkey_request(hcon->hdev, &hcon->dst,
 						hcon->type, hcon->dst_type);
@@ -528,8 +526,6 @@
 						hcon->type, hcon->dst_type,
 						passkey, 0);
 
-	hci_dev_unlock(hcon->hdev);
-
 	return ret;
 }
 
@@ -659,8 +655,8 @@
 		 * just remove it.
 		 */
 		if (!bacmp(&smp->remote_irk->rpa, BDADDR_ANY)) {
-			list_del(&smp->remote_irk->list);
-			kfree(smp->remote_irk);
+			list_del_rcu(&smp->remote_irk->list);
+			kfree_rcu(smp->remote_irk, rcu);
 			smp->remote_irk = NULL;
 		}
 	}
@@ -1126,18 +1122,20 @@
 	return true;
 }
 
-bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level)
+bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level,
+			     enum smp_key_pref key_pref)
 {
 	if (sec_level == BT_SECURITY_LOW)
 		return true;
 
-	/* If we're encrypted with an STK always claim insufficient
-	 * security. This way we allow the connection to be re-encrypted
-	 * with an LTK, even if the LTK provides the same level of
-	 * security. Only exception is if we don't have an LTK (e.g.
-	 * because of key distribution bits).
+	/* If we're encrypted with an STK but the caller prefers using
+	 * LTK claim insufficient security. This way we allow the
+	 * connection to be re-encrypted with an LTK, even if the LTK
+	 * provides the same level of security. Only exception is if we
+	 * don't have an LTK (e.g. because of key distribution bits).
 	 */
-	if (test_bit(HCI_CONN_STK_ENCRYPT, &hcon->flags) &&
+	if (key_pref == SMP_USE_LTK &&
+	    test_bit(HCI_CONN_STK_ENCRYPT, &hcon->flags) &&
 	    hci_find_ltk_by_addr(hcon->hdev, &hcon->dst, hcon->dst_type,
 				 hcon->role))
 		return false;
@@ -1171,7 +1169,7 @@
 	else
 		sec_level = authreq_to_seclevel(auth);
 
-	if (smp_sufficient_security(hcon, sec_level))
+	if (smp_sufficient_security(hcon, sec_level, SMP_USE_LTK))
 		return 0;
 
 	if (sec_level > hcon->pending_sec_level)
@@ -1221,7 +1219,7 @@
 	if (!test_bit(HCI_LE_ENABLED, &hcon->hdev->dev_flags))
 		return 1;
 
-	if (smp_sufficient_security(hcon, sec_level))
+	if (smp_sufficient_security(hcon, sec_level, SMP_USE_LTK))
 		return 1;
 
 	if (sec_level > hcon->pending_sec_level)
@@ -1323,7 +1321,6 @@
 
 	skb_pull(skb, sizeof(*rp));
 
-	hci_dev_lock(hdev);
 	authenticated = (hcon->sec_level == BT_SECURITY_HIGH);
 	ltk = hci_add_ltk(hdev, &hcon->dst, hcon->dst_type, SMP_LTK,
 			  authenticated, smp->tk, smp->enc_key_size,
@@ -1331,7 +1328,6 @@
 	smp->ltk = ltk;
 	if (!(smp->remote_key_dist & KEY_DIST_MASK))
 		smp_distribute_keys(smp);
-	hci_dev_unlock(hdev);
 
 	return 0;
 }
@@ -1378,8 +1374,6 @@
 
 	skb_pull(skb, sizeof(*info));
 
-	hci_dev_lock(hcon->hdev);
-
 	/* Strictly speaking the Core Specification (4.1) allows sending
 	 * an empty address which would force us to rely on just the IRK
 	 * as "identity information". However, since such
@@ -1407,8 +1401,6 @@
 	if (!(smp->remote_key_dist & KEY_DIST_MASK))
 		smp_distribute_keys(smp);
 
-	hci_dev_unlock(hcon->hdev);
-
 	return 0;
 }
 
@@ -1417,7 +1409,6 @@
 	struct smp_cmd_sign_info *rp = (void *) skb->data;
 	struct l2cap_chan *chan = conn->smp;
 	struct smp_chan *smp = chan->data;
-	struct hci_dev *hdev = conn->hcon->hdev;
 	struct smp_csrk *csrk;
 
 	BT_DBG("conn %p", conn);
@@ -1430,7 +1421,6 @@
 
 	skb_pull(skb, sizeof(*rp));
 
-	hci_dev_lock(hdev);
 	csrk = kzalloc(sizeof(*csrk), GFP_KERNEL);
 	if (csrk) {
 		csrk->master = 0x01;
@@ -1438,7 +1428,6 @@
 	}
 	smp->csrk = csrk;
 	smp_distribute_keys(smp);
-	hci_dev_unlock(hdev);
 
 	return 0;
 }
@@ -1662,6 +1651,13 @@
 	chan->omtu	= pchan->omtu;
 	chan->mode	= pchan->mode;
 
+	/* Other L2CAP channels may request SMP routines in order to
+	 * change the security level. This means that the SMP channel
+	 * lock must be considered in its own category to avoid lockdep
+	 * warnings.
+	 */
+	atomic_set(&chan->nesting, L2CAP_NESTING_SMP);
+
 	BT_DBG("created chan %p", chan);
 
 	return chan;
@@ -1693,7 +1689,7 @@
 
 	BT_DBG("%s", hdev->name);
 
-	tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC);
+	tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0, 0);
 	if (IS_ERR(tfm_aes)) {
 		int err = PTR_ERR(tfm_aes);
 		BT_ERR("Unable to create crypto context");
@@ -1719,6 +1715,9 @@
 	chan->imtu = L2CAP_DEFAULT_MTU;
 	chan->ops = &smp_root_chan_ops;
 
+	/* Set correct nesting level for a parent/listening channel */
+	atomic_set(&chan->nesting, L2CAP_NESTING_PARENT);
+
 	hdev->smp_data = chan;
 
 	return 0;
diff --git a/net/bluetooth/smp.h b/net/bluetooth/smp.h
index 86a683a..f76083b 100644
--- a/net/bluetooth/smp.h
+++ b/net/bluetooth/smp.h
@@ -133,8 +133,15 @@
 	return BT_SECURITY_MEDIUM;
 }
 
+/* Key preferences for smp_sufficient security */
+enum smp_key_pref {
+	SMP_ALLOW_STK,
+	SMP_USE_LTK,
+};
+
 /* SMP Commands */
-bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level);
+bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level,
+			     enum smp_key_pref key_pref);
 int smp_conn_security(struct hci_conn *hcon, __u8 sec_level);
 int smp_user_confirm_reply(struct hci_conn *conn, u16 mgmt_op, __le32 passkey);
 
diff --git a/net/ieee802154/6lowpan_rtnl.c b/net/ieee802154/6lowpan_rtnl.c
index 290e14f..16f5e91 100644
--- a/net/ieee802154/6lowpan_rtnl.c
+++ b/net/ieee802154/6lowpan_rtnl.c
@@ -597,7 +597,7 @@
 
 	entry->ldev = dev;
 
-	/* Set the lowpan harware address to the wpan hardware address. */
+	/* Set the lowpan hardware address to the wpan hardware address. */
 	memcpy(dev->dev_addr, real_dev->dev_addr, IEEE802154_ADDR_LEN);
 
 	mutex_lock(&lowpan_dev_info(dev)->dev_list_mtx);
diff --git a/net/ieee802154/Makefile b/net/ieee802154/Makefile
index 38354d4..9f6970f 100644
--- a/net/ieee802154/Makefile
+++ b/net/ieee802154/Makefile
@@ -3,7 +3,7 @@
 
 ieee802154_6lowpan-y := 6lowpan_rtnl.o reassembly.o
 ieee802154-y := netlink.o nl-mac.o nl-phy.o nl_policy.o core.o \
-                header_ops.o sysfs.o
+                header_ops.o sysfs.o nl802154.o
 af_802154-y := af_ieee802154.o raw.o dgram.o
 
 ccflags-y += -D__CHECK_ENDIAN__
diff --git a/net/ieee802154/core.c b/net/ieee802154/core.c
index d1cd0ed..18bc7e7 100644
--- a/net/ieee802154/core.c
+++ b/net/ieee802154/core.c
@@ -18,11 +18,17 @@
 #include <linux/device.h>
 
 #include <net/cfg802154.h>
+#include <net/rtnetlink.h>
 
 #include "ieee802154.h"
+#include "nl802154.h"
 #include "sysfs.h"
 #include "core.h"
 
+/* RCU-protected (and RTNL for writers) */
+LIST_HEAD(cfg802154_rdev_list);
+int cfg802154_rdev_list_generation;
+
 static int wpan_phy_match(struct device *dev, const void *data)
 {
 	return !strcmp(dev_name(dev), (const char *)data);
@@ -69,8 +75,25 @@
 }
 EXPORT_SYMBOL(wpan_phy_for_each);
 
+struct cfg802154_registered_device *
+cfg802154_rdev_by_wpan_phy_idx(int wpan_phy_idx)
+{
+	struct cfg802154_registered_device *result = NULL, *rdev;
+
+	ASSERT_RTNL();
+
+	list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
+		if (rdev->wpan_phy_idx == wpan_phy_idx) {
+			result = rdev;
+			break;
+		}
+	}
+
+	return result;
+}
+
 struct wpan_phy *
-wpan_phy_alloc(const struct cfg802154_ops *ops, size_t priv_size)
+wpan_phy_new(const struct cfg802154_ops *ops, size_t priv_size)
 {
 	static atomic_t wpan_phy_counter = ATOMIC_INIT(0);
 	struct cfg802154_registered_device *rdev;
@@ -97,25 +120,71 @@
 
 	mutex_init(&rdev->wpan_phy.pib_lock);
 
+	INIT_LIST_HEAD(&rdev->wpan_dev_list);
 	device_initialize(&rdev->wpan_phy.dev);
 	dev_set_name(&rdev->wpan_phy.dev, "wpan-phy%d", rdev->wpan_phy_idx);
 
 	rdev->wpan_phy.dev.class = &wpan_phy_class;
 	rdev->wpan_phy.dev.platform_data = rdev;
 
+	init_waitqueue_head(&rdev->dev_wait);
+
 	return &rdev->wpan_phy;
 }
-EXPORT_SYMBOL(wpan_phy_alloc);
+EXPORT_SYMBOL(wpan_phy_new);
 
 int wpan_phy_register(struct wpan_phy *phy)
 {
-	return device_add(&phy->dev);
+	struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(phy);
+	int ret;
+
+	rtnl_lock();
+	ret = device_add(&phy->dev);
+	if (ret) {
+		rtnl_unlock();
+		return ret;
+	}
+
+	list_add_rcu(&rdev->list, &cfg802154_rdev_list);
+	cfg802154_rdev_list_generation++;
+
+	/* TODO phy registered lock */
+	rtnl_unlock();
+
+	/* TODO nl802154 phy notify */
+
+	return 0;
 }
 EXPORT_SYMBOL(wpan_phy_register);
 
 void wpan_phy_unregister(struct wpan_phy *phy)
 {
+	struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(phy);
+
+	wait_event(rdev->dev_wait, ({
+		int __count;
+		rtnl_lock();
+		__count = rdev->opencount;
+		rtnl_unlock();
+		__count == 0; }));
+
+	rtnl_lock();
+	/* TODO nl802154 phy notify */
+	/* TODO phy registered lock */
+
+	WARN_ON(!list_empty(&rdev->wpan_dev_list));
+
+	/* First remove the hardware from everywhere, this makes
+	 * it impossible to find from userspace.
+	 */
+	list_del_rcu(&rdev->list);
+	synchronize_rcu();
+
+	cfg802154_rdev_list_generation++;
+
 	device_del(&phy->dev);
+
+	rtnl_unlock();
 }
 EXPORT_SYMBOL(wpan_phy_unregister);
 
@@ -130,6 +199,79 @@
 	kfree(rdev);
 }
 
+static void
+cfg802154_update_iface_num(struct cfg802154_registered_device *rdev,
+			   int iftype, int num)
+{
+	ASSERT_RTNL();
+
+	rdev->num_running_ifaces += num;
+}
+
+static int cfg802154_netdev_notifier_call(struct notifier_block *nb,
+					  unsigned long state, void *ptr)
+{
+	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
+	struct cfg802154_registered_device *rdev;
+
+	if (!wpan_dev)
+		return NOTIFY_DONE;
+
+	rdev = wpan_phy_to_rdev(wpan_dev->wpan_phy);
+
+	/* TODO WARN_ON unspec type */
+
+	switch (state) {
+		/* TODO NETDEV_DEVTYPE */
+	case NETDEV_REGISTER:
+		wpan_dev->identifier = ++rdev->wpan_dev_id;
+		list_add_rcu(&wpan_dev->list, &rdev->wpan_dev_list);
+		rdev->devlist_generation++;
+
+		wpan_dev->netdev = dev;
+		break;
+	case NETDEV_DOWN:
+		cfg802154_update_iface_num(rdev, wpan_dev->iftype, -1);
+
+		rdev->opencount--;
+		wake_up(&rdev->dev_wait);
+		break;
+	case NETDEV_UP:
+		cfg802154_update_iface_num(rdev, wpan_dev->iftype, 1);
+
+		rdev->opencount++;
+		break;
+	case NETDEV_UNREGISTER:
+		/* It is possible to get NETDEV_UNREGISTER
+		 * multiple times. To detect that, check
+		 * that the interface is still on the list
+		 * of registered interfaces, and only then
+		 * remove and clean it up.
+		 */
+		if (!list_empty(&wpan_dev->list)) {
+			list_del_rcu(&wpan_dev->list);
+			rdev->devlist_generation++;
+		}
+		/* synchronize (so that we won't find this netdev
+		 * from other code any more) and then clear the list
+		 * head so that the above code can safely check for
+		 * !list_empty() to avoid double-cleanup.
+		 */
+		synchronize_rcu();
+		INIT_LIST_HEAD(&wpan_dev->list);
+		break;
+	default:
+		return NOTIFY_DONE;
+	}
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block cfg802154_netdev_notifier = {
+	.notifier_call = cfg802154_netdev_notifier_call,
+};
+
 static int __init wpan_phy_class_init(void)
 {
 	int rc;
@@ -138,11 +280,25 @@
 	if (rc)
 		goto err;
 
-	rc = ieee802154_nl_init();
+	rc = register_netdevice_notifier(&cfg802154_netdev_notifier);
 	if (rc)
 		goto err_nl;
 
+	rc = ieee802154_nl_init();
+	if (rc)
+		goto err_notifier;
+
+	rc = nl802154_init();
+	if (rc)
+		goto err_ieee802154_nl;
+
 	return 0;
+
+err_ieee802154_nl:
+	ieee802154_nl_exit();
+
+err_notifier:
+	unregister_netdevice_notifier(&cfg802154_netdev_notifier);
 err_nl:
 	wpan_phy_sysfs_exit();
 err:
@@ -152,7 +308,9 @@
 
 static void __exit wpan_phy_class_exit(void)
 {
+	nl802154_exit();
 	ieee802154_nl_exit();
+	unregister_netdevice_notifier(&cfg802154_netdev_notifier);
 	wpan_phy_sysfs_exit();
 }
 module_exit(wpan_phy_class_exit);
diff --git a/net/ieee802154/core.h b/net/ieee802154/core.h
index fea60b3..f3e9558 100644
--- a/net/ieee802154/core.h
+++ b/net/ieee802154/core.h
@@ -5,10 +5,22 @@
 
 struct cfg802154_registered_device {
 	const struct cfg802154_ops *ops;
+	struct list_head list;
 
 	/* wpan_phy index, internal only */
 	int wpan_phy_idx;
 
+	/* also protected by devlist_mtx */
+	int opencount;
+	wait_queue_head_t dev_wait;
+
+	/* protected by RTNL only */
+	int num_running_ifaces;
+
+	/* associated wpan interfaces, protected by rtnl or RCU */
+	struct list_head wpan_dev_list;
+	int devlist_generation, wpan_dev_id;
+
 	/* must be last because of the way we do wpan_phy_priv(),
 	 * and it should at least be aligned to NETDEV_ALIGN
 	 */
@@ -23,7 +35,12 @@
 			    wpan_phy);
 }
 
+extern struct list_head cfg802154_rdev_list;
+extern int cfg802154_rdev_list_generation;
+
 /* free object */
 void cfg802154_dev_free(struct cfg802154_registered_device *rdev);
+struct cfg802154_registered_device *
+cfg802154_rdev_by_wpan_phy_idx(int wpan_phy_idx);
 
 #endif /* __IEEE802154_CORE_H */
diff --git a/net/ieee802154/ieee802154.h b/net/ieee802154/ieee802154.h
index 42ae63a..a5d7515 100644
--- a/net/ieee802154/ieee802154.h
+++ b/net/ieee802154/ieee802154.h
@@ -15,7 +15,7 @@
 #define IEEE_802154_LOCAL_H
 
 int __init ieee802154_nl_init(void);
-void __exit ieee802154_nl_exit(void);
+void ieee802154_nl_exit(void);
 
 #define IEEE802154_OP(_cmd, _func)			\
 	{						\
diff --git a/net/ieee802154/netlink.c b/net/ieee802154/netlink.c
index 6c3c2595..6ad0da3 100644
--- a/net/ieee802154/netlink.c
+++ b/net/ieee802154/netlink.c
@@ -1,5 +1,5 @@
 /*
- * Netlink inteface for IEEE 802.15.4 stack
+ * Netlink interface for IEEE 802.15.4 stack
  *
  * Copyright 2007, 2008 Siemens AG
  *
@@ -155,7 +155,7 @@
 						    ieee802154_mcgrps);
 }
 
-void __exit ieee802154_nl_exit(void)
+void ieee802154_nl_exit(void)
 {
 	genl_unregister_family(&nl802154_family);
 }
diff --git a/net/ieee802154/nl-mac.c b/net/ieee802154/nl-mac.c
index 7127b9d..1f085ed 100644
--- a/net/ieee802154/nl-mac.c
+++ b/net/ieee802154/nl-mac.c
@@ -1,5 +1,5 @@
 /*
- * Netlink inteface for IEEE 802.15.4 stack
+ * Netlink interface for IEEE 802.15.4 stack
  *
  * Copyright 2007, 2008 Siemens AG
  *
@@ -113,7 +113,9 @@
 	if (ops->get_mac_params) {
 		struct ieee802154_mac_params params;
 
+		rtnl_lock();
 		ops->get_mac_params(dev, &params);
+		rtnl_unlock();
 
 		if (nla_put_s8(msg, IEEE802154_ATTR_TXPOWER,
 			       params.transmit_power) ||
@@ -164,7 +166,10 @@
 	if (!dev)
 		return NULL;
 
-	if (dev->type != ARPHRD_IEEE802154) {
+	/* Check on mtu is currently a hacked solution because lowpan
+	 * and wpan have the same ARPHRD type.
+	 */
+	if (dev->type != ARPHRD_IEEE802154 || dev->mtu != IEEE802154_MTU) {
 		dev_put(dev);
 		return NULL;
 	}
@@ -348,8 +353,10 @@
 		return -EINVAL;
 	}
 
+	rtnl_lock();
 	ret = ieee802154_mlme_ops(dev)->start_req(dev, &addr, channel, page,
 		bcn_ord, sf_ord, pan_coord, blx, coord_realign);
+	rtnl_unlock();
 
 	/* FIXME: add validation for unused parameters to be sane
 	 * for SoftMAC
@@ -444,7 +451,11 @@
 
 	idx = 0;
 	for_each_netdev(net, dev) {
-		if (idx < s_idx || (dev->type != ARPHRD_IEEE802154))
+		/* Check on mtu is currently a hacked solution because lowpan
+		 * and wpan have the same ARPHRD type.
+		 */
+		if (idx < s_idx || dev->type != ARPHRD_IEEE802154 ||
+		    dev->mtu != IEEE802154_MTU)
 			goto cont;
 
 		if (ieee802154_nl_fill_iface(skb, NETLINK_CB(cb->skb).portid,
@@ -497,6 +508,7 @@
 	phy = dev->ieee802154_ptr->wpan_phy;
 	get_device(&phy->dev);
 
+	rtnl_lock();
 	ops->get_mac_params(dev, &params);
 
 	if (info->attrs[IEEE802154_ATTR_TXPOWER])
@@ -524,6 +536,7 @@
 		params.frame_retries = nla_get_s8(info->attrs[IEEE802154_ATTR_FRAME_RETRIES]);
 
 	rc = ops->set_mac_params(dev, &params);
+	rtnl_unlock();
 
 	wpan_phy_put(phy);
 	dev_put(dev);
@@ -776,7 +789,11 @@
 	int rc;
 
 	for_each_netdev(net, dev) {
-		if (idx < first_dev || dev->type != ARPHRD_IEEE802154)
+		/* Check on mtu is currently a hacked solution because lowpan
+		 * and wpan have the same ARPHRD type.
+		 */
+		if (idx < first_dev || dev->type != ARPHRD_IEEE802154 ||
+		    dev->mtu != IEEE802154_MTU)
 			goto skip;
 
 		data.ops = ieee802154_mlme_ops(dev);
diff --git a/net/ieee802154/nl-phy.c b/net/ieee802154/nl-phy.c
index 80a946d..6246354 100644
--- a/net/ieee802154/nl-phy.c
+++ b/net/ieee802154/nl-phy.c
@@ -1,5 +1,5 @@
 /*
- * Netlink inteface for IEEE 802.15.4 stack
+ * Netlink interface for IEEE 802.15.4 stack
  *
  * Copyright 2007, 2008 Siemens AG
  *
diff --git a/net/ieee802154/nl802154.c b/net/ieee802154/nl802154.c
new file mode 100644
index 0000000..8896477
--- /dev/null
+++ b/net/ieee802154/nl802154.c
@@ -0,0 +1,957 @@
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Authors:
+ * Alexander Aring <aar@pengutronix.de>
+ *
+ * Based on: net/wireless/nl80211.c
+ */
+
+#include <linux/rtnetlink.h>
+
+#include <net/cfg802154.h>
+#include <net/genetlink.h>
+#include <net/mac802154.h>
+#include <net/netlink.h>
+#include <net/nl802154.h>
+#include <net/sock.h>
+
+#include "nl802154.h"
+#include "rdev-ops.h"
+#include "core.h"
+
+static int nl802154_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
+			     struct genl_info *info);
+
+static void nl802154_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
+			       struct genl_info *info);
+
+/* the netlink family */
+static struct genl_family nl802154_fam = {
+	.id = GENL_ID_GENERATE,		/* don't bother with a hardcoded ID */
+	.name = NL802154_GENL_NAME,	/* have users key off the name instead */
+	.hdrsize = 0,			/* no private header */
+	.version = 1,			/* no particular meaning now */
+	.maxattr = NL802154_ATTR_MAX,
+	.netnsok = true,
+	.pre_doit = nl802154_pre_doit,
+	.post_doit = nl802154_post_doit,
+};
+
+/* multicast groups */
+enum nl802154_multicast_groups {
+	NL802154_MCGRP_CONFIG,
+};
+
+static const struct genl_multicast_group nl802154_mcgrps[] = {
+	[NL802154_MCGRP_CONFIG] = { .name = "config", },
+};
+
+/* returns ERR_PTR values */
+static struct wpan_dev *
+__cfg802154_wpan_dev_from_attrs(struct net *netns, struct nlattr **attrs)
+{
+	struct cfg802154_registered_device *rdev;
+	struct wpan_dev *result = NULL;
+	bool have_ifidx = attrs[NL802154_ATTR_IFINDEX];
+	bool have_wpan_dev_id = attrs[NL802154_ATTR_WPAN_DEV];
+	u64 wpan_dev_id;
+	int wpan_phy_idx = -1;
+	int ifidx = -1;
+
+	ASSERT_RTNL();
+
+	if (!have_ifidx && !have_wpan_dev_id)
+		return ERR_PTR(-EINVAL);
+
+	if (have_ifidx)
+		ifidx = nla_get_u32(attrs[NL802154_ATTR_IFINDEX]);
+	if (have_wpan_dev_id) {
+		wpan_dev_id = nla_get_u64(attrs[NL802154_ATTR_WPAN_DEV]);
+		wpan_phy_idx = wpan_dev_id >> 32;
+	}
+
+	list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
+		struct wpan_dev *wpan_dev;
+
+		/* TODO netns compare */
+
+		if (have_wpan_dev_id && rdev->wpan_phy_idx != wpan_phy_idx)
+			continue;
+
+		list_for_each_entry(wpan_dev, &rdev->wpan_dev_list, list) {
+			if (have_ifidx && wpan_dev->netdev &&
+			    wpan_dev->netdev->ifindex == ifidx) {
+				result = wpan_dev;
+				break;
+			}
+			if (have_wpan_dev_id &&
+			    wpan_dev->identifier == (u32)wpan_dev_id) {
+				result = wpan_dev;
+				break;
+			}
+		}
+
+		if (result)
+			break;
+	}
+
+	if (result)
+		return result;
+
+	return ERR_PTR(-ENODEV);
+}
+
+static struct cfg802154_registered_device *
+__cfg802154_rdev_from_attrs(struct net *netns, struct nlattr **attrs)
+{
+	struct cfg802154_registered_device *rdev = NULL, *tmp;
+	struct net_device *netdev;
+
+	ASSERT_RTNL();
+
+	if (!attrs[NL802154_ATTR_WPAN_PHY] &&
+	    !attrs[NL802154_ATTR_IFINDEX] &&
+	    !attrs[NL802154_ATTR_WPAN_DEV])
+		return ERR_PTR(-EINVAL);
+
+	if (attrs[NL802154_ATTR_WPAN_PHY])
+		rdev = cfg802154_rdev_by_wpan_phy_idx(
+				nla_get_u32(attrs[NL802154_ATTR_WPAN_PHY]));
+
+	if (attrs[NL802154_ATTR_WPAN_DEV]) {
+		u64 wpan_dev_id = nla_get_u64(attrs[NL802154_ATTR_WPAN_DEV]);
+		struct wpan_dev *wpan_dev;
+		bool found = false;
+
+		tmp = cfg802154_rdev_by_wpan_phy_idx(wpan_dev_id >> 32);
+		if (tmp) {
+			/* make sure wpan_dev exists */
+			list_for_each_entry(wpan_dev, &tmp->wpan_dev_list, list) {
+				if (wpan_dev->identifier != (u32)wpan_dev_id)
+					continue;
+				found = true;
+				break;
+			}
+
+			if (!found)
+				tmp = NULL;
+
+			if (rdev && tmp != rdev)
+				return ERR_PTR(-EINVAL);
+			rdev = tmp;
+		}
+	}
+
+	if (attrs[NL802154_ATTR_IFINDEX]) {
+		int ifindex = nla_get_u32(attrs[NL802154_ATTR_IFINDEX]);
+
+		netdev = __dev_get_by_index(netns, ifindex);
+		if (netdev) {
+			if (netdev->ieee802154_ptr)
+				tmp = wpan_phy_to_rdev(
+						netdev->ieee802154_ptr->wpan_phy);
+			else
+				tmp = NULL;
+
+			/* not wireless device -- return error */
+			if (!tmp)
+				return ERR_PTR(-EINVAL);
+
+			/* mismatch -- return error */
+			if (rdev && tmp != rdev)
+				return ERR_PTR(-EINVAL);
+
+			rdev = tmp;
+		}
+	}
+
+	if (!rdev)
+		return ERR_PTR(-ENODEV);
+
+	/* TODO netns compare */
+
+	return rdev;
+}
+
+/* This function returns a pointer to the driver
+ * that the genl_info item that is passed refers to.
+ *
+ * The result of this can be a PTR_ERR and hence must
+ * be checked with IS_ERR() for errors.
+ */
+static struct cfg802154_registered_device *
+cfg802154_get_dev_from_info(struct net *netns, struct genl_info *info)
+{
+	return __cfg802154_rdev_from_attrs(netns, info->attrs);
+}
+
+/* policy for the attributes */
+static const struct nla_policy nl802154_policy[NL802154_ATTR_MAX+1] = {
+	[NL802154_ATTR_WPAN_PHY] = { .type = NLA_U32 },
+	[NL802154_ATTR_WPAN_PHY_NAME] = { .type = NLA_NUL_STRING,
+					  .len = 20-1 },
+
+	[NL802154_ATTR_IFINDEX] = { .type = NLA_U32 },
+	[NL802154_ATTR_IFTYPE] = { .type = NLA_U32 },
+	[NL802154_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
+
+	[NL802154_ATTR_WPAN_DEV] = { .type = NLA_U64 },
+
+	[NL802154_ATTR_PAGE] = { .type = NLA_U8, },
+	[NL802154_ATTR_CHANNEL] = { .type = NLA_U8, },
+
+	[NL802154_ATTR_TX_POWER] = { .type = NLA_S8, },
+
+	[NL802154_ATTR_CCA_MODE] = { .type = NLA_U8, },
+
+	[NL802154_ATTR_SUPPORTED_CHANNEL] = { .type = NLA_U32, },
+
+	[NL802154_ATTR_PAN_ID] = { .type = NLA_U16, },
+	[NL802154_ATTR_EXTENDED_ADDR] = { .type = NLA_U64 },
+	[NL802154_ATTR_SHORT_ADDR] = { .type = NLA_U16, },
+
+	[NL802154_ATTR_MIN_BE] = { .type = NLA_U8, },
+	[NL802154_ATTR_MAX_BE] = { .type = NLA_U8, },
+	[NL802154_ATTR_MAX_CSMA_BACKOFFS] = { .type = NLA_U8, },
+
+	[NL802154_ATTR_MAX_FRAME_RETRIES] = { .type = NLA_S8, },
+
+	[NL802154_ATTR_LBT_MODE] = { .type = NLA_U8, },
+};
+
+/* message building helper */
+static inline void *nl802154hdr_put(struct sk_buff *skb, u32 portid, u32 seq,
+				    int flags, u8 cmd)
+{
+	/* since there is no private header just add the generic one */
+	return genlmsg_put(skb, portid, seq, &nl802154_fam, flags, cmd);
+}
+
+static int
+nl802154_send_wpan_phy_channels(struct cfg802154_registered_device *rdev,
+				struct sk_buff *msg)
+{
+	struct nlattr *nl_page;
+	unsigned long page;
+
+	nl_page = nla_nest_start(msg, NL802154_ATTR_CHANNELS_SUPPORTED);
+	if (!nl_page)
+		return -ENOBUFS;
+
+	for (page = 0; page <= IEEE802154_MAX_PAGE; page++) {
+		if (nla_put_u32(msg, NL802154_ATTR_SUPPORTED_CHANNEL,
+				rdev->wpan_phy.channels_supported[page]))
+			return -ENOBUFS;
+	}
+	nla_nest_end(msg, nl_page);
+
+	return 0;
+}
+
+static int nl802154_send_wpan_phy(struct cfg802154_registered_device *rdev,
+				  enum nl802154_commands cmd,
+				  struct sk_buff *msg, u32 portid, u32 seq,
+				  int flags)
+{
+	void *hdr;
+
+	hdr = nl802154hdr_put(msg, portid, seq, flags, cmd);
+	if (!hdr)
+		return -ENOBUFS;
+
+	if (nla_put_u32(msg, NL802154_ATTR_WPAN_PHY, rdev->wpan_phy_idx) ||
+	    nla_put_string(msg, NL802154_ATTR_WPAN_PHY_NAME,
+			   wpan_phy_name(&rdev->wpan_phy)) ||
+	    nla_put_u32(msg, NL802154_ATTR_GENERATION,
+			cfg802154_rdev_list_generation))
+		goto nla_put_failure;
+
+	if (cmd != NL802154_CMD_NEW_WPAN_PHY)
+		goto finish;
+
+	/* DUMP PHY PIB */
+
+	/* current channel settings */
+	if (nla_put_u8(msg, NL802154_ATTR_PAGE,
+		       rdev->wpan_phy.current_page) ||
+	    nla_put_u8(msg, NL802154_ATTR_CHANNEL,
+		       rdev->wpan_phy.current_channel))
+		goto nla_put_failure;
+
+	/* supported channels array */
+	if (nl802154_send_wpan_phy_channels(rdev, msg))
+		goto nla_put_failure;
+
+	/* cca mode */
+	if (nla_put_u8(msg, NL802154_ATTR_CCA_MODE,
+		       rdev->wpan_phy.cca_mode))
+		goto nla_put_failure;
+
+	if (nla_put_s8(msg, NL802154_ATTR_TX_POWER,
+		       rdev->wpan_phy.transmit_power))
+		goto nla_put_failure;
+
+finish:
+	return genlmsg_end(msg, hdr);
+
+nla_put_failure:
+	genlmsg_cancel(msg, hdr);
+	return -EMSGSIZE;
+}
+
+struct nl802154_dump_wpan_phy_state {
+	s64 filter_wpan_phy;
+	long start;
+
+};
+
+static int nl802154_dump_wpan_phy_parse(struct sk_buff *skb,
+					struct netlink_callback *cb,
+					struct nl802154_dump_wpan_phy_state *state)
+{
+	struct nlattr **tb = nl802154_fam.attrbuf;
+	int ret = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl802154_fam.hdrsize,
+			      tb, nl802154_fam.maxattr, nl802154_policy);
+
+	/* TODO check if we can handle error here,
+	 * we have no backward compatibility
+	 */
+	if (ret)
+		return 0;
+
+	if (tb[NL802154_ATTR_WPAN_PHY])
+		state->filter_wpan_phy = nla_get_u32(tb[NL802154_ATTR_WPAN_PHY]);
+	if (tb[NL802154_ATTR_WPAN_DEV])
+		state->filter_wpan_phy = nla_get_u64(tb[NL802154_ATTR_WPAN_DEV]) >> 32;
+	if (tb[NL802154_ATTR_IFINDEX]) {
+		struct net_device *netdev;
+		struct cfg802154_registered_device *rdev;
+		int ifidx = nla_get_u32(tb[NL802154_ATTR_IFINDEX]);
+
+		/* TODO netns */
+		netdev = __dev_get_by_index(&init_net, ifidx);
+		if (!netdev)
+			return -ENODEV;
+		if (netdev->ieee802154_ptr) {
+			rdev = wpan_phy_to_rdev(
+					netdev->ieee802154_ptr->wpan_phy);
+			state->filter_wpan_phy = rdev->wpan_phy_idx;
+		}
+	}
+
+	return 0;
+}
+
+static int
+nl802154_dump_wpan_phy(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	int idx = 0, ret;
+	struct nl802154_dump_wpan_phy_state *state = (void *)cb->args[0];
+	struct cfg802154_registered_device *rdev;
+
+	rtnl_lock();
+	if (!state) {
+		state = kzalloc(sizeof(*state), GFP_KERNEL);
+		if (!state) {
+			rtnl_unlock();
+			return -ENOMEM;
+		}
+		state->filter_wpan_phy = -1;
+		ret = nl802154_dump_wpan_phy_parse(skb, cb, state);
+		if (ret) {
+			kfree(state);
+			rtnl_unlock();
+			return ret;
+		}
+		cb->args[0] = (long)state;
+	}
+
+	list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
+		/* TODO net ns compare */
+		if (++idx <= state->start)
+			continue;
+		if (state->filter_wpan_phy != -1 &&
+		    state->filter_wpan_phy != rdev->wpan_phy_idx)
+			continue;
+		/* attempt to fit multiple wpan_phy data chunks into the skb */
+		ret = nl802154_send_wpan_phy(rdev,
+					     NL802154_CMD_NEW_WPAN_PHY,
+					     skb,
+					     NETLINK_CB(cb->skb).portid,
+					     cb->nlh->nlmsg_seq, NLM_F_MULTI);
+		if (ret < 0) {
+			if ((ret == -ENOBUFS || ret == -EMSGSIZE) &&
+			    !skb->len && cb->min_dump_alloc < 4096) {
+				cb->min_dump_alloc = 4096;
+				rtnl_unlock();
+				return 1;
+			}
+			idx--;
+			break;
+		}
+		break;
+	}
+	rtnl_unlock();
+
+	state->start = idx;
+
+	return skb->len;
+}
+
+static int nl802154_dump_wpan_phy_done(struct netlink_callback *cb)
+{
+	kfree((void *)cb->args[0]);
+	return 0;
+}
+
+static int nl802154_get_wpan_phy(struct sk_buff *skb, struct genl_info *info)
+{
+	struct sk_buff *msg;
+	struct cfg802154_registered_device *rdev = info->user_ptr[0];
+
+	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+	if (!msg)
+		return -ENOMEM;
+
+	if (nl802154_send_wpan_phy(rdev, NL802154_CMD_NEW_WPAN_PHY, msg,
+				   info->snd_portid, info->snd_seq, 0) < 0) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	return genlmsg_reply(msg, info);
+}
+
+static inline u64 wpan_dev_id(struct wpan_dev *wpan_dev)
+{
+	return (u64)wpan_dev->identifier |
+	       ((u64)wpan_phy_to_rdev(wpan_dev->wpan_phy)->wpan_phy_idx << 32);
+}
+
+static int
+nl802154_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags,
+		    struct cfg802154_registered_device *rdev,
+		    struct wpan_dev *wpan_dev)
+{
+	struct net_device *dev = wpan_dev->netdev;
+	void *hdr;
+
+	hdr = nl802154hdr_put(msg, portid, seq, flags,
+			      NL802154_CMD_NEW_INTERFACE);
+	if (!hdr)
+		return -1;
+
+	if (dev &&
+	    (nla_put_u32(msg, NL802154_ATTR_IFINDEX, dev->ifindex) ||
+	     nla_put_string(msg, NL802154_ATTR_IFNAME, dev->name)))
+		goto nla_put_failure;
+
+	if (nla_put_u32(msg, NL802154_ATTR_WPAN_PHY, rdev->wpan_phy_idx) ||
+	    nla_put_u32(msg, NL802154_ATTR_IFTYPE, wpan_dev->iftype) ||
+	    nla_put_u64(msg, NL802154_ATTR_WPAN_DEV, wpan_dev_id(wpan_dev)) ||
+	    nla_put_u32(msg, NL802154_ATTR_GENERATION,
+			rdev->devlist_generation ^
+			(cfg802154_rdev_list_generation << 2)))
+		goto nla_put_failure;
+
+	/* address settings */
+	if (nla_put_le64(msg, NL802154_ATTR_EXTENDED_ADDR,
+			 wpan_dev->extended_addr) ||
+	    nla_put_le16(msg, NL802154_ATTR_SHORT_ADDR,
+			 wpan_dev->short_addr) ||
+	    nla_put_le16(msg, NL802154_ATTR_PAN_ID, wpan_dev->pan_id))
+		goto nla_put_failure;
+
+	/* ARET handling */
+	if (nla_put_s8(msg, NL802154_ATTR_MAX_FRAME_RETRIES,
+		       wpan_dev->frame_retries) ||
+	    nla_put_u8(msg, NL802154_ATTR_MAX_BE, wpan_dev->max_be) ||
+	    nla_put_u8(msg, NL802154_ATTR_MAX_CSMA_BACKOFFS,
+		       wpan_dev->csma_retries) ||
+	    nla_put_u8(msg, NL802154_ATTR_MIN_BE, wpan_dev->min_be))
+		goto nla_put_failure;
+
+	/* listen before transmit */
+	if (nla_put_u8(msg, NL802154_ATTR_LBT_MODE, wpan_dev->lbt))
+		goto nla_put_failure;
+
+	return genlmsg_end(msg, hdr);
+
+nla_put_failure:
+	genlmsg_cancel(msg, hdr);
+	return -EMSGSIZE;
+}
+
+static int
+nl802154_dump_interface(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	int wp_idx = 0;
+	int if_idx = 0;
+	int wp_start = cb->args[0];
+	int if_start = cb->args[1];
+	struct cfg802154_registered_device *rdev;
+	struct wpan_dev *wpan_dev;
+
+	rtnl_lock();
+	list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
+		/* TODO netns compare */
+		if (wp_idx < wp_start) {
+			wp_idx++;
+			continue;
+		}
+		if_idx = 0;
+
+		list_for_each_entry(wpan_dev, &rdev->wpan_dev_list, list) {
+			if (if_idx < if_start) {
+				if_idx++;
+				continue;
+			}
+			if (nl802154_send_iface(skb, NETLINK_CB(cb->skb).portid,
+						cb->nlh->nlmsg_seq, NLM_F_MULTI,
+						rdev, wpan_dev) < 0) {
+				goto out;
+			}
+			if_idx++;
+		}
+
+		wp_idx++;
+	}
+out:
+	rtnl_unlock();
+
+	cb->args[0] = wp_idx;
+	cb->args[1] = if_idx;
+
+	return skb->len;
+}
+
+static int nl802154_get_interface(struct sk_buff *skb, struct genl_info *info)
+{
+	struct sk_buff *msg;
+	struct cfg802154_registered_device *rdev = info->user_ptr[0];
+	struct wpan_dev *wdev = info->user_ptr[1];
+
+	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+	if (!msg)
+		return -ENOMEM;
+
+	if (nl802154_send_iface(msg, info->snd_portid, info->snd_seq, 0,
+				rdev, wdev) < 0) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	return genlmsg_reply(msg, info);
+}
+
+static int nl802154_new_interface(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg802154_registered_device *rdev = info->user_ptr[0];
+	enum nl802154_iftype type = NL802154_IFTYPE_UNSPEC;
+	__le64 extended_addr = cpu_to_le64(0x0000000000000000ULL);
+
+	/* TODO avoid failing a new interface
+	 * creation due to pending removal?
+	 */
+
+	if (!info->attrs[NL802154_ATTR_IFNAME])
+		return -EINVAL;
+
+	if (info->attrs[NL802154_ATTR_IFTYPE]) {
+		type = nla_get_u32(info->attrs[NL802154_ATTR_IFTYPE]);
+		if (type > NL802154_IFTYPE_MAX)
+			return -EINVAL;
+	}
+
+	/* TODO add nla_get_le64 to netlink */
+	if (info->attrs[NL802154_ATTR_EXTENDED_ADDR])
+		extended_addr = (__force __le64)nla_get_u64(
+				info->attrs[NL802154_ATTR_EXTENDED_ADDR]);
+
+	if (!rdev->ops->add_virtual_intf)
+		return -EOPNOTSUPP;
+
+	return rdev_add_virtual_intf(rdev,
+				     nla_data(info->attrs[NL802154_ATTR_IFNAME]),
+				     type, extended_addr);
+}
+
+static int nl802154_del_interface(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg802154_registered_device *rdev = info->user_ptr[0];
+	struct wpan_dev *wpan_dev = info->user_ptr[1];
+
+	if (!rdev->ops->del_virtual_intf)
+		return -EOPNOTSUPP;
+
+	/* If we remove a wpan device without a netdev then clear
+	 * user_ptr[1] so that nl802154_post_doit won't dereference it
+	 * to check if it needs to do dev_put(). Otherwise it crashes
+	 * since the wpan_dev has been freed, unlike with a netdev where
+	 * we need the dev_put() for the netdev to really be freed.
+	 */
+	if (!wpan_dev->netdev)
+		info->user_ptr[1] = NULL;
+
+	return rdev_del_virtual_intf(rdev, wpan_dev);
+}
+
+static int nl802154_set_channel(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg802154_registered_device *rdev = info->user_ptr[0];
+	u8 channel, page;
+
+	if (!info->attrs[NL802154_ATTR_PAGE] ||
+	    !info->attrs[NL802154_ATTR_CHANNEL])
+		return -EINVAL;
+
+	page = nla_get_u8(info->attrs[NL802154_ATTR_PAGE]);
+	channel = nla_get_u8(info->attrs[NL802154_ATTR_CHANNEL]);
+
+	/* check 802.15.4 constraints */
+	if (page > IEEE802154_MAX_PAGE || channel > IEEE802154_MAX_CHANNEL)
+		return -EINVAL;
+
+	return rdev_set_channel(rdev, page, channel);
+}
+
+static int nl802154_set_pan_id(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg802154_registered_device *rdev = info->user_ptr[0];
+	struct net_device *dev = info->user_ptr[1];
+	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
+	__le16 pan_id;
+
+	/* conflict here while tx/rx calls */
+	if (netif_running(dev))
+		return -EBUSY;
+
+	/* don't change address fields on monitor */
+	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
+		return -EINVAL;
+
+	if (!info->attrs[NL802154_ATTR_PAN_ID])
+		return -EINVAL;
+
+	pan_id = nla_get_le16(info->attrs[NL802154_ATTR_PAN_ID]);
+
+	return rdev_set_pan_id(rdev, wpan_dev, pan_id);
+}
+
+static int nl802154_set_short_addr(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg802154_registered_device *rdev = info->user_ptr[0];
+	struct net_device *dev = info->user_ptr[1];
+	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
+	__le16 short_addr;
+
+	/* conflict here while tx/rx calls */
+	if (netif_running(dev))
+		return -EBUSY;
+
+	/* don't change address fields on monitor */
+	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
+		return -EINVAL;
+
+	if (!info->attrs[NL802154_ATTR_SHORT_ADDR])
+		return -EINVAL;
+
+	short_addr = nla_get_le16(info->attrs[NL802154_ATTR_SHORT_ADDR]);
+
+	return rdev_set_short_addr(rdev, wpan_dev, short_addr);
+}
+
+static int
+nl802154_set_backoff_exponent(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg802154_registered_device *rdev = info->user_ptr[0];
+	struct net_device *dev = info->user_ptr[1];
+	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
+	u8 min_be, max_be;
+
+	/* should be set on netif open inside phy settings */
+	if (netif_running(dev))
+		return -EBUSY;
+
+	if (!info->attrs[NL802154_ATTR_MIN_BE] ||
+	    !info->attrs[NL802154_ATTR_MAX_BE])
+		return -EINVAL;
+
+	min_be = nla_get_u8(info->attrs[NL802154_ATTR_MIN_BE]);
+	max_be = nla_get_u8(info->attrs[NL802154_ATTR_MAX_BE]);
+
+	/* check 802.15.4 constraints */
+	if (max_be < 3 || max_be > 8 || min_be > max_be)
+		return -EINVAL;
+
+	return rdev_set_backoff_exponent(rdev, wpan_dev, min_be, max_be);
+}
+
+static int
+nl802154_set_max_csma_backoffs(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg802154_registered_device *rdev = info->user_ptr[0];
+	struct net_device *dev = info->user_ptr[1];
+	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
+	u8 max_csma_backoffs;
+
+	/* conflict here while other running iface settings */
+	if (netif_running(dev))
+		return -EBUSY;
+
+	if (!info->attrs[NL802154_ATTR_MAX_CSMA_BACKOFFS])
+		return -EINVAL;
+
+	max_csma_backoffs = nla_get_u8(
+			info->attrs[NL802154_ATTR_MAX_CSMA_BACKOFFS]);
+
+	/* check 802.15.4 constraints */
+	if (max_csma_backoffs > 5)
+		return -EINVAL;
+
+	return rdev_set_max_csma_backoffs(rdev, wpan_dev, max_csma_backoffs);
+}
+
+static int
+nl802154_set_max_frame_retries(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg802154_registered_device *rdev = info->user_ptr[0];
+	struct net_device *dev = info->user_ptr[1];
+	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
+	s8 max_frame_retries;
+
+	if (netif_running(dev))
+		return -EBUSY;
+
+	if (!info->attrs[NL802154_ATTR_MAX_FRAME_RETRIES])
+		return -EINVAL;
+
+	max_frame_retries = nla_get_s8(
+			info->attrs[NL802154_ATTR_MAX_FRAME_RETRIES]);
+
+	/* check 802.15.4 constraints */
+	if (max_frame_retries < -1 || max_frame_retries > 7)
+		return -EINVAL;
+
+	return rdev_set_max_frame_retries(rdev, wpan_dev, max_frame_retries);
+}
+
+static int nl802154_set_lbt_mode(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg802154_registered_device *rdev = info->user_ptr[0];
+	struct net_device *dev = info->user_ptr[1];
+	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
+	bool mode;
+
+	if (netif_running(dev))
+		return -EBUSY;
+
+	if (!info->attrs[NL802154_ATTR_LBT_MODE])
+		return -EINVAL;
+
+	mode = !!nla_get_u8(info->attrs[NL802154_ATTR_LBT_MODE]);
+	return rdev_set_lbt_mode(rdev, wpan_dev, mode);
+}
+
+#define NL802154_FLAG_NEED_WPAN_PHY	0x01
+#define NL802154_FLAG_NEED_NETDEV	0x02
+#define NL802154_FLAG_NEED_RTNL		0x04
+#define NL802154_FLAG_CHECK_NETDEV_UP	0x08
+#define NL802154_FLAG_NEED_NETDEV_UP	(NL802154_FLAG_NEED_NETDEV |\
+					 NL802154_FLAG_CHECK_NETDEV_UP)
+#define NL802154_FLAG_NEED_WPAN_DEV	0x10
+#define NL802154_FLAG_NEED_WPAN_DEV_UP	(NL802154_FLAG_NEED_WPAN_DEV |\
+					 NL802154_FLAG_CHECK_NETDEV_UP)
+
+static int nl802154_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
+			     struct genl_info *info)
+{
+	struct cfg802154_registered_device *rdev;
+	struct wpan_dev *wpan_dev;
+	struct net_device *dev;
+	bool rtnl = ops->internal_flags & NL802154_FLAG_NEED_RTNL;
+
+	if (rtnl)
+		rtnl_lock();
+
+	if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_PHY) {
+		rdev = cfg802154_get_dev_from_info(genl_info_net(info), info);
+		if (IS_ERR(rdev)) {
+			if (rtnl)
+				rtnl_unlock();
+			return PTR_ERR(rdev);
+		}
+		info->user_ptr[0] = rdev;
+	} else if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV ||
+		   ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) {
+		ASSERT_RTNL();
+		wpan_dev = __cfg802154_wpan_dev_from_attrs(genl_info_net(info),
+							   info->attrs);
+		if (IS_ERR(wpan_dev)) {
+			if (rtnl)
+				rtnl_unlock();
+			return PTR_ERR(wpan_dev);
+		}
+
+		dev = wpan_dev->netdev;
+		rdev = wpan_phy_to_rdev(wpan_dev->wpan_phy);
+
+		if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV) {
+			if (!dev) {
+				if (rtnl)
+					rtnl_unlock();
+				return -EINVAL;
+			}
+
+			info->user_ptr[1] = dev;
+		} else {
+			info->user_ptr[1] = wpan_dev;
+		}
+
+		if (dev) {
+			if (ops->internal_flags & NL802154_FLAG_CHECK_NETDEV_UP &&
+			    !netif_running(dev)) {
+				if (rtnl)
+					rtnl_unlock();
+				return -ENETDOWN;
+			}
+
+			dev_hold(dev);
+		}
+
+		info->user_ptr[0] = rdev;
+	}
+
+	return 0;
+}
+
+static void nl802154_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
+			       struct genl_info *info)
+{
+	if (info->user_ptr[1]) {
+		if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) {
+			struct wpan_dev *wpan_dev = info->user_ptr[1];
+
+			if (wpan_dev->netdev)
+				dev_put(wpan_dev->netdev);
+		} else {
+			dev_put(info->user_ptr[1]);
+		}
+	}
+
+	if (ops->internal_flags & NL802154_FLAG_NEED_RTNL)
+		rtnl_unlock();
+}
+
+static const struct genl_ops nl802154_ops[] = {
+	{
+		.cmd = NL802154_CMD_GET_WPAN_PHY,
+		.doit = nl802154_get_wpan_phy,
+		.dumpit = nl802154_dump_wpan_phy,
+		.done = nl802154_dump_wpan_phy_done,
+		.policy = nl802154_policy,
+		/* can be retrieved by unprivileged users */
+		.internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
+				  NL802154_FLAG_NEED_RTNL,
+	},
+	{
+		.cmd = NL802154_CMD_GET_INTERFACE,
+		.doit = nl802154_get_interface,
+		.dumpit = nl802154_dump_interface,
+		.policy = nl802154_policy,
+		/* can be retrieved by unprivileged users */
+		.internal_flags = NL802154_FLAG_NEED_WPAN_DEV |
+				  NL802154_FLAG_NEED_RTNL,
+	},
+	{
+		.cmd = NL802154_CMD_NEW_INTERFACE,
+		.doit = nl802154_new_interface,
+		.policy = nl802154_policy,
+		.flags = GENL_ADMIN_PERM,
+		.internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
+				  NL802154_FLAG_NEED_RTNL,
+	},
+	{
+		.cmd = NL802154_CMD_DEL_INTERFACE,
+		.doit = nl802154_del_interface,
+		.policy = nl802154_policy,
+		.flags = GENL_ADMIN_PERM,
+		.internal_flags = NL802154_FLAG_NEED_WPAN_DEV |
+				  NL802154_FLAG_NEED_RTNL,
+	},
+	{
+		.cmd = NL802154_CMD_SET_CHANNEL,
+		.doit = nl802154_set_channel,
+		.policy = nl802154_policy,
+		.flags = GENL_ADMIN_PERM,
+		.internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
+				  NL802154_FLAG_NEED_RTNL,
+	},
+	{
+		.cmd = NL802154_CMD_SET_PAN_ID,
+		.doit = nl802154_set_pan_id,
+		.policy = nl802154_policy,
+		.flags = GENL_ADMIN_PERM,
+		.internal_flags = NL802154_FLAG_NEED_NETDEV |
+				  NL802154_FLAG_NEED_RTNL,
+	},
+	{
+		.cmd = NL802154_CMD_SET_SHORT_ADDR,
+		.doit = nl802154_set_short_addr,
+		.policy = nl802154_policy,
+		.flags = GENL_ADMIN_PERM,
+		.internal_flags = NL802154_FLAG_NEED_NETDEV |
+				  NL802154_FLAG_NEED_RTNL,
+	},
+	{
+		.cmd = NL802154_CMD_SET_BACKOFF_EXPONENT,
+		.doit = nl802154_set_backoff_exponent,
+		.policy = nl802154_policy,
+		.flags = GENL_ADMIN_PERM,
+		.internal_flags = NL802154_FLAG_NEED_NETDEV |
+				  NL802154_FLAG_NEED_RTNL,
+	},
+	{
+		.cmd = NL802154_CMD_SET_MAX_CSMA_BACKOFFS,
+		.doit = nl802154_set_max_csma_backoffs,
+		.policy = nl802154_policy,
+		.flags = GENL_ADMIN_PERM,
+		.internal_flags = NL802154_FLAG_NEED_NETDEV |
+				  NL802154_FLAG_NEED_RTNL,
+	},
+	{
+		.cmd = NL802154_CMD_SET_MAX_FRAME_RETRIES,
+		.doit = nl802154_set_max_frame_retries,
+		.policy = nl802154_policy,
+		.flags = GENL_ADMIN_PERM,
+		.internal_flags = NL802154_FLAG_NEED_NETDEV |
+				  NL802154_FLAG_NEED_RTNL,
+	},
+	{
+		.cmd = NL802154_CMD_SET_LBT_MODE,
+		.doit = nl802154_set_lbt_mode,
+		.policy = nl802154_policy,
+		.flags = GENL_ADMIN_PERM,
+		.internal_flags = NL802154_FLAG_NEED_NETDEV |
+				  NL802154_FLAG_NEED_RTNL,
+	},
+};
+
+/* initialisation/exit functions */
+int nl802154_init(void)
+{
+	return genl_register_family_with_ops_groups(&nl802154_fam, nl802154_ops,
+						    nl802154_mcgrps);
+}
+
+void nl802154_exit(void)
+{
+	genl_unregister_family(&nl802154_fam);
+}
diff --git a/net/ieee802154/nl802154.h b/net/ieee802154/nl802154.h
new file mode 100644
index 0000000..3846a89
--- /dev/null
+++ b/net/ieee802154/nl802154.h
@@ -0,0 +1,7 @@
+#ifndef __IEEE802154_NL802154_H
+#define __IEEE802154_NL802154_H
+
+int nl802154_init(void);
+void nl802154_exit(void);
+
+#endif /* __IEEE802154_NL802154_H */
diff --git a/net/ieee802154/rdev-ops.h b/net/ieee802154/rdev-ops.h
index ac8824e..aff54fbd 100644
--- a/net/ieee802154/rdev-ops.h
+++ b/net/ieee802154/rdev-ops.h
@@ -20,4 +20,70 @@
 	rdev->ops->del_virtual_intf_deprecated(&rdev->wpan_phy, dev);
 }
 
+static inline int
+rdev_add_virtual_intf(struct cfg802154_registered_device *rdev, char *name,
+		      enum nl802154_iftype type, __le64 extended_addr)
+{
+	return rdev->ops->add_virtual_intf(&rdev->wpan_phy, name, type,
+					   extended_addr);
+}
+
+static inline int
+rdev_del_virtual_intf(struct cfg802154_registered_device *rdev,
+		      struct wpan_dev *wpan_dev)
+{
+	return rdev->ops->del_virtual_intf(&rdev->wpan_phy, wpan_dev);
+}
+
+static inline int
+rdev_set_channel(struct cfg802154_registered_device *rdev, u8 page, u8 channel)
+{
+	return rdev->ops->set_channel(&rdev->wpan_phy, page, channel);
+}
+
+static inline int
+rdev_set_pan_id(struct cfg802154_registered_device *rdev,
+		struct wpan_dev *wpan_dev, __le16 pan_id)
+{
+	return rdev->ops->set_pan_id(&rdev->wpan_phy, wpan_dev, pan_id);
+}
+
+static inline int
+rdev_set_short_addr(struct cfg802154_registered_device *rdev,
+		    struct wpan_dev *wpan_dev, __le16 short_addr)
+{
+	return rdev->ops->set_short_addr(&rdev->wpan_phy, wpan_dev, short_addr);
+}
+
+static inline int
+rdev_set_backoff_exponent(struct cfg802154_registered_device *rdev,
+			  struct wpan_dev *wpan_dev, u8 min_be, u8 max_be)
+{
+	return rdev->ops->set_backoff_exponent(&rdev->wpan_phy, wpan_dev,
+					       min_be, max_be);
+}
+
+static inline int
+rdev_set_max_csma_backoffs(struct cfg802154_registered_device *rdev,
+			   struct wpan_dev *wpan_dev, u8 max_csma_backoffs)
+{
+	return rdev->ops->set_max_csma_backoffs(&rdev->wpan_phy, wpan_dev,
+						max_csma_backoffs);
+}
+
+static inline int
+rdev_set_max_frame_retries(struct cfg802154_registered_device *rdev,
+			   struct wpan_dev *wpan_dev, s8 max_frame_retries)
+{
+	return rdev->ops->set_max_frame_retries(&rdev->wpan_phy, wpan_dev,
+						max_frame_retries);
+}
+
+static inline int
+rdev_set_lbt_mode(struct cfg802154_registered_device *rdev,
+		  struct wpan_dev *wpan_dev, bool mode)
+{
+	return rdev->ops->set_lbt_mode(&rdev->wpan_phy, wpan_dev, mode);
+}
+
 #endif /* __CFG802154_RDEV_OPS */
diff --git a/net/ieee802154/sysfs.c b/net/ieee802154/sysfs.c
index 8819998..1613b9c 100644
--- a/net/ieee802154/sysfs.c
+++ b/net/ieee802154/sysfs.c
@@ -27,6 +27,27 @@
 			    wpan_phy.dev);
 }
 
+#define SHOW_FMT(name, fmt, member)					\
+static ssize_t name ## _show(struct device *dev,			\
+			     struct device_attribute *attr,		\
+			     char *buf)					\
+{									\
+	return sprintf(buf, fmt "\n", dev_to_rdev(dev)->member);	\
+}									\
+static DEVICE_ATTR_RO(name)
+
+SHOW_FMT(index, "%d", wpan_phy_idx);
+
+static ssize_t name_show(struct device *dev,
+			 struct device_attribute *attr,
+			 char *buf)
+{
+	struct wpan_phy *wpan_phy = &dev_to_rdev(dev)->wpan_phy;
+
+	return sprintf(buf, "%s\n", dev_name(&wpan_phy->dev));
+}
+static DEVICE_ATTR_RO(name);
+
 #define MASTER_SHOW_COMPLEX(name, format_string, args...)		\
 static ssize_t name ## _show(struct device *dev,			\
 			    struct device_attribute *attr, char *buf)	\
@@ -78,6 +99,9 @@
 }
 
 static struct attribute *pmib_attrs[] = {
+	&dev_attr_index.attr,
+	&dev_attr_name.attr,
+	/* below will be removed soon */
 	&dev_attr_current_channel.attr,
 	&dev_attr_current_page.attr,
 	&dev_attr_channels_supported.attr,
diff --git a/net/mac80211/aes_ccm.c b/net/mac80211/aes_ccm.c
index ec24378..09d9caa 100644
--- a/net/mac80211/aes_ccm.c
+++ b/net/mac80211/aes_ccm.c
@@ -53,6 +53,9 @@
 		__aligned(__alignof__(struct aead_request));
 	struct aead_request *aead_req = (void *) aead_req_data;
 
+	if (data_len == 0)
+		return -EINVAL;
+
 	memset(aead_req, 0, sizeof(aead_req_data));
 
 	sg_init_one(&pt, data, data_len);
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index 56b5357..509bc15 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -805,7 +805,7 @@
 
 	memset(&params, 0, sizeof(params));
 	memset(&csa_ie, 0, sizeof(csa_ie));
-	err = ieee80211_parse_ch_switch_ie(sdata, elems, beacon,
+	err = ieee80211_parse_ch_switch_ie(sdata, elems,
 					   ifibss->chandef.chan->band,
 					   sta_flags, ifibss->bssid, &csa_ie);
 	/* can't switch to destination channel, fail */
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index cf95d03..cc6e964 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1723,7 +1723,6 @@
  * ieee80211_parse_ch_switch_ie - parses channel switch IEs
  * @sdata: the sdata of the interface which has received the frame
  * @elems: parsed 802.11 elements received with the frame
- * @beacon: indicates if the frame was a beacon or probe response
  * @current_band: indicates the current band
  * @sta_flags: contains information about own capabilities and restrictions
  *	to decide which channel switch announcements can be accepted. Only the
@@ -1737,7 +1736,7 @@
  * Return: 0 on success, <0 on error and >0 if there is nothing to parse.
  */
 int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
-				 struct ieee802_11_elems *elems, bool beacon,
+				 struct ieee802_11_elems *elems,
 				 enum ieee80211_band current_band,
 				 u32 sta_flags, u8 *bssid,
 				 struct ieee80211_csa_ie *csa_ie);
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 96c4572c..4173553 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -778,10 +778,12 @@
 	int i, flushed;
 	struct ps_data *ps;
 	struct cfg80211_chan_def chandef;
+	bool cancel_scan;
 
 	clear_bit(SDATA_STATE_RUNNING, &sdata->state);
 
-	if (rcu_access_pointer(local->scan_sdata) == sdata)
+	cancel_scan = rcu_access_pointer(local->scan_sdata) == sdata;
+	if (cancel_scan)
 		ieee80211_scan_cancel(local);
 
 	/*
@@ -912,6 +914,8 @@
 		list_del(&sdata->u.vlan.list);
 		mutex_unlock(&local->mtx);
 		RCU_INIT_POINTER(sdata->vif.chanctx_conf, NULL);
+		/* see comment in the default case below */
+		ieee80211_free_keys(sdata, true);
 		/* no need to tell driver */
 		break;
 	case NL80211_IFTYPE_MONITOR:
@@ -937,17 +941,16 @@
 		/*
 		 * When we get here, the interface is marked down.
 		 * Free the remaining keys, if there are any
-		 * (shouldn't be, except maybe in WDS mode?)
+		 * (which can happen in AP mode if userspace sets
+		 * keys before the interface is operating, and maybe
+		 * also in WDS mode)
 		 *
 		 * Force the key freeing to always synchronize_net()
 		 * to wait for the RX path in case it is using this
-		 * interface enqueuing frames * at this very time on
+		 * interface enqueuing frames at this very time on
 		 * another CPU.
 		 */
 		ieee80211_free_keys(sdata, true);
-
-		/* fall through */
-	case NL80211_IFTYPE_AP:
 		skb_queue_purge(&sdata->skb_queue);
 	}
 
@@ -1005,6 +1008,9 @@
 
 	ieee80211_recalc_ps(local, -1);
 
+	if (cancel_scan)
+		flush_delayed_work(&local->scan_work);
+
 	if (local->open_count == 0) {
 		ieee80211_stop_device(local);
 
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index e9f99c1..0c8b2a7 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -874,7 +874,7 @@
 
 	memset(&params, 0, sizeof(params));
 	memset(&csa_ie, 0, sizeof(csa_ie));
-	err = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, band,
+	err = ieee80211_parse_ch_switch_ie(sdata, elems, band,
 					   sta_flags, sdata->vif.addr,
 					   &csa_ie);
 	if (err < 0)
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 8a23a64..75a9bf5 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -1123,7 +1123,7 @@
 
 	current_band = cbss->channel->band;
 	memset(&csa_ie, 0, sizeof(csa_ie));
-	res = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, current_band,
+	res = ieee80211_parse_ch_switch_ie(sdata, elems, current_band,
 					   ifmgd->flags,
 					   ifmgd->associated->bssid, &csa_ie);
 	if (res	< 0)
@@ -1225,7 +1225,8 @@
 		ieee80211_queue_work(&local->hw, &ifmgd->chswitch_work);
 	else
 		mod_timer(&ifmgd->chswitch_timer,
-			  TU_TO_EXP_TIME(csa_ie.count * cbss->beacon_interval));
+			  TU_TO_EXP_TIME((csa_ie.count - 1) *
+					 cbss->beacon_interval));
 }
 
 static bool
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index d9bbb73..49c23bd 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -1761,11 +1761,14 @@
 	sc = le16_to_cpu(hdr->seq_ctrl);
 	frag = sc & IEEE80211_SCTL_FRAG;
 
-	if (likely((!ieee80211_has_morefrags(fc) && frag == 0) ||
-		   is_multicast_ether_addr(hdr->addr1))) {
-		/* not fragmented */
+	if (likely(!ieee80211_has_morefrags(fc) && frag == 0))
+		goto out;
+
+	if (is_multicast_ether_addr(hdr->addr1)) {
+		rx->local->dot11MulticastReceivedFrameCount++;
 		goto out;
 	}
+
 	I802_DEBUG_INC(rx->local->rx_handlers_fragments);
 
 	if (skb_linearize(rx->skb))
@@ -1858,10 +1861,7 @@
  out:
 	if (rx->sta)
 		rx->sta->rx_packets++;
-	if (is_multicast_ether_addr(hdr->addr1))
-		rx->local->dot11MulticastReceivedFrameCount++;
-	else
-		ieee80211_led_rx(rx->local);
+	ieee80211_led_rx(rx->local);
 	return RX_CONTINUE;
 }
 
diff --git a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c
index 6ab0090..efeba56 100644
--- a/net/mac80211/spectmgmt.c
+++ b/net/mac80211/spectmgmt.c
@@ -22,7 +22,7 @@
 #include "wme.h"
 
 int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
-				 struct ieee802_11_elems *elems, bool beacon,
+				 struct ieee802_11_elems *elems,
 				 enum ieee80211_band current_band,
 				 u32 sta_flags, u8 *bssid,
 				 struct ieee80211_csa_ie *csa_ie)
@@ -91,19 +91,13 @@
 		return -EINVAL;
 	}
 
-	if (!beacon && sec_chan_offs) {
+	if (sec_chan_offs) {
 		secondary_channel_offset = sec_chan_offs->sec_chan_offs;
-	} else if (beacon && ht_oper) {
-		secondary_channel_offset =
-			ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET;
 	} else if (!(sta_flags & IEEE80211_STA_DISABLE_HT)) {
-		/* If it's not a beacon, HT is enabled and the IE not present,
-		 * it's 20 MHz, 802.11-2012 8.5.2.6:
-		 *	This element [the Secondary Channel Offset Element] is
-		 *	present when switching to a 40 MHz channel. It may be
-		 *	present when switching to a 20 MHz channel (in which
-		 *	case the secondary channel offset is set to SCN).
-		 */
+		/* If the secondary channel offset IE is not present,
+		 * we can't know what's the post-CSA offset, so the
+		 * best we can do is use 20MHz.
+		*/
 		secondary_channel_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
 	}
 
diff --git a/net/mac802154/cfg.c b/net/mac802154/cfg.c
index d2c4e8f8..c035708 100644
--- a/net/mac802154/cfg.c
+++ b/net/mac802154/cfg.c
@@ -17,6 +17,7 @@
 #include <net/cfg802154.h>
 
 #include "ieee802154_i.h"
+#include "driver-ops.h"
 #include "cfg.h"
 
 static struct net_device *
@@ -27,7 +28,8 @@
 	struct net_device *dev;
 
 	rtnl_lock();
-	dev = ieee802154_if_add(local, name, NULL, type);
+	dev = ieee802154_if_add(local, name, type,
+				cpu_to_le64(0x0000000000000000ULL));
 	rtnl_unlock();
 
 	return dev;
@@ -41,7 +43,168 @@
 	ieee802154_if_remove(sdata);
 }
 
+static int
+ieee802154_add_iface(struct wpan_phy *phy, const char *name,
+		     enum nl802154_iftype type, __le64 extended_addr)
+{
+	struct ieee802154_local *local = wpan_phy_priv(phy);
+	struct net_device *err;
+
+	err = ieee802154_if_add(local, name, type, extended_addr);
+	if (IS_ERR(err))
+		return PTR_ERR(err);
+
+	return 0;
+}
+
+static int
+ieee802154_del_iface(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev)
+{
+	ieee802154_if_remove(IEEE802154_WPAN_DEV_TO_SUB_IF(wpan_dev));
+
+	return 0;
+}
+
+static int
+ieee802154_set_channel(struct wpan_phy *wpan_phy, u8 page, u8 channel)
+{
+	struct ieee802154_local *local = wpan_phy_priv(wpan_phy);
+	int ret;
+
+	ASSERT_RTNL();
+
+	/* check if phy support this setting */
+	if (!(wpan_phy->channels_supported[page] & BIT(channel)))
+		return -EINVAL;
+
+	ret = drv_set_channel(local, page, channel);
+	if (!ret) {
+		wpan_phy->current_page = page;
+		wpan_phy->current_channel = channel;
+	}
+
+	return ret;
+}
+
+static int
+ieee802154_set_pan_id(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev,
+		      __le16 pan_id)
+{
+	ASSERT_RTNL();
+
+	/* TODO
+	 * I am not sure about to check here on broadcast pan_id.
+	 * Broadcast is a valid setting, comment from 802.15.4:
+	 * If this value is 0xffff, the device is not associated.
+	 *
+	 * This could useful to simple deassociate an device.
+	 */
+	if (pan_id == cpu_to_le16(IEEE802154_PAN_ID_BROADCAST))
+		return -EINVAL;
+
+	wpan_dev->pan_id = pan_id;
+	return 0;
+}
+
+static int
+ieee802154_set_backoff_exponent(struct wpan_phy *wpan_phy,
+				struct wpan_dev *wpan_dev,
+				u8 min_be, u8 max_be)
+{
+	struct ieee802154_local *local = wpan_phy_priv(wpan_phy);
+
+	ASSERT_RTNL();
+
+	if (!(local->hw.flags & IEEE802154_HW_CSMA_PARAMS))
+		return -EOPNOTSUPP;
+
+	wpan_dev->min_be = min_be;
+	wpan_dev->max_be = max_be;
+	return 0;
+}
+
+static int
+ieee802154_set_short_addr(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev,
+			  __le16 short_addr)
+{
+	ASSERT_RTNL();
+
+	/* TODO
+	 * I am not sure about to check here on broadcast short_addr.
+	 * Broadcast is a valid setting, comment from 802.15.4:
+	 * A value of 0xfffe indicates that the device has
+	 * associated but has not been allocated an address. A
+	 * value of 0xffff indicates that the device does not
+	 * have a short address.
+	 *
+	 * I think we should allow to set these settings but
+	 * don't allow to allow socket communication with it.
+	 */
+	if (short_addr == cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC) ||
+	    short_addr == cpu_to_le16(IEEE802154_ADDR_SHORT_BROADCAST))
+		return -EINVAL;
+
+	wpan_dev->short_addr = short_addr;
+	return 0;
+}
+
+static int
+ieee802154_set_max_csma_backoffs(struct wpan_phy *wpan_phy,
+				 struct wpan_dev *wpan_dev,
+				 u8 max_csma_backoffs)
+{
+	struct ieee802154_local *local = wpan_phy_priv(wpan_phy);
+
+	ASSERT_RTNL();
+
+	if (!(local->hw.flags & IEEE802154_HW_CSMA_PARAMS))
+		return -EOPNOTSUPP;
+
+	wpan_dev->csma_retries = max_csma_backoffs;
+	return 0;
+}
+
+static int
+ieee802154_set_max_frame_retries(struct wpan_phy *wpan_phy,
+				 struct wpan_dev *wpan_dev,
+				 s8 max_frame_retries)
+{
+	struct ieee802154_local *local = wpan_phy_priv(wpan_phy);
+
+	ASSERT_RTNL();
+
+	if (!(local->hw.flags & IEEE802154_HW_FRAME_RETRIES))
+		return -EOPNOTSUPP;
+
+	wpan_dev->frame_retries = max_frame_retries;
+	return 0;
+}
+
+static int
+ieee802154_set_lbt_mode(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev,
+			bool mode)
+{
+	struct ieee802154_local *local = wpan_phy_priv(wpan_phy);
+
+	ASSERT_RTNL();
+
+	if (!(local->hw.flags & IEEE802154_HW_LBT))
+		return -EOPNOTSUPP;
+
+	wpan_dev->lbt = mode;
+	return 0;
+}
+
 const struct cfg802154_ops mac802154_config_ops = {
 	.add_virtual_intf_deprecated = ieee802154_add_iface_deprecated,
 	.del_virtual_intf_deprecated = ieee802154_del_iface_deprecated,
+	.add_virtual_intf = ieee802154_add_iface,
+	.del_virtual_intf = ieee802154_del_iface,
+	.set_channel = ieee802154_set_channel,
+	.set_pan_id = ieee802154_set_pan_id,
+	.set_short_addr = ieee802154_set_short_addr,
+	.set_backoff_exponent = ieee802154_set_backoff_exponent,
+	.set_max_csma_backoffs = ieee802154_set_max_csma_backoffs,
+	.set_max_frame_retries = ieee802154_set_max_frame_retries,
+	.set_lbt_mode = ieee802154_set_lbt_mode,
 };
diff --git a/net/mac802154/driver-ops.h b/net/mac802154/driver-ops.h
index dfd29ff..f21e864 100644
--- a/net/mac802154/driver-ops.h
+++ b/net/mac802154/driver-ops.h
@@ -50,16 +50,15 @@
 	local->started = false;
 }
 
-static inline int drv_set_channel(struct ieee802154_local *local,
-				  const u8 page, const u8 channel)
+static inline int
+drv_set_channel(struct ieee802154_local *local, u8 page, u8 channel)
 {
 	might_sleep();
 
 	return local->ops->set_channel(&local->hw, page, channel);
 }
 
-static inline int drv_set_tx_power(struct ieee802154_local *local,
-				   const s8 dbm)
+static inline int drv_set_tx_power(struct ieee802154_local *local, s8 dbm)
 {
 	might_sleep();
 
@@ -71,8 +70,7 @@
 	return local->ops->set_txpower(&local->hw, dbm);
 }
 
-static inline int drv_set_cca_mode(struct ieee802154_local *local,
-				   const u8 cca_mode)
+static inline int drv_set_cca_mode(struct ieee802154_local *local, u8 cca_mode)
 {
 	might_sleep();
 
@@ -84,8 +82,7 @@
 	return local->ops->set_cca_mode(&local->hw, cca_mode);
 }
 
-static inline int drv_set_lbt_mode(struct ieee802154_local *local,
-				   const bool mode)
+static inline int drv_set_lbt_mode(struct ieee802154_local *local, bool mode)
 {
 	might_sleep();
 
@@ -97,8 +94,8 @@
 	return local->ops->set_lbt(&local->hw, mode);
 }
 
-static inline int drv_set_cca_ed_level(struct ieee802154_local *local,
-				       const s32 ed_level)
+static inline int
+drv_set_cca_ed_level(struct ieee802154_local *local, s32 ed_level)
 {
 	might_sleep();
 
@@ -110,8 +107,7 @@
 	return local->ops->set_cca_ed_level(&local->hw, ed_level);
 }
 
-static inline int drv_set_pan_id(struct ieee802154_local *local,
-				 const __le16 pan_id)
+static inline int drv_set_pan_id(struct ieee802154_local *local, __le16 pan_id)
 {
 	struct ieee802154_hw_addr_filt filt;
 
@@ -128,8 +124,8 @@
 					    IEEE802154_AFILT_PANID_CHANGED);
 }
 
-static inline int drv_set_extended_addr(struct ieee802154_local *local,
-					const __le64 extended_addr)
+static inline int
+drv_set_extended_addr(struct ieee802154_local *local, __le64 extended_addr)
 {
 	struct ieee802154_hw_addr_filt filt;
 
@@ -146,8 +142,8 @@
 					    IEEE802154_AFILT_IEEEADDR_CHANGED);
 }
 
-static inline int drv_set_short_addr(struct ieee802154_local *local,
-				     const __le16 short_addr)
+static inline int
+drv_set_short_addr(struct ieee802154_local *local, __le16 short_addr)
 {
 	struct ieee802154_hw_addr_filt filt;
 
@@ -164,8 +160,8 @@
 					    IEEE802154_AFILT_SADDR_CHANGED);
 }
 
-static inline int drv_set_pan_coord(struct ieee802154_local *local,
-				    const bool is_coord)
+static inline int
+drv_set_pan_coord(struct ieee802154_local *local, bool is_coord)
 {
 	struct ieee802154_hw_addr_filt filt;
 
@@ -182,9 +178,9 @@
 					    IEEE802154_AFILT_PANC_CHANGED);
 }
 
-static inline int drv_set_csma_params(struct ieee802154_local *local,
-				      u8 min_be, u8 max_be,
-				      u8 max_csma_backoffs)
+static inline int
+drv_set_csma_params(struct ieee802154_local *local, u8 min_be, u8 max_be,
+		    u8 max_csma_backoffs)
 {
 	might_sleep();
 
@@ -197,8 +193,8 @@
 					   max_csma_backoffs);
 }
 
-static inline int drv_set_max_frame_retries(struct ieee802154_local *local,
-					    s8 max_frame_retries)
+static inline int
+drv_set_max_frame_retries(struct ieee802154_local *local, s8 max_frame_retries)
 {
 	might_sleep();
 
@@ -210,8 +206,8 @@
 	return local->ops->set_frame_retries(&local->hw, max_frame_retries);
 }
 
-static inline int drv_set_promiscuous_mode(struct ieee802154_local *local,
-					   const bool on)
+static inline int
+drv_set_promiscuous_mode(struct ieee802154_local *local, bool on)
 {
 	might_sleep();
 
diff --git a/net/mac802154/ieee802154_i.h b/net/mac802154/ieee802154_i.h
index 4acacea..bebd70f 100644
--- a/net/mac802154/ieee802154_i.h
+++ b/net/mac802154/ieee802154_i.h
@@ -20,8 +20,10 @@
 #define __IEEE802154_I_H
 
 #include <linux/mutex.h>
+#include <linux/hrtimer.h>
 #include <net/cfg802154.h>
 #include <net/mac802154.h>
+#include <net/nl802154.h>
 #include <net/ieee802154_netdev.h>
 
 #include "llsec.h"
@@ -51,6 +53,8 @@
 	 */
 	struct workqueue_struct	*workqueue;
 
+	struct hrtimer ifs_timer;
+
 	bool started;
 
 	struct tasklet_struct tasklet;
@@ -84,18 +88,6 @@
 
 	spinlock_t mib_lock;
 
-	__le16 pan_id;
-	__le16 short_addr;
-	__le64 extended_addr;
-	bool promiscuous_mode;
-
-	struct ieee802154_mac_params mac_params;
-
-	/* MAC BSN field */
-	u8 bsn;
-	/* MAC DSN field */
-	u8 dsn;
-
 	/* protects sec from concurrent access by netlink. access by
 	 * encrypt/decrypt/header_create safe without additional protection.
 	 */
@@ -108,6 +100,9 @@
 
 #define MAC802154_CHAN_NONE		0xff /* No channel is assigned */
 
+/* utility functions/constants */
+extern const void *const mac802154_wpan_phy_privid; /*  for wpan_phy privid */
+
 static inline struct ieee802154_local *
 hw_to_local(struct ieee802154_hw *hw)
 {
@@ -120,22 +115,25 @@
 	return netdev_priv(dev);
 }
 
+static inline struct ieee802154_sub_if_data *
+IEEE802154_WPAN_DEV_TO_SUB_IF(struct wpan_dev *wpan_dev)
+{
+	return container_of(wpan_dev, struct ieee802154_sub_if_data, wpan_dev);
+}
+
 static inline bool
 ieee802154_sdata_running(struct ieee802154_sub_if_data *sdata)
 {
 	return test_bit(SDATA_STATE_RUNNING, &sdata->state);
 }
 
-extern struct ieee802154_reduced_mlme_ops mac802154_mlme_reduced;
 extern struct ieee802154_mlme_ops mac802154_mlme_wpan;
 
-void mac802154_monitor_setup(struct net_device *dev);
 netdev_tx_t
 ieee802154_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev);
-
-void mac802154_wpan_setup(struct net_device *dev);
 netdev_tx_t
 ieee802154_subif_start_xmit(struct sk_buff *skb, struct net_device *dev);
+enum hrtimer_restart ieee802154_xmit_ifs_timer(struct hrtimer *timer);
 
 /* MIB callbacks */
 void mac802154_dev_set_short_addr(struct net_device *dev, __le16 val);
@@ -178,11 +176,13 @@
 			 struct ieee802154_llsec_table **t);
 void mac802154_unlock_table(struct net_device *dev);
 
-struct net_device *
-mac802154_add_iface(struct wpan_phy *phy, const char *name, int type);
+/* interface handling */
+int ieee802154_iface_init(void);
+void ieee802154_iface_exit(void);
 void ieee802154_if_remove(struct ieee802154_sub_if_data *sdata);
 struct net_device *
 ieee802154_if_add(struct ieee802154_local *local, const char *name,
-		  struct wpan_dev **new_wpan_dev, int type);
+		  enum nl802154_iftype type, __le64 extended_addr);
+void ieee802154_remove_interfaces(struct ieee802154_local *local);
 
 #endif /* __IEEE802154_I_H */
diff --git a/net/mac802154/iface.c b/net/mac802154/iface.c
index 384f4bb..9ae8930 100644
--- a/net/mac802154/iface.c
+++ b/net/mac802154/iface.c
@@ -22,8 +22,7 @@
 #include <linux/if_arp.h>
 #include <linux/ieee802154.h>
 
-#include <net/rtnetlink.h>
-#include <linux/nl802154.h>
+#include <net/nl802154.h>
 #include <net/mac802154.h>
 #include <net/ieee802154_netdev.h>
 #include <net/cfg802154.h>
@@ -35,16 +34,17 @@
 {
 	struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
 	struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
+	struct wpan_dev *wpan_dev = &sdata->wpan_dev;
 	int rc = 0;
 
 	if (ops->llsec) {
 		struct ieee802154_llsec_params params;
 		int changed = 0;
 
-		params.pan_id = sdata->pan_id;
+		params.pan_id = wpan_dev->pan_id;
 		changed |= IEEE802154_LLSEC_PARAM_PAN_ID;
 
-		params.hwaddr = sdata->extended_addr;
+		params.hwaddr = wpan_dev->extended_addr;
 		changed |= IEEE802154_LLSEC_PARAM_HWADDR;
 
 		rc = ops->llsec->set_params(dev, &params, changed);
@@ -57,10 +57,13 @@
 mac802154_wpan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 {
 	struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+	struct wpan_dev *wpan_dev = &sdata->wpan_dev;
 	struct sockaddr_ieee802154 *sa =
 		(struct sockaddr_ieee802154 *)&ifr->ifr_addr;
 	int err = -ENOIOCTLCMD;
 
+	ASSERT_RTNL();
+
 	spin_lock_bh(&sdata->mib_lock);
 
 	switch (cmd) {
@@ -68,8 +71,8 @@
 	{
 		u16 pan_id, short_addr;
 
-		pan_id = le16_to_cpu(sdata->pan_id);
-		short_addr = le16_to_cpu(sdata->short_addr);
+		pan_id = le16_to_cpu(wpan_dev->pan_id);
+		short_addr = le16_to_cpu(wpan_dev->short_addr);
 		if (pan_id == IEEE802154_PANID_BROADCAST ||
 		    short_addr == IEEE802154_ADDR_BROADCAST) {
 			err = -EADDRNOTAVAIL;
@@ -85,6 +88,11 @@
 		break;
 	}
 	case SIOCSIFADDR:
+		if (netif_running(dev)) {
+			spin_unlock_bh(&sdata->mib_lock);
+			return -EBUSY;
+		}
+
 		dev_warn(&dev->dev,
 			 "Using DEBUGing ioctl SIOCSIFADDR isn't recommended!\n");
 		if (sa->family != AF_IEEE802154 ||
@@ -96,8 +104,8 @@
 			break;
 		}
 
-		sdata->pan_id = cpu_to_le16(sa->addr.pan_id);
-		sdata->short_addr = cpu_to_le16(sa->addr.short_addr);
+		wpan_dev->pan_id = cpu_to_le16(sa->addr.pan_id);
+		wpan_dev->short_addr = cpu_to_le16(sa->addr.short_addr);
 
 		err = mac802154_wpan_update_llsec(dev);
 		break;
@@ -121,7 +129,7 @@
 		return -EINVAL;
 
 	memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
-	sdata->extended_addr = extended_addr;
+	sdata->wpan_dev.extended_addr = extended_addr;
 
 	return mac802154_wpan_update_llsec(dev);
 }
@@ -135,7 +143,7 @@
 
 	ASSERT_RTNL();
 
-	if (sdata->vif.type == IEEE802154_DEV_WPAN) {
+	if (sdata->vif.type == NL802154_IFTYPE_NODE) {
 		mutex_lock(&sdata->local->iflist_mtx);
 		list_for_each_entry(subif, &sdata->local->interfaces, list) {
 			if (subif != sdata &&
@@ -172,6 +180,7 @@
 	int rc;
 	struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
 	struct ieee802154_local *local = sdata->local;
+	struct wpan_dev *wpan_dev = &sdata->wpan_dev;
 	struct wpan_phy *phy = sdata->local->phy;
 
 	rc = mac802154_slave_open(dev);
@@ -181,42 +190,42 @@
 	mutex_lock(&phy->pib_lock);
 
 	if (local->hw.flags & IEEE802154_HW_PROMISCUOUS) {
-		rc = drv_set_promiscuous_mode(local, sdata->promiscuous_mode);
+		rc = drv_set_promiscuous_mode(local,
+					      wpan_dev->promiscuous_mode);
 		if (rc < 0)
 			goto out;
 	}
 
 	if (local->hw.flags & IEEE802154_HW_AFILT) {
-		rc = drv_set_pan_id(local, sdata->pan_id);
+		rc = drv_set_pan_id(local, wpan_dev->pan_id);
 		if (rc < 0)
 			goto out;
 
-		rc = drv_set_extended_addr(local, sdata->extended_addr);
+		rc = drv_set_extended_addr(local, wpan_dev->extended_addr);
 		if (rc < 0)
 			goto out;
 
-		rc = drv_set_short_addr(local, sdata->short_addr);
+		rc = drv_set_short_addr(local, wpan_dev->short_addr);
 		if (rc < 0)
 			goto out;
 	}
 
 	if (local->hw.flags & IEEE802154_HW_LBT) {
-		rc = drv_set_lbt_mode(local, sdata->mac_params.lbt);
+		rc = drv_set_lbt_mode(local, wpan_dev->lbt);
 		if (rc < 0)
 			goto out;
 	}
 
 	if (local->hw.flags & IEEE802154_HW_CSMA_PARAMS) {
-		rc = drv_set_csma_params(local, sdata->mac_params.min_be,
-					 sdata->mac_params.max_be,
-					 sdata->mac_params.csma_retries);
+		rc = drv_set_csma_params(local, wpan_dev->min_be,
+					 wpan_dev->max_be,
+					 wpan_dev->csma_retries);
 		if (rc < 0)
 			goto out;
 	}
 
 	if (local->hw.flags & IEEE802154_HW_FRAME_RETRIES) {
-		rc = drv_set_max_frame_retries(local,
-					       sdata->mac_params.frame_retries);
+		rc = drv_set_max_frame_retries(local, wpan_dev->frame_retries);
 		if (rc < 0)
 			goto out;
 	}
@@ -236,6 +245,8 @@
 
 	ASSERT_RTNL();
 
+	hrtimer_cancel(&local->ifs_timer);
+
 	netif_stop_queue(dev);
 	local->open_count--;
 
@@ -288,6 +299,7 @@
 {
 	struct ieee802154_hdr hdr;
 	struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+	struct wpan_dev *wpan_dev = &sdata->wpan_dev;
 	struct ieee802154_mac_cb *cb = mac_cb(skb);
 	int hlen;
 
@@ -306,17 +318,17 @@
 	if (!saddr) {
 		spin_lock_bh(&sdata->mib_lock);
 
-		if (sdata->short_addr == cpu_to_le16(IEEE802154_ADDR_BROADCAST) ||
-		    sdata->short_addr == cpu_to_le16(IEEE802154_ADDR_UNDEF) ||
-		    sdata->pan_id == cpu_to_le16(IEEE802154_PANID_BROADCAST)) {
+		if (wpan_dev->short_addr == cpu_to_le16(IEEE802154_ADDR_BROADCAST) ||
+		    wpan_dev->short_addr == cpu_to_le16(IEEE802154_ADDR_UNDEF) ||
+		    wpan_dev->pan_id == cpu_to_le16(IEEE802154_PANID_BROADCAST)) {
 			hdr.source.mode = IEEE802154_ADDR_LONG;
-			hdr.source.extended_addr = sdata->extended_addr;
+			hdr.source.extended_addr = wpan_dev->extended_addr;
 		} else {
 			hdr.source.mode = IEEE802154_ADDR_SHORT;
-			hdr.source.short_addr = sdata->short_addr;
+			hdr.source.short_addr = wpan_dev->short_addr;
 		}
 
-		hdr.source.pan_id = sdata->pan_id;
+		hdr.source.pan_id = wpan_dev->pan_id;
 
 		spin_unlock_bh(&sdata->mib_lock);
 	} else {
@@ -394,42 +406,48 @@
 }
 
 static int
-ieee802154_setup_sdata(struct ieee802154_sub_if_data *sdata, int type)
+ieee802154_setup_sdata(struct ieee802154_sub_if_data *sdata,
+		       enum nl802154_iftype type)
 {
+	struct wpan_dev *wpan_dev = &sdata->wpan_dev;
+
 	/* set some type-dependent values */
 	sdata->vif.type = type;
+	sdata->wpan_dev.iftype = type;
 
-	get_random_bytes(&sdata->bsn, 1);
-	get_random_bytes(&sdata->dsn, 1);
+	get_random_bytes(&wpan_dev->bsn, 1);
+	get_random_bytes(&wpan_dev->dsn, 1);
 
 	/* defaults per 802.15.4-2011 */
-	sdata->mac_params.min_be = 3;
-	sdata->mac_params.max_be = 5;
-	sdata->mac_params.csma_retries = 4;
+	wpan_dev->min_be = 3;
+	wpan_dev->max_be = 5;
+	wpan_dev->csma_retries = 4;
 	/* for compatibility, actual default is 3 */
-	sdata->mac_params.frame_retries = -1;
+	wpan_dev->frame_retries = -1;
 
-	ieee802154_be64_to_le64(&sdata->extended_addr, sdata->dev->dev_addr);
-	sdata->pan_id = cpu_to_le16(IEEE802154_PANID_BROADCAST);
-	sdata->short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST);
+	wpan_dev->pan_id = cpu_to_le16(IEEE802154_PANID_BROADCAST);
+	wpan_dev->short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST);
 
 	switch (type) {
-	case IEEE802154_DEV_WPAN:
+	case NL802154_IFTYPE_NODE:
+		ieee802154_be64_to_le64(&wpan_dev->extended_addr,
+					sdata->dev->dev_addr);
+
 		sdata->dev->header_ops = &mac802154_header_ops;
 		sdata->dev->destructor = mac802154_wpan_free;
 		sdata->dev->netdev_ops = &mac802154_wpan_ops;
 		sdata->dev->ml_priv = &mac802154_mlme_wpan;
-		sdata->promiscuous_mode = false;
+		wpan_dev->promiscuous_mode = false;
 
 		spin_lock_init(&sdata->mib_lock);
 		mutex_init(&sdata->sec_mtx);
 
 		mac802154_llsec_init(&sdata->sec);
 		break;
-	case IEEE802154_DEV_MONITOR:
+	case NL802154_IFTYPE_MONITOR:
 		sdata->dev->destructor = free_netdev;
 		sdata->dev->netdev_ops = &mac802154_monitor_ops;
-		sdata->promiscuous_mode = true;
+		wpan_dev->promiscuous_mode = true;
 		break;
 	default:
 		BUG();
@@ -440,7 +458,7 @@
 
 struct net_device *
 ieee802154_if_add(struct ieee802154_local *local, const char *name,
-		  struct wpan_dev **new_wpan_dev, int type)
+		  enum nl802154_iftype type, __le64 extended_addr)
 {
 	struct net_device *ndev = NULL;
 	struct ieee802154_sub_if_data *sdata = NULL;
@@ -459,11 +477,18 @@
 	if (ret < 0)
 		goto err;
 
+	ieee802154_le64_to_be64(ndev->perm_addr,
+				&local->hw.phy->perm_extended_addr);
 	switch (type) {
-	case IEEE802154_DEV_WPAN:
+	case NL802154_IFTYPE_NODE:
 		ndev->type = ARPHRD_IEEE802154;
+		if (ieee802154_is_valid_extended_addr(extended_addr))
+			ieee802154_le64_to_be64(ndev->dev_addr, &extended_addr);
+		else
+			memcpy(ndev->dev_addr, ndev->perm_addr,
+			       IEEE802154_EXTENDED_ADDR_LEN);
 		break;
-	case IEEE802154_DEV_MONITOR:
+	case NL802154_IFTYPE_MONITOR:
 		ndev->type = ARPHRD_IEEE802154_MONITOR;
 		break;
 	default:
@@ -471,9 +496,6 @@
 		goto err;
 	}
 
-	ieee802154_le64_to_be64(ndev->perm_addr,
-				&local->hw.phy->perm_extended_addr);
-	memcpy(ndev->dev_addr, ndev->perm_addr, IEEE802154_EXTENDED_ADDR_LEN);
 	/* TODO check this */
 	SET_NETDEV_DEV(ndev, &local->phy->dev);
 	sdata = netdev_priv(ndev);
@@ -488,19 +510,14 @@
 	if (ret)
 		goto err;
 
-	if (ndev) {
-		ret = register_netdevice(ndev);
-		if (ret < 0)
-			goto err;
-	}
+	ret = register_netdevice(ndev);
+	if (ret < 0)
+		goto err;
 
 	mutex_lock(&local->iflist_mtx);
 	list_add_tail_rcu(&sdata->list, &local->interfaces);
 	mutex_unlock(&local->iflist_mtx);
 
-	if (new_wpan_dev)
-		*new_wpan_dev = &sdata->wpan_dev;
-
 	return ndev;
 
 err:
@@ -519,3 +536,51 @@
 	synchronize_rcu();
 	unregister_netdevice(sdata->dev);
 }
+
+void ieee802154_remove_interfaces(struct ieee802154_local *local)
+{
+	struct ieee802154_sub_if_data *sdata, *tmp;
+
+	mutex_lock(&local->iflist_mtx);
+	list_for_each_entry_safe(sdata, tmp, &local->interfaces, list) {
+		list_del(&sdata->list);
+
+		unregister_netdevice(sdata->dev);
+	}
+	mutex_unlock(&local->iflist_mtx);
+}
+
+static int netdev_notify(struct notifier_block *nb,
+			 unsigned long state, void *ptr)
+{
+	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+	struct ieee802154_sub_if_data *sdata;
+
+	if (state != NETDEV_CHANGENAME)
+		return NOTIFY_DONE;
+
+	if (!dev->ieee802154_ptr || !dev->ieee802154_ptr->wpan_phy)
+		return NOTIFY_DONE;
+
+	if (dev->ieee802154_ptr->wpan_phy->privid != mac802154_wpan_phy_privid)
+		return NOTIFY_DONE;
+
+	sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+	memcpy(sdata->name, dev->name, IFNAMSIZ);
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block mac802154_netdev_notifier = {
+	.notifier_call = netdev_notify,
+};
+
+int ieee802154_iface_init(void)
+{
+	return register_netdevice_notifier(&mac802154_netdev_notifier);
+}
+
+void ieee802154_iface_exit(void)
+{
+	unregister_netdevice_notifier(&mac802154_netdev_notifier);
+}
diff --git a/net/mac802154/mac_cmd.c b/net/mac802154/mac_cmd.c
index 00b2b21..6aacb18 100644
--- a/net/mac802154/mac_cmd.c
+++ b/net/mac802154/mac_cmd.c
@@ -39,6 +39,8 @@
 	struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
 	int rc = 0;
 
+	ASSERT_RTNL();
+
 	BUG_ON(addr->mode != IEEE802154_ADDR_SHORT);
 
 	mac802154_dev_set_pan_id(dev, addr->pan_id);
@@ -72,11 +74,22 @@
 {
 	struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
 	struct ieee802154_local *local = sdata->local;
+	struct wpan_dev *wpan_dev = &sdata->wpan_dev;
 	int ret;
 
-	mutex_lock(&sdata->local->iflist_mtx);
-	sdata->mac_params = *params;
-	mutex_unlock(&sdata->local->iflist_mtx);
+	ASSERT_RTNL();
+
+	/* PHY */
+	wpan_dev->wpan_phy->transmit_power = params->transmit_power;
+	wpan_dev->wpan_phy->cca_mode = params->cca_mode;
+	wpan_dev->wpan_phy->cca_ed_level = params->cca_ed_level;
+
+	/* MAC */
+	wpan_dev->min_be = params->min_be;
+	wpan_dev->max_be = params->max_be;
+	wpan_dev->csma_retries = params->csma_retries;
+	wpan_dev->frame_retries = params->frame_retries;
+	wpan_dev->lbt = params->lbt;
 
 	if (local->hw.flags & IEEE802154_HW_TXPOWER) {
 		ret = drv_set_tx_power(local, params->transmit_power);
@@ -103,10 +116,21 @@
 				     struct ieee802154_mac_params *params)
 {
 	struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+	struct wpan_dev *wpan_dev = &sdata->wpan_dev;
 
-	mutex_lock(&sdata->local->iflist_mtx);
-	*params = sdata->mac_params;
-	mutex_unlock(&sdata->local->iflist_mtx);
+	ASSERT_RTNL();
+
+	/* PHY */
+	params->transmit_power = wpan_dev->wpan_phy->transmit_power;
+	params->cca_mode = wpan_dev->wpan_phy->cca_mode;
+	params->cca_ed_level = wpan_dev->wpan_phy->cca_ed_level;
+
+	/* MAC */
+	params->min_be = wpan_dev->min_be;
+	params->max_be = wpan_dev->max_be;
+	params->csma_retries = wpan_dev->csma_retries;
+	params->frame_retries = wpan_dev->frame_retries;
+	params->lbt = wpan_dev->lbt;
 }
 
 static struct ieee802154_llsec_ops mac802154_llsec_ops = {
diff --git a/net/mac802154/main.c b/net/mac802154/main.c
index 7d0ff7f..8500378 100644
--- a/net/mac802154/main.c
+++ b/net/mac802154/main.c
@@ -4,8 +4,6 @@
  * Written by:
  * Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
  *
- * Based on the code from 'linux-zigbee.sourceforge.net' project.
- *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2
  * as published by the Free Software Foundation.
@@ -21,7 +19,7 @@
 #include <linux/netdevice.h>
 
 #include <net/netlink.h>
-#include <linux/nl802154.h>
+#include <net/nl802154.h>
 #include <net/mac802154.h>
 #include <net/ieee802154_netdev.h>
 #include <net/route.h>
@@ -86,12 +84,14 @@
 
 	priv_size = ALIGN(sizeof(*local), NETDEV_ALIGN) + priv_data_len;
 
-	phy = wpan_phy_alloc(&mac802154_config_ops, priv_size);
+	phy = wpan_phy_new(&mac802154_config_ops, priv_size);
 	if (!phy) {
 		pr_err("failure to allocate master IEEE802.15.4 device\n");
 		return NULL;
 	}
 
+	phy->privid = mac802154_wpan_phy_privid;
+
 	local = wpan_phy_priv(phy);
 	local->phy = phy;
 	local->hw.phy = local->phy;
@@ -123,6 +123,18 @@
 }
 EXPORT_SYMBOL(ieee802154_free_hw);
 
+static void ieee802154_setup_wpan_phy_pib(struct wpan_phy *wpan_phy)
+{
+	/* TODO warn on empty symbol_duration
+	 * Should be done when all drivers sets this value.
+	 */
+
+	wpan_phy->lifs_period = IEEE802154_LIFS_PERIOD *
+				wpan_phy->symbol_duration;
+	wpan_phy->sifs_period = IEEE802154_SIFS_PERIOD *
+				wpan_phy->symbol_duration;
+}
+
 int ieee802154_register_hw(struct ieee802154_hw *hw)
 {
 	struct ieee802154_local *local = hw_to_local(hw);
@@ -136,15 +148,21 @@
 		goto out;
 	}
 
+	hrtimer_init(&local->ifs_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	local->ifs_timer.function = ieee802154_xmit_ifs_timer;
+
 	wpan_phy_set_dev(local->phy, local->hw.parent);
 
+	ieee802154_setup_wpan_phy_pib(local->phy);
+
 	rc = wpan_phy_register(local->phy);
 	if (rc < 0)
 		goto out_wq;
 
 	rtnl_lock();
 
-	dev = ieee802154_if_add(local, "wpan%d", NULL, IEEE802154_DEV_WPAN);
+	dev = ieee802154_if_add(local, "wpan%d", NL802154_IFTYPE_NODE,
+				cpu_to_le64(0x0000000000000000ULL));
 	if (IS_ERR(dev)) {
 		rtnl_unlock();
 		rc = PTR_ERR(dev);
@@ -165,7 +183,6 @@
 void ieee802154_unregister_hw(struct ieee802154_hw *hw)
 {
 	struct ieee802154_local *local = hw_to_local(hw);
-	struct ieee802154_sub_if_data *sdata, *next;
 
 	tasklet_kill(&local->tasklet);
 	flush_workqueue(local->workqueue);
@@ -173,13 +190,7 @@
 
 	rtnl_lock();
 
-	list_for_each_entry_safe(sdata, next, &local->interfaces, list) {
-		mutex_lock(&sdata->local->iflist_mtx);
-		list_del(&sdata->list);
-		mutex_unlock(&sdata->local->iflist_mtx);
-
-		unregister_netdevice(sdata->dev);
-	}
+	ieee802154_remove_interfaces(local);
 
 	rtnl_unlock();
 
@@ -187,5 +198,20 @@
 }
 EXPORT_SYMBOL(ieee802154_unregister_hw);
 
-MODULE_DESCRIPTION("IEEE 802.15.4 implementation");
+static int __init ieee802154_init(void)
+{
+	return ieee802154_iface_init();
+}
+
+static void __exit ieee802154_exit(void)
+{
+	ieee802154_iface_exit();
+
+	rcu_barrier();
+}
+
+subsys_initcall(ieee802154_init);
+module_exit(ieee802154_exit);
+
+MODULE_DESCRIPTION("IEEE 802.15.4 subsystem");
 MODULE_LICENSE("GPL v2");
diff --git a/net/mac802154/mib.c b/net/mac802154/mib.c
index 6fa7491..3596b29 100644
--- a/net/mac802154/mib.c
+++ b/net/mac802154/mib.c
@@ -33,7 +33,7 @@
 	BUG_ON(dev->type != ARPHRD_IEEE802154);
 
 	spin_lock_bh(&sdata->mib_lock);
-	sdata->short_addr = val;
+	sdata->wpan_dev.short_addr = val;
 	spin_unlock_bh(&sdata->mib_lock);
 }
 
@@ -45,7 +45,7 @@
 	BUG_ON(dev->type != ARPHRD_IEEE802154);
 
 	spin_lock_bh(&sdata->mib_lock);
-	ret = sdata->short_addr;
+	ret = sdata->wpan_dev.short_addr;
 	spin_unlock_bh(&sdata->mib_lock);
 
 	return ret;
@@ -59,7 +59,7 @@
 	BUG_ON(dev->type != ARPHRD_IEEE802154);
 
 	spin_lock_bh(&sdata->mib_lock);
-	ret = sdata->pan_id;
+	ret = sdata->wpan_dev.pan_id;
 	spin_unlock_bh(&sdata->mib_lock);
 
 	return ret;
@@ -72,7 +72,7 @@
 	BUG_ON(dev->type != ARPHRD_IEEE802154);
 
 	spin_lock_bh(&sdata->mib_lock);
-	sdata->pan_id = val;
+	sdata->wpan_dev.pan_id = val;
 	spin_unlock_bh(&sdata->mib_lock);
 }
 
@@ -82,7 +82,7 @@
 
 	BUG_ON(dev->type != ARPHRD_IEEE802154);
 
-	return sdata->dsn++;
+	return sdata->wpan_dev.dsn++;
 }
 
 void mac802154_dev_set_page_channel(struct net_device *dev, u8 page, u8 chan)
diff --git a/net/mac802154/rx.c b/net/mac802154/rx.c
index 4b54cf3..041dbd5 100644
--- a/net/mac802154/rx.c
+++ b/net/mac802154/rx.c
@@ -25,8 +25,7 @@
 
 #include <net/mac802154.h>
 #include <net/ieee802154_netdev.h>
-#include <net/rtnetlink.h>
-#include <linux/nl802154.h>
+#include <net/nl802154.h>
 
 #include "ieee802154_i.h"
 
@@ -42,6 +41,7 @@
 ieee802154_subif_frame(struct ieee802154_sub_if_data *sdata,
 		       struct sk_buff *skb, const struct ieee802154_hdr *hdr)
 {
+	struct wpan_dev *wpan_dev = &sdata->wpan_dev;
 	__le16 span, sshort;
 	int rc;
 
@@ -49,8 +49,8 @@
 
 	spin_lock_bh(&sdata->mib_lock);
 
-	span = sdata->pan_id;
-	sshort = sdata->short_addr;
+	span = wpan_dev->pan_id;
+	sshort = wpan_dev->short_addr;
 
 	switch (mac_cb(skb)->dest.mode) {
 	case IEEE802154_ADDR_NONE:
@@ -65,7 +65,7 @@
 		if (mac_cb(skb)->dest.pan_id != span &&
 		    mac_cb(skb)->dest.pan_id != cpu_to_le16(IEEE802154_PANID_BROADCAST))
 			skb->pkt_type = PACKET_OTHERHOST;
-		else if (mac_cb(skb)->dest.extended_addr == sdata->extended_addr)
+		else if (mac_cb(skb)->dest.extended_addr == wpan_dev->extended_addr)
 			skb->pkt_type = PACKET_HOST;
 		else
 			skb->pkt_type = PACKET_OTHERHOST;
@@ -208,7 +208,7 @@
 	}
 
 	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
-		if (sdata->vif.type != IEEE802154_DEV_WPAN ||
+		if (sdata->vif.type != NL802154_IFTYPE_NODE ||
 		    !netif_running(sdata->dev))
 			continue;
 
@@ -233,7 +233,7 @@
 	skb->protocol = htons(ETH_P_IEEE802154);
 
 	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
-		if (sdata->vif.type != IEEE802154_DEV_MONITOR)
+		if (sdata->vif.type != NL802154_IFTYPE_MONITOR)
 			continue;
 
 		if (!ieee802154_sdata_running(sdata))
diff --git a/net/mac802154/tx.c b/net/mac802154/tx.c
index cc37b77..c62e956 100644
--- a/net/mac802154/tx.c
+++ b/net/mac802154/tx.c
@@ -60,7 +60,7 @@
 	if (res)
 		goto err_tx;
 
-	ieee802154_xmit_complete(&local->hw, skb);
+	ieee802154_xmit_complete(&local->hw, skb, false);
 
 	dev->stats.tx_packets++;
 	dev->stats.tx_bytes += skb->len;
diff --git a/net/mac802154/util.c b/net/mac802154/util.c
index 117e4ef..5fc9790 100644
--- a/net/mac802154/util.c
+++ b/net/mac802154/util.c
@@ -15,6 +15,9 @@
 
 #include "ieee802154_i.h"
 
+/* privid for wpan_phys to determine whether they belong to us or not */
+const void *const mac802154_wpan_phy_privid = &mac802154_wpan_phy_privid;
+
 void ieee802154_wake_queue(struct ieee802154_hw *hw)
 {
 	struct ieee802154_local *local = hw_to_local(hw);
@@ -47,9 +50,35 @@
 }
 EXPORT_SYMBOL(ieee802154_stop_queue);
 
-void ieee802154_xmit_complete(struct ieee802154_hw *hw, struct sk_buff *skb)
+enum hrtimer_restart ieee802154_xmit_ifs_timer(struct hrtimer *timer)
 {
-	ieee802154_wake_queue(hw);
-	consume_skb(skb);
+	struct ieee802154_local *local =
+		container_of(timer, struct ieee802154_local, ifs_timer);
+
+	ieee802154_wake_queue(&local->hw);
+
+	return HRTIMER_NORESTART;
+}
+
+void ieee802154_xmit_complete(struct ieee802154_hw *hw, struct sk_buff *skb,
+			      bool ifs_handling)
+{
+	if (ifs_handling) {
+		struct ieee802154_local *local = hw_to_local(hw);
+
+		if (skb->len > 18)
+			hrtimer_start(&local->ifs_timer,
+				      ktime_set(0, hw->phy->lifs_period * NSEC_PER_USEC),
+				      HRTIMER_MODE_REL);
+		else
+			hrtimer_start(&local->ifs_timer,
+				      ktime_set(0, hw->phy->sifs_period * NSEC_PER_USEC),
+				      HRTIMER_MODE_REL);
+
+		consume_skb(skb);
+	} else {
+		ieee802154_wake_queue(hw);
+		consume_skb(skb);
+	}
 }
 EXPORT_SYMBOL(ieee802154_xmit_complete);
diff --git a/net/nfc/digital_dep.c b/net/nfc/digital_dep.c
index b60aa35..f72be74 100644
--- a/net/nfc/digital_dep.c
+++ b/net/nfc/digital_dep.c
@@ -17,6 +17,9 @@
 
 #include "digital.h"
 
+#define DIGITAL_NFC_DEP_N_RETRY_NACK	2
+#define DIGITAL_NFC_DEP_N_RETRY_ATN	2
+
 #define DIGITAL_NFC_DEP_FRAME_DIR_OUT 0xD4
 #define DIGITAL_NFC_DEP_FRAME_DIR_IN  0xD5
 
@@ -32,20 +35,32 @@
 #define DIGITAL_ATR_REQ_MIN_SIZE 16
 #define DIGITAL_ATR_REQ_MAX_SIZE 64
 
-#define DIGITAL_LR_BITS_PAYLOAD_SIZE_254B 0x30
-#define DIGITAL_FSL_BITS_PAYLOAD_SIZE_254B \
-				(DIGITAL_LR_BITS_PAYLOAD_SIZE_254B >> 4)
+#define DIGITAL_DID_MAX	14
+
+#define DIGITAL_PAYLOAD_SIZE_MAX	254
+#define DIGITAL_PAYLOAD_BITS_TO_PP(s)	(((s) & 0x3) << 4)
+#define DIGITAL_PAYLOAD_PP_TO_BITS(s)	(((s) >> 4) & 0x3)
+#define DIGITAL_PAYLOAD_BITS_TO_FSL(s)	((s) & 0x3)
+#define DIGITAL_PAYLOAD_FSL_TO_BITS(s)	((s) & 0x3)
+
 #define DIGITAL_GB_BIT	0x02
 
+#define DIGITAL_NFC_DEP_REQ_RES_HEADROOM	2 /* SoD: [SB (NFC-A)] + LEN */
+#define DIGITAL_NFC_DEP_REQ_RES_TAILROOM	2 /* EoD: 2-byte CRC */
+
 #define DIGITAL_NFC_DEP_PFB_TYPE(pfb) ((pfb) & 0xE0)
 
 #define DIGITAL_NFC_DEP_PFB_TIMEOUT_BIT 0x10
+#define DIGITAL_NFC_DEP_PFB_MI_BIT	0x10
+#define DIGITAL_NFC_DEP_PFB_NACK_BIT	0x10
+#define DIGITAL_NFC_DEP_PFB_DID_BIT	0x04
 
 #define DIGITAL_NFC_DEP_PFB_IS_TIMEOUT(pfb) \
 				((pfb) & DIGITAL_NFC_DEP_PFB_TIMEOUT_BIT)
-#define DIGITAL_NFC_DEP_MI_BIT_SET(pfb)  ((pfb) & 0x10)
+#define DIGITAL_NFC_DEP_MI_BIT_SET(pfb)  ((pfb) & DIGITAL_NFC_DEP_PFB_MI_BIT)
+#define DIGITAL_NFC_DEP_NACK_BIT_SET(pfb) ((pfb) & DIGITAL_NFC_DEP_PFB_NACK_BIT)
 #define DIGITAL_NFC_DEP_NAD_BIT_SET(pfb) ((pfb) & 0x08)
-#define DIGITAL_NFC_DEP_DID_BIT_SET(pfb) ((pfb) & 0x04)
+#define DIGITAL_NFC_DEP_DID_BIT_SET(pfb) ((pfb) & DIGITAL_NFC_DEP_PFB_DID_BIT)
 #define DIGITAL_NFC_DEP_PFB_PNI(pfb)     ((pfb) & 0x03)
 
 #define DIGITAL_NFC_DEP_PFB_I_PDU          0x00
@@ -97,6 +112,34 @@
 
 static void digital_in_recv_dep_res(struct nfc_digital_dev *ddev, void *arg,
 				    struct sk_buff *resp);
+static void digital_tg_recv_dep_req(struct nfc_digital_dev *ddev, void *arg,
+				    struct sk_buff *resp);
+
+static const u8 digital_payload_bits_map[4] = {
+	[0] = 64,
+	[1] = 128,
+	[2] = 192,
+	[3] = 254
+};
+
+static u8 digital_payload_bits_to_size(u8 payload_bits)
+{
+	if (payload_bits >= ARRAY_SIZE(digital_payload_bits_map))
+		return 0;
+
+	return digital_payload_bits_map[payload_bits];
+}
+
+static u8 digital_payload_size_to_bits(u8 payload_size)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(digital_payload_bits_map); i++)
+		if (digital_payload_bits_map[i] == payload_size)
+			return i;
+
+	return 0xff;
+}
 
 static void digital_skb_push_dep_sod(struct nfc_digital_dev *ddev,
 				     struct sk_buff *skb)
@@ -129,6 +172,106 @@
 	return 0;
 }
 
+static struct sk_buff *
+digital_send_dep_data_prep(struct nfc_digital_dev *ddev, struct sk_buff *skb,
+			   struct digital_dep_req_res *dep_req_res,
+			   struct digital_data_exch *data_exch)
+{
+	struct sk_buff *new_skb;
+
+	if (skb->len > ddev->remote_payload_max) {
+		dep_req_res->pfb |= DIGITAL_NFC_DEP_PFB_MI_BIT;
+
+		new_skb = digital_skb_alloc(ddev, ddev->remote_payload_max);
+		if (!new_skb) {
+			kfree_skb(ddev->chaining_skb);
+			ddev->chaining_skb = NULL;
+
+			return ERR_PTR(-ENOMEM);
+		}
+
+		skb_reserve(new_skb, ddev->tx_headroom + NFC_HEADER_SIZE +
+					DIGITAL_NFC_DEP_REQ_RES_HEADROOM);
+		memcpy(skb_put(new_skb, ddev->remote_payload_max), skb->data,
+		       ddev->remote_payload_max);
+		skb_pull(skb, ddev->remote_payload_max);
+
+		ddev->chaining_skb = skb;
+		ddev->data_exch = data_exch;
+	} else {
+		ddev->chaining_skb = NULL;
+		new_skb = skb;
+	}
+
+	return new_skb;
+}
+
+static struct sk_buff *
+digital_recv_dep_data_gather(struct nfc_digital_dev *ddev, u8 pfb,
+			     struct sk_buff *resp,
+			     int (*send_ack)(struct nfc_digital_dev *ddev,
+					     struct digital_data_exch
+							     *data_exch),
+			     struct digital_data_exch *data_exch)
+{
+	struct sk_buff *new_skb;
+	int rc;
+
+	if (DIGITAL_NFC_DEP_MI_BIT_SET(pfb) && (!ddev->chaining_skb)) {
+		ddev->chaining_skb =
+			nfc_alloc_recv_skb(8 * ddev->local_payload_max,
+					   GFP_KERNEL);
+		if (!ddev->chaining_skb) {
+			rc = -ENOMEM;
+			goto error;
+		}
+	}
+
+	if (ddev->chaining_skb) {
+		if (resp->len > skb_tailroom(ddev->chaining_skb)) {
+			new_skb = skb_copy_expand(ddev->chaining_skb,
+						  skb_headroom(
+							  ddev->chaining_skb),
+						  8 * ddev->local_payload_max,
+						  GFP_KERNEL);
+			if (!new_skb) {
+				rc = -ENOMEM;
+				goto error;
+			}
+
+			kfree_skb(ddev->chaining_skb);
+			ddev->chaining_skb = new_skb;
+		}
+
+		memcpy(skb_put(ddev->chaining_skb, resp->len), resp->data,
+		       resp->len);
+
+		kfree_skb(resp);
+		resp = NULL;
+
+		if (DIGITAL_NFC_DEP_MI_BIT_SET(pfb)) {
+			rc = send_ack(ddev, data_exch);
+			if (rc)
+				goto error;
+
+			return NULL;
+		}
+
+		resp = ddev->chaining_skb;
+		ddev->chaining_skb = NULL;
+	}
+
+	return resp;
+
+error:
+	kfree_skb(resp);
+
+	kfree_skb(ddev->chaining_skb);
+	ddev->chaining_skb = NULL;
+
+	return ERR_PTR(rc);
+}
+
 static void digital_in_recv_psl_res(struct nfc_digital_dev *ddev, void *arg,
 				    struct sk_buff *resp)
 {
@@ -198,6 +341,8 @@
 {
 	struct sk_buff *skb;
 	struct digital_psl_req *psl_req;
+	int rc;
+	u8 payload_size, payload_bits;
 
 	skb = digital_skb_alloc(ddev, sizeof(*psl_req));
 	if (!skb)
@@ -211,14 +356,24 @@
 	psl_req->cmd = DIGITAL_CMD_PSL_REQ;
 	psl_req->did = 0;
 	psl_req->brs = (0x2 << 3) | 0x2; /* 424F both directions */
-	psl_req->fsl = DIGITAL_FSL_BITS_PAYLOAD_SIZE_254B;
+
+	payload_size = min(ddev->local_payload_max, ddev->remote_payload_max);
+	payload_bits = digital_payload_size_to_bits(payload_size);
+	psl_req->fsl = DIGITAL_PAYLOAD_BITS_TO_FSL(payload_bits);
+
+	ddev->local_payload_max = payload_size;
+	ddev->remote_payload_max = payload_size;
 
 	digital_skb_push_dep_sod(ddev, skb);
 
 	ddev->skb_add_crc(skb);
 
-	return digital_in_send_cmd(ddev, skb, 500, digital_in_recv_psl_res,
-				   target);
+	rc = digital_in_send_cmd(ddev, skb, 500, digital_in_recv_psl_res,
+				 target);
+	if (rc)
+		kfree_skb(skb);
+
+	return rc;
 }
 
 static void digital_in_recv_atr_res(struct nfc_digital_dev *ddev, void *arg,
@@ -226,7 +381,7 @@
 {
 	struct nfc_target *target = arg;
 	struct digital_atr_res *atr_res;
-	u8 gb_len;
+	u8 gb_len, payload_bits;
 	int rc;
 
 	if (IS_ERR(resp)) {
@@ -256,6 +411,14 @@
 
 	atr_res = (struct digital_atr_res *)resp->data;
 
+	payload_bits = DIGITAL_PAYLOAD_PP_TO_BITS(atr_res->pp);
+	ddev->remote_payload_max = digital_payload_bits_to_size(payload_bits);
+
+	if (!ddev->remote_payload_max) {
+		rc = -EINVAL;
+		goto exit;
+	}
+
 	rc = nfc_set_remote_general_bytes(ddev->nfc_dev, atr_res->gb, gb_len);
 	if (rc)
 		goto exit;
@@ -286,6 +449,8 @@
 	struct sk_buff *skb;
 	struct digital_atr_req *atr_req;
 	uint size;
+	int rc;
+	u8 payload_bits;
 
 	size = DIGITAL_ATR_REQ_MIN_SIZE + gb_len;
 
@@ -314,7 +479,9 @@
 	atr_req->bs = 0;
 	atr_req->br = 0;
 
-	atr_req->pp = DIGITAL_LR_BITS_PAYLOAD_SIZE_254B;
+	ddev->local_payload_max = DIGITAL_PAYLOAD_SIZE_MAX;
+	payload_bits = digital_payload_size_to_bits(ddev->local_payload_max);
+	atr_req->pp = DIGITAL_PAYLOAD_BITS_TO_PP(payload_bits);
 
 	if (gb_len) {
 		atr_req->pp |= DIGITAL_GB_BIT;
@@ -325,8 +492,113 @@
 
 	ddev->skb_add_crc(skb);
 
-	return digital_in_send_cmd(ddev, skb, 500, digital_in_recv_atr_res,
-				   target);
+	rc = digital_in_send_cmd(ddev, skb, 500, digital_in_recv_atr_res,
+				 target);
+	if (rc)
+		kfree_skb(skb);
+
+	return rc;
+}
+
+static int digital_in_send_ack(struct nfc_digital_dev *ddev,
+			       struct digital_data_exch *data_exch)
+{
+	struct digital_dep_req_res *dep_req;
+	struct sk_buff *skb;
+	int rc;
+
+	skb = digital_skb_alloc(ddev, 1);
+	if (!skb)
+		return -ENOMEM;
+
+	skb_push(skb, sizeof(struct digital_dep_req_res));
+
+	dep_req = (struct digital_dep_req_res *)skb->data;
+
+	dep_req->dir = DIGITAL_NFC_DEP_FRAME_DIR_OUT;
+	dep_req->cmd = DIGITAL_CMD_DEP_REQ;
+	dep_req->pfb = DIGITAL_NFC_DEP_PFB_ACK_NACK_PDU |
+		       ddev->curr_nfc_dep_pni;
+
+	digital_skb_push_dep_sod(ddev, skb);
+
+	ddev->skb_add_crc(skb);
+
+	ddev->saved_skb = skb_get(skb);
+	ddev->saved_skb_len = skb->len;
+
+	rc = digital_in_send_cmd(ddev, skb, 1500, digital_in_recv_dep_res,
+				 data_exch);
+	if (rc) {
+		kfree_skb(skb);
+		kfree_skb(ddev->saved_skb);
+		ddev->saved_skb = NULL;
+	}
+
+	return rc;
+}
+
+static int digital_in_send_nack(struct nfc_digital_dev *ddev,
+				struct digital_data_exch *data_exch)
+{
+	struct digital_dep_req_res *dep_req;
+	struct sk_buff *skb;
+	int rc;
+
+	skb = digital_skb_alloc(ddev, 1);
+	if (!skb)
+		return -ENOMEM;
+
+	skb_push(skb, sizeof(struct digital_dep_req_res));
+
+	dep_req = (struct digital_dep_req_res *)skb->data;
+
+	dep_req->dir = DIGITAL_NFC_DEP_FRAME_DIR_OUT;
+	dep_req->cmd = DIGITAL_CMD_DEP_REQ;
+	dep_req->pfb = DIGITAL_NFC_DEP_PFB_ACK_NACK_PDU |
+		       DIGITAL_NFC_DEP_PFB_NACK_BIT | ddev->curr_nfc_dep_pni;
+
+	digital_skb_push_dep_sod(ddev, skb);
+
+	ddev->skb_add_crc(skb);
+
+	rc = digital_in_send_cmd(ddev, skb, 1500, digital_in_recv_dep_res,
+				 data_exch);
+	if (rc)
+		kfree_skb(skb);
+
+	return rc;
+}
+
+static int digital_in_send_atn(struct nfc_digital_dev *ddev,
+			       struct digital_data_exch *data_exch)
+{
+	struct digital_dep_req_res *dep_req;
+	struct sk_buff *skb;
+	int rc;
+
+	skb = digital_skb_alloc(ddev, 1);
+	if (!skb)
+		return -ENOMEM;
+
+	skb_push(skb, sizeof(struct digital_dep_req_res));
+
+	dep_req = (struct digital_dep_req_res *)skb->data;
+
+	dep_req->dir = DIGITAL_NFC_DEP_FRAME_DIR_OUT;
+	dep_req->cmd = DIGITAL_CMD_DEP_REQ;
+	dep_req->pfb = DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU;
+
+	digital_skb_push_dep_sod(ddev, skb);
+
+	ddev->skb_add_crc(skb);
+
+	rc = digital_in_send_cmd(ddev, skb, 1500, digital_in_recv_dep_res,
+				 data_exch);
+	if (rc)
+		kfree_skb(skb);
+
+	return rc;
 }
 
 static int digital_in_send_rtox(struct nfc_digital_dev *ddev,
@@ -355,12 +627,30 @@
 
 	ddev->skb_add_crc(skb);
 
+	ddev->saved_skb = skb_get(skb);
+	ddev->saved_skb_len = skb->len;
+
 	rc = digital_in_send_cmd(ddev, skb, 1500, digital_in_recv_dep_res,
 				 data_exch);
+	if (rc) {
+		kfree_skb(skb);
+		kfree_skb(ddev->saved_skb);
+		ddev->saved_skb = NULL;
+	}
 
 	return rc;
 }
 
+static int digital_in_send_saved_skb(struct nfc_digital_dev *ddev,
+				     struct digital_data_exch *data_exch)
+{
+	skb_get(ddev->saved_skb);
+	skb_push(ddev->saved_skb, ddev->saved_skb_len);
+
+	return digital_in_send_cmd(ddev, ddev->saved_skb, 1500,
+				   digital_in_recv_dep_res, data_exch);
+}
+
 static void digital_in_recv_dep_res(struct nfc_digital_dev *ddev, void *arg,
 				    struct sk_buff *resp)
 {
@@ -373,13 +663,28 @@
 	if (IS_ERR(resp)) {
 		rc = PTR_ERR(resp);
 		resp = NULL;
-		goto exit;
-	}
 
-	rc = ddev->skb_check_crc(resp);
-	if (rc) {
-		PROTOCOL_ERR("14.4.1.6");
-		goto error;
+		if (((rc != -ETIMEDOUT) || ddev->nack_count) &&
+		    (ddev->nack_count++ < DIGITAL_NFC_DEP_N_RETRY_NACK)) {
+			ddev->atn_count = 0;
+
+			rc = digital_in_send_nack(ddev, data_exch);
+			if (rc)
+				goto error;
+
+			return;
+		} else if ((rc == -ETIMEDOUT) &&
+			   (ddev->atn_count++ < DIGITAL_NFC_DEP_N_RETRY_ATN)) {
+			ddev->nack_count = 0;
+
+			rc = digital_in_send_atn(ddev, data_exch);
+			if (rc)
+				goto error;
+
+			return;
+		}
+
+		goto exit;
 	}
 
 	rc = digital_skb_pull_dep_sod(ddev, resp);
@@ -388,10 +693,37 @@
 		goto exit;
 	}
 
+	rc = ddev->skb_check_crc(resp);
+	if (rc) {
+		if ((resp->len >= 4) &&
+		    (ddev->nack_count++ < DIGITAL_NFC_DEP_N_RETRY_NACK)) {
+			ddev->atn_count = 0;
+
+			rc = digital_in_send_nack(ddev, data_exch);
+			if (rc)
+				goto error;
+
+			kfree_skb(resp);
+
+			return;
+		}
+
+		PROTOCOL_ERR("14.4.1.6");
+		goto error;
+	}
+
+	ddev->atn_count = 0;
+	ddev->nack_count = 0;
+
+	if (resp->len > ddev->local_payload_max) {
+		rc = -EMSGSIZE;
+		goto exit;
+	}
+
+	size = sizeof(struct digital_dep_req_res);
 	dep_res = (struct digital_dep_req_res *)resp->data;
 
-	if (resp->len < sizeof(struct digital_dep_req_res) ||
-	    dep_res->dir != DIGITAL_NFC_DEP_FRAME_DIR_IN ||
+	if (resp->len < size || dep_res->dir != DIGITAL_NFC_DEP_FRAME_DIR_IN ||
 	    dep_res->cmd != DIGITAL_CMD_DEP_RES) {
 		rc = -EIO;
 		goto error;
@@ -399,6 +731,24 @@
 
 	pfb = dep_res->pfb;
 
+	if (DIGITAL_NFC_DEP_DID_BIT_SET(pfb)) {
+		PROTOCOL_ERR("14.8.2.1");
+		rc = -EIO;
+		goto error;
+	}
+
+	if (DIGITAL_NFC_DEP_NAD_BIT_SET(pfb)) {
+		rc = -EIO;
+		goto exit;
+	}
+
+	if (size > resp->len) {
+		rc = -EIO;
+		goto error;
+	}
+
+	skb_pull(resp, size);
+
 	switch (DIGITAL_NFC_DEP_PFB_TYPE(pfb)) {
 	case DIGITAL_NFC_DEP_PFB_I_PDU:
 		if (DIGITAL_NFC_DEP_PFB_PNI(pfb) != ddev->curr_nfc_dep_pni) {
@@ -409,21 +759,71 @@
 
 		ddev->curr_nfc_dep_pni =
 			DIGITAL_NFC_DEP_PFB_PNI(ddev->curr_nfc_dep_pni + 1);
+
+		kfree_skb(ddev->saved_skb);
+		ddev->saved_skb = NULL;
+
+		resp = digital_recv_dep_data_gather(ddev, pfb, resp,
+						    digital_in_send_ack,
+						    data_exch);
+		if (IS_ERR(resp)) {
+			rc = PTR_ERR(resp);
+			resp = NULL;
+			goto error;
+		}
+
+		/* If resp is NULL then we're still chaining so return and
+		 * wait for the next part of the PDU.  Else, the PDU is
+		 * complete so pass it up.
+		 */
+		if (!resp)
+			return;
+
 		rc = 0;
 		break;
 
 	case DIGITAL_NFC_DEP_PFB_ACK_NACK_PDU:
-		pr_err("Received a ACK/NACK PDU\n");
-		rc = -EIO;
-		goto error;
-
-	case DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU:
-		if (!DIGITAL_NFC_DEP_PFB_IS_TIMEOUT(pfb)) {
-			rc = -EINVAL;
-			goto error;
+		if (DIGITAL_NFC_DEP_PFB_PNI(pfb) != ddev->curr_nfc_dep_pni) {
+			PROTOCOL_ERR("14.12.3.3");
+			rc = -EIO;
+			goto exit;
 		}
 
-		rc = digital_in_send_rtox(ddev, data_exch, resp->data[3]);
+		ddev->curr_nfc_dep_pni =
+			DIGITAL_NFC_DEP_PFB_PNI(ddev->curr_nfc_dep_pni + 1);
+
+		if (ddev->chaining_skb && !DIGITAL_NFC_DEP_NACK_BIT_SET(pfb)) {
+			kfree_skb(ddev->saved_skb);
+			ddev->saved_skb = NULL;
+
+			rc = digital_in_send_dep_req(ddev, NULL,
+						     ddev->chaining_skb,
+						     ddev->data_exch);
+			if (rc)
+				goto error;
+
+			return;
+		}
+
+		pr_err("Received a ACK/NACK PDU\n");
+		rc = -EINVAL;
+		goto exit;
+
+	case DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU:
+		if (!DIGITAL_NFC_DEP_PFB_IS_TIMEOUT(pfb)) { /* ATN */
+			rc = digital_in_send_saved_skb(ddev, data_exch);
+			if (rc) {
+				kfree_skb(ddev->saved_skb);
+				goto error;
+			}
+
+			return;
+		}
+
+		kfree_skb(ddev->saved_skb);
+		ddev->saved_skb = NULL;
+
+		rc = digital_in_send_rtox(ddev, data_exch, resp->data[0]);
 		if (rc)
 			goto error;
 
@@ -431,30 +831,18 @@
 		return;
 	}
 
-	if (DIGITAL_NFC_DEP_MI_BIT_SET(pfb)) {
-		pr_err("MI bit set. Chained PDU not supported\n");
-		rc = -EIO;
-		goto error;
-	}
-
-	size = sizeof(struct digital_dep_req_res);
-
-	if (DIGITAL_NFC_DEP_DID_BIT_SET(pfb))
-		size++;
-
-	if (size > resp->len) {
-		rc = -EIO;
-		goto error;
-	}
-
-	skb_pull(resp, size);
-
 exit:
 	data_exch->cb(data_exch->cb_context, resp, rc);
 
 error:
 	kfree(data_exch);
 
+	kfree_skb(ddev->chaining_skb);
+	ddev->chaining_skb = NULL;
+
+	kfree_skb(ddev->saved_skb);
+	ddev->saved_skb = NULL;
+
 	if (rc)
 		kfree_skb(resp);
 }
@@ -464,20 +852,47 @@
 			    struct digital_data_exch *data_exch)
 {
 	struct digital_dep_req_res *dep_req;
+	struct sk_buff *chaining_skb, *tmp_skb;
+	int rc;
 
 	skb_push(skb, sizeof(struct digital_dep_req_res));
 
 	dep_req = (struct digital_dep_req_res *)skb->data;
+
 	dep_req->dir = DIGITAL_NFC_DEP_FRAME_DIR_OUT;
 	dep_req->cmd = DIGITAL_CMD_DEP_REQ;
 	dep_req->pfb = ddev->curr_nfc_dep_pni;
 
-	digital_skb_push_dep_sod(ddev, skb);
+	ddev->atn_count = 0;
+	ddev->nack_count = 0;
 
-	ddev->skb_add_crc(skb);
+	chaining_skb = ddev->chaining_skb;
 
-	return digital_in_send_cmd(ddev, skb, 1500, digital_in_recv_dep_res,
-				   data_exch);
+	tmp_skb = digital_send_dep_data_prep(ddev, skb, dep_req, data_exch);
+	if (IS_ERR(tmp_skb))
+		return PTR_ERR(tmp_skb);
+
+	digital_skb_push_dep_sod(ddev, tmp_skb);
+
+	ddev->skb_add_crc(tmp_skb);
+
+	ddev->saved_skb = skb_get(tmp_skb);
+	ddev->saved_skb_len = tmp_skb->len;
+
+	rc = digital_in_send_cmd(ddev, tmp_skb, 1500, digital_in_recv_dep_res,
+				 data_exch);
+	if (rc) {
+		if (tmp_skb != skb)
+			kfree_skb(tmp_skb);
+
+		kfree_skb(chaining_skb);
+		ddev->chaining_skb = NULL;
+
+		kfree_skb(ddev->saved_skb);
+		ddev->saved_skb = NULL;
+	}
+
+	return rc;
 }
 
 static void digital_tg_set_rf_tech(struct nfc_digital_dev *ddev, u8 rf_tech)
@@ -507,11 +922,106 @@
 	}
 }
 
+static int digital_tg_send_ack(struct nfc_digital_dev *ddev,
+			       struct digital_data_exch *data_exch)
+{
+	struct digital_dep_req_res *dep_res;
+	struct sk_buff *skb;
+	int rc;
+
+	skb = digital_skb_alloc(ddev, 1);
+	if (!skb)
+		return -ENOMEM;
+
+	skb_push(skb, sizeof(struct digital_dep_req_res));
+
+	dep_res = (struct digital_dep_req_res *)skb->data;
+
+	dep_res->dir = DIGITAL_NFC_DEP_FRAME_DIR_IN;
+	dep_res->cmd = DIGITAL_CMD_DEP_RES;
+	dep_res->pfb = DIGITAL_NFC_DEP_PFB_ACK_NACK_PDU |
+		       ddev->curr_nfc_dep_pni;
+
+	if (ddev->did) {
+		dep_res->pfb |= DIGITAL_NFC_DEP_PFB_DID_BIT;
+
+		memcpy(skb_put(skb, sizeof(ddev->did)), &ddev->did,
+		       sizeof(ddev->did));
+	}
+
+	ddev->curr_nfc_dep_pni =
+		DIGITAL_NFC_DEP_PFB_PNI(ddev->curr_nfc_dep_pni + 1);
+
+	digital_skb_push_dep_sod(ddev, skb);
+
+	ddev->skb_add_crc(skb);
+
+	ddev->saved_skb = skb_get(skb);
+	ddev->saved_skb_len = skb->len;
+
+	rc = digital_tg_send_cmd(ddev, skb, 1500, digital_tg_recv_dep_req,
+				 data_exch);
+	if (rc) {
+		kfree_skb(skb);
+		kfree_skb(ddev->saved_skb);
+		ddev->saved_skb = NULL;
+	}
+
+	return rc;
+}
+
+static int digital_tg_send_atn(struct nfc_digital_dev *ddev)
+{
+	struct digital_dep_req_res *dep_res;
+	struct sk_buff *skb;
+	int rc;
+
+	skb = digital_skb_alloc(ddev, 1);
+	if (!skb)
+		return -ENOMEM;
+
+	skb_push(skb, sizeof(struct digital_dep_req_res));
+
+	dep_res = (struct digital_dep_req_res *)skb->data;
+
+	dep_res->dir = DIGITAL_NFC_DEP_FRAME_DIR_IN;
+	dep_res->cmd = DIGITAL_CMD_DEP_RES;
+	dep_res->pfb = DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU;
+
+	if (ddev->did) {
+		dep_res->pfb |= DIGITAL_NFC_DEP_PFB_DID_BIT;
+
+		memcpy(skb_put(skb, sizeof(ddev->did)), &ddev->did,
+		       sizeof(ddev->did));
+	}
+
+	digital_skb_push_dep_sod(ddev, skb);
+
+	ddev->skb_add_crc(skb);
+
+	rc = digital_tg_send_cmd(ddev, skb, 1500, digital_tg_recv_dep_req,
+				 NULL);
+	if (rc)
+		kfree_skb(skb);
+
+	return rc;
+}
+
+static int digital_tg_send_saved_skb(struct nfc_digital_dev *ddev)
+{
+	skb_get(ddev->saved_skb);
+	skb_push(ddev->saved_skb, ddev->saved_skb_len);
+
+	return digital_tg_send_cmd(ddev, ddev->saved_skb, 1500,
+				   digital_tg_recv_dep_req, NULL);
+}
+
 static void digital_tg_recv_dep_req(struct nfc_digital_dev *ddev, void *arg,
 				    struct sk_buff *resp)
 {
 	int rc;
 	struct digital_dep_req_res *dep_req;
+	u8 pfb;
 	size_t size;
 
 	if (IS_ERR(resp)) {
@@ -532,6 +1042,11 @@
 		goto exit;
 	}
 
+	if (resp->len > ddev->local_payload_max) {
+		rc = -EMSGSIZE;
+		goto exit;
+	}
+
 	size = sizeof(struct digital_dep_req_res);
 	dep_req = (struct digital_dep_req_res *)resp->data;
 
@@ -541,34 +1056,147 @@
 		goto exit;
 	}
 
-	if (DIGITAL_NFC_DEP_DID_BIT_SET(dep_req->pfb))
-		size++;
+	pfb = dep_req->pfb;
 
-	if (resp->len < size) {
+	if (DIGITAL_NFC_DEP_DID_BIT_SET(pfb)) {
+		if (ddev->did && (ddev->did == resp->data[3])) {
+			size++;
+		} else {
+			rc = -EIO;
+			goto exit;
+		}
+	} else if (ddev->did) {
 		rc = -EIO;
 		goto exit;
 	}
 
-	switch (DIGITAL_NFC_DEP_PFB_TYPE(dep_req->pfb)) {
-	case DIGITAL_NFC_DEP_PFB_I_PDU:
-		pr_debug("DIGITAL_NFC_DEP_PFB_I_PDU\n");
-		ddev->curr_nfc_dep_pni = DIGITAL_NFC_DEP_PFB_PNI(dep_req->pfb);
-		break;
-	case DIGITAL_NFC_DEP_PFB_ACK_NACK_PDU:
-		pr_err("Received a ACK/NACK PDU\n");
-		rc = -EINVAL;
+	if (DIGITAL_NFC_DEP_NAD_BIT_SET(pfb)) {
+		rc = -EIO;
 		goto exit;
-	case DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU:
-		pr_err("Received a SUPERVISOR PDU\n");
-		rc = -EINVAL;
+	}
+
+	if (size > resp->len) {
+		rc = -EIO;
 		goto exit;
 	}
 
 	skb_pull(resp, size);
 
+	switch (DIGITAL_NFC_DEP_PFB_TYPE(pfb)) {
+	case DIGITAL_NFC_DEP_PFB_I_PDU:
+		pr_debug("DIGITAL_NFC_DEP_PFB_I_PDU\n");
+
+		if ((ddev->atn_count && (DIGITAL_NFC_DEP_PFB_PNI(pfb - 1) !=
+						ddev->curr_nfc_dep_pni)) ||
+		    (DIGITAL_NFC_DEP_PFB_PNI(pfb) != ddev->curr_nfc_dep_pni)) {
+			PROTOCOL_ERR("14.12.3.4");
+			rc = -EIO;
+			goto exit;
+		}
+
+		if (ddev->atn_count) {
+			ddev->atn_count = 0;
+
+			rc = digital_tg_send_saved_skb(ddev);
+			if (rc)
+				goto exit;
+
+			return;
+		}
+
+		kfree_skb(ddev->saved_skb);
+		ddev->saved_skb = NULL;
+
+		resp = digital_recv_dep_data_gather(ddev, pfb, resp,
+						    digital_tg_send_ack, NULL);
+		if (IS_ERR(resp)) {
+			rc = PTR_ERR(resp);
+			resp = NULL;
+			goto exit;
+		}
+
+		/* If resp is NULL then we're still chaining so return and
+		 * wait for the next part of the PDU.  Else, the PDU is
+		 * complete so pass it up.
+		 */
+		if (!resp)
+			return;
+
+		rc = 0;
+		break;
+	case DIGITAL_NFC_DEP_PFB_ACK_NACK_PDU:
+		if (!DIGITAL_NFC_DEP_NACK_BIT_SET(pfb)) { /* ACK */
+			if ((ddev->atn_count &&
+			     (DIGITAL_NFC_DEP_PFB_PNI(pfb - 1) !=
+						ddev->curr_nfc_dep_pni)) ||
+			    (DIGITAL_NFC_DEP_PFB_PNI(pfb) !=
+						ddev->curr_nfc_dep_pni) ||
+			    !ddev->chaining_skb || !ddev->saved_skb) {
+				rc = -EIO;
+				goto exit;
+			}
+
+			if (ddev->atn_count) {
+				ddev->atn_count = 0;
+
+				rc = digital_tg_send_saved_skb(ddev);
+				if (rc)
+					goto exit;
+
+				return;
+			}
+
+			kfree_skb(ddev->saved_skb);
+			ddev->saved_skb = NULL;
+
+			rc = digital_tg_send_dep_res(ddev, ddev->chaining_skb);
+			if (rc)
+				goto exit;
+		} else { /* NACK */
+			if ((DIGITAL_NFC_DEP_PFB_PNI(pfb + 1) !=
+						ddev->curr_nfc_dep_pni) ||
+			    !ddev->saved_skb) {
+				rc = -EIO;
+				goto exit;
+			}
+
+			ddev->atn_count = 0;
+
+			rc = digital_tg_send_saved_skb(ddev);
+			if (rc) {
+				kfree_skb(ddev->saved_skb);
+				goto exit;
+			}
+		}
+
+		return;
+	case DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU:
+		if (DIGITAL_NFC_DEP_PFB_IS_TIMEOUT(pfb)) {
+			rc = -EINVAL;
+			goto exit;
+		}
+
+		rc = digital_tg_send_atn(ddev);
+		if (rc)
+			goto exit;
+
+		ddev->atn_count++;
+
+		kfree_skb(resp);
+		return;
+	}
+
 	rc = nfc_tm_data_received(ddev->nfc_dev, resp);
 
 exit:
+	kfree_skb(ddev->chaining_skb);
+	ddev->chaining_skb = NULL;
+
+	ddev->atn_count = 0;
+
+	kfree_skb(ddev->saved_skb);
+	ddev->saved_skb = NULL;
+
 	if (rc)
 		kfree_skb(resp);
 }
@@ -576,20 +1204,54 @@
 int digital_tg_send_dep_res(struct nfc_digital_dev *ddev, struct sk_buff *skb)
 {
 	struct digital_dep_req_res *dep_res;
+	struct sk_buff *chaining_skb, *tmp_skb;
+	int rc;
 
 	skb_push(skb, sizeof(struct digital_dep_req_res));
+
 	dep_res = (struct digital_dep_req_res *)skb->data;
 
 	dep_res->dir = DIGITAL_NFC_DEP_FRAME_DIR_IN;
 	dep_res->cmd = DIGITAL_CMD_DEP_RES;
 	dep_res->pfb = ddev->curr_nfc_dep_pni;
 
-	digital_skb_push_dep_sod(ddev, skb);
+	if (ddev->did) {
+		dep_res->pfb |= DIGITAL_NFC_DEP_PFB_DID_BIT;
 
-	ddev->skb_add_crc(skb);
+		memcpy(skb_put(skb, sizeof(ddev->did)), &ddev->did,
+		       sizeof(ddev->did));
+	}
 
-	return digital_tg_send_cmd(ddev, skb, 1500, digital_tg_recv_dep_req,
-				   NULL);
+	ddev->curr_nfc_dep_pni =
+		DIGITAL_NFC_DEP_PFB_PNI(ddev->curr_nfc_dep_pni + 1);
+
+	chaining_skb = ddev->chaining_skb;
+
+	tmp_skb = digital_send_dep_data_prep(ddev, skb, dep_res, NULL);
+	if (IS_ERR(tmp_skb))
+		return PTR_ERR(tmp_skb);
+
+	digital_skb_push_dep_sod(ddev, tmp_skb);
+
+	ddev->skb_add_crc(tmp_skb);
+
+	ddev->saved_skb = skb_get(tmp_skb);
+	ddev->saved_skb_len = tmp_skb->len;
+
+	rc = digital_tg_send_cmd(ddev, tmp_skb, 1500, digital_tg_recv_dep_req,
+				 NULL);
+	if (rc) {
+		if (tmp_skb != skb)
+			kfree_skb(tmp_skb);
+
+		kfree_skb(chaining_skb);
+		ddev->chaining_skb = NULL;
+
+		kfree_skb(ddev->saved_skb);
+		ddev->saved_skb = NULL;
+	}
+
+	return rc;
 }
 
 static void digital_tg_send_psl_res_complete(struct nfc_digital_dev *ddev,
@@ -632,9 +1294,10 @@
 
 	ddev->skb_add_crc(skb);
 
+	ddev->curr_nfc_dep_pni = 0;
+
 	rc = digital_tg_send_cmd(ddev, skb, 0, digital_tg_send_psl_res_complete,
 				 (void *)(unsigned long)rf_tech);
-
 	if (rc)
 		kfree_skb(skb);
 
@@ -647,7 +1310,7 @@
 	int rc;
 	struct digital_psl_req *psl_req;
 	u8 rf_tech;
-	u8 dsi;
+	u8 dsi, payload_size, payload_bits;
 
 	if (IS_ERR(resp)) {
 		rc = PTR_ERR(resp);
@@ -692,6 +1355,18 @@
 		goto exit;
 	}
 
+	payload_bits = DIGITAL_PAYLOAD_FSL_TO_BITS(psl_req->fsl);
+	payload_size = digital_payload_bits_to_size(payload_bits);
+
+	if (!payload_size || (payload_size > min(ddev->local_payload_max,
+						 ddev->remote_payload_max))) {
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	ddev->local_payload_max = payload_size;
+	ddev->remote_payload_max = payload_size;
+
 	rc = digital_tg_send_psl_res(ddev, psl_req->did, rf_tech);
 
 exit:
@@ -712,6 +1387,8 @@
 	if (resp->data[0] == DIGITAL_NFC_DEP_NFCA_SOD_SB)
 		offset++;
 
+	ddev->atn_count = 0;
+
 	if (resp->data[offset] == DIGITAL_CMD_PSL_REQ)
 		digital_tg_recv_psl_req(ddev, arg, resp);
 	else
@@ -723,7 +1400,7 @@
 {
 	struct digital_atr_res *atr_res;
 	struct sk_buff *skb;
-	u8 *gb;
+	u8 *gb, payload_bits;
 	size_t gb_len;
 	int rc;
 
@@ -744,7 +1421,11 @@
 	atr_res->cmd = DIGITAL_CMD_ATR_RES;
 	memcpy(atr_res->nfcid3, atr_req->nfcid3, sizeof(atr_req->nfcid3));
 	atr_res->to = 8;
-	atr_res->pp = DIGITAL_LR_BITS_PAYLOAD_SIZE_254B;
+
+	ddev->local_payload_max = DIGITAL_PAYLOAD_SIZE_MAX;
+	payload_bits = digital_payload_size_to_bits(ddev->local_payload_max);
+	atr_res->pp = DIGITAL_PAYLOAD_BITS_TO_PP(payload_bits);
+
 	if (gb_len) {
 		skb_put(skb, gb_len);
 
@@ -756,12 +1437,12 @@
 
 	ddev->skb_add_crc(skb);
 
+	ddev->curr_nfc_dep_pni = 0;
+
 	rc = digital_tg_send_cmd(ddev, skb, 999,
 				 digital_tg_send_atr_res_complete, NULL);
-	if (rc) {
+	if (rc)
 		kfree_skb(skb);
-		return rc;
-	}
 
 	return rc;
 }
@@ -772,7 +1453,7 @@
 	int rc;
 	struct digital_atr_req *atr_req;
 	size_t gb_len, min_size;
-	u8 poll_tech_count;
+	u8 poll_tech_count, payload_bits;
 
 	if (IS_ERR(resp)) {
 		rc = PTR_ERR(resp);
@@ -815,11 +1496,22 @@
 	atr_req = (struct digital_atr_req *)resp->data;
 
 	if (atr_req->dir != DIGITAL_NFC_DEP_FRAME_DIR_OUT ||
-	    atr_req->cmd != DIGITAL_CMD_ATR_REQ) {
+	    atr_req->cmd != DIGITAL_CMD_ATR_REQ ||
+	    atr_req->did > DIGITAL_DID_MAX) {
 		rc = -EINVAL;
 		goto exit;
 	}
 
+	payload_bits = DIGITAL_PAYLOAD_PP_TO_BITS(atr_req->pp);
+	ddev->remote_payload_max = digital_payload_bits_to_size(payload_bits);
+
+	if (!ddev->remote_payload_max) {
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	ddev->did = atr_req->did;
+
 	rc = digital_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING,
 				     NFC_DIGITAL_FRAMING_NFC_DEP_ACTIVATED);
 	if (rc)
diff --git a/net/nfc/hci/command.c b/net/nfc/hci/command.c
index 677d24b..91df487 100644
--- a/net/nfc/hci/command.c
+++ b/net/nfc/hci/command.c
@@ -345,6 +345,9 @@
 
 	pr_debug("\n");
 
+	if (hdev->gate2pipe[dest_gate] == NFC_HCI_DO_NOT_CREATE_PIPE)
+		return 0;
+
 	if (hdev->gate2pipe[dest_gate] != NFC_HCI_INVALID_PIPE)
 		return -EADDRINUSE;
 
diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c
index 1177082..ef50e77 100644
--- a/net/nfc/hci/core.c
+++ b/net/nfc/hci/core.c
@@ -167,6 +167,48 @@
 void nfc_hci_cmd_received(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd,
 			  struct sk_buff *skb)
 {
+	int r = 0;
+	u8 gate = nfc_hci_pipe2gate(hdev, pipe);
+	u8 local_gate, new_pipe;
+	u8 gate_opened = 0x00;
+
+	pr_debug("from gate %x pipe %x cmd %x\n", gate, pipe, cmd);
+
+	switch (cmd) {
+	case NFC_HCI_ADM_NOTIFY_PIPE_CREATED:
+		if (skb->len != 5) {
+			r = -EPROTO;
+			break;
+		}
+
+		local_gate = skb->data[3];
+		new_pipe = skb->data[4];
+		nfc_hci_send_response(hdev, gate, NFC_HCI_ANY_OK, NULL, 0);
+
+		/* save the new created pipe and bind with local gate,
+		 * the description for skb->data[3] is destination gate id
+		 * but since we received this cmd from host controller, we
+		 * are the destination and it is our local gate
+		 */
+		hdev->gate2pipe[local_gate] = new_pipe;
+		break;
+	case NFC_HCI_ANY_OPEN_PIPE:
+		/* if the pipe is already created, we allow remote host to
+		 * open it
+		 */
+		if (gate != 0xff)
+			nfc_hci_send_response(hdev, gate, NFC_HCI_ANY_OK,
+					      &gate_opened, 1);
+		break;
+	case NFC_HCI_ADM_NOTIFY_ALL_PIPE_CLEARED:
+		nfc_hci_send_response(hdev, gate, NFC_HCI_ANY_OK, NULL, 0);
+		break;
+	default:
+		pr_info("Discarded unknown cmd %x to gate %x\n", cmd, gate);
+		r = -EINVAL;
+		break;
+	}
+
 	kfree_skb(skb);
 }
 
@@ -717,6 +759,19 @@
 	return 0;
 }
 
+static int hci_se_io(struct nfc_dev *nfc_dev, u32 se_idx,
+		     u8 *apdu, size_t apdu_length,
+		     se_io_cb_t cb, void *cb_context)
+{
+	struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
+
+	if (hdev->ops->se_io)
+		return hdev->ops->se_io(hdev, se_idx, apdu,
+					apdu_length, cb, cb_context);
+
+	return 0;
+}
+
 static void nfc_hci_failure(struct nfc_hci_dev *hdev, int err)
 {
 	mutex_lock(&hdev->msg_tx_mutex);
@@ -830,6 +885,7 @@
 	.discover_se = hci_discover_se,
 	.enable_se = hci_enable_se,
 	.disable_se = hci_disable_se,
+	.se_io = hci_se_io,
 };
 
 struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops,
diff --git a/net/nfc/llcp_commands.c b/net/nfc/llcp_commands.c
index a3ad69a..c3435f8 100644
--- a/net/nfc/llcp_commands.c
+++ b/net/nfc/llcp_commands.c
@@ -401,7 +401,8 @@
 	u8 *miux_tlv = NULL, miux_tlv_length;
 	u8 *rw_tlv = NULL, rw_tlv_length, rw;
 	int err;
-	u16 size = 0, miux;
+	u16 size = 0;
+	__be16 miux;
 
 	pr_debug("Sending CONNECT\n");
 
@@ -465,7 +466,8 @@
 	u8 *miux_tlv = NULL, miux_tlv_length;
 	u8 *rw_tlv = NULL, rw_tlv_length, rw;
 	int err;
-	u16 size = 0, miux;
+	u16 size = 0;
+	__be16 miux;
 
 	pr_debug("Sending CC\n");
 
diff --git a/net/nfc/llcp_core.c b/net/nfc/llcp_core.c
index 51e7887..b18f07c 100644
--- a/net/nfc/llcp_core.c
+++ b/net/nfc/llcp_core.c
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2011  Intel Corporation. All rights reserved.
+ * Copyright (C) 2014 Marvell International Ltd.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -1511,8 +1512,10 @@
 	struct nfc_llcp_local *local;
 
 	local = nfc_llcp_find_local(dev);
-	if (local == NULL)
+	if (local == NULL) {
+		kfree_skb(skb);
 		return -ENODEV;
+	}
 
 	__nfc_llcp_recv(local, skb);
 
diff --git a/net/nfc/llcp_sock.c b/net/nfc/llcp_sock.c
index 51f077a..4894c41 100644
--- a/net/nfc/llcp_sock.c
+++ b/net/nfc/llcp_sock.c
@@ -524,13 +524,13 @@
 
 static inline unsigned int llcp_accept_poll(struct sock *parent)
 {
-	struct nfc_llcp_sock *llcp_sock, *n, *parent_sock;
+	struct nfc_llcp_sock *llcp_sock, *parent_sock;
 	struct sock *sk;
 
 	parent_sock = nfc_llcp_sock(parent);
 
-	list_for_each_entry_safe(llcp_sock, n, &parent_sock->accept_queue,
-				 accept_queue) {
+	list_for_each_entry(llcp_sock, &parent_sock->accept_queue,
+			    accept_queue) {
 		sk = &llcp_sock->sk;
 
 		if (sk->sk_state == LLCP_CONNECTED)
diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c
index 90b16cb..51feb5e 100644
--- a/net/nfc/nci/core.c
+++ b/net/nfc/nci/core.c
@@ -3,6 +3,7 @@
  *  NFC Controller (NFCC) and a Device Host (DH).
  *
  *  Copyright (C) 2011 Texas Instruments, Inc.
+ *  Copyright (C) 2014 Marvell International Ltd.
  *
  *  Written by Ilan Elias <ilane@ti.com>
  *
@@ -196,18 +197,24 @@
 	nci_send_cmd(ndev, NCI_OP_CORE_SET_CONFIG_CMD, (3 + param->len), &cmd);
 }
 
+struct nci_rf_discover_param {
+	__u32	im_protocols;
+	__u32	tm_protocols;
+};
+
 static void nci_rf_discover_req(struct nci_dev *ndev, unsigned long opt)
 {
+	struct nci_rf_discover_param *param =
+		(struct nci_rf_discover_param *)opt;
 	struct nci_rf_disc_cmd cmd;
-	__u32 protocols = opt;
 
 	cmd.num_disc_configs = 0;
 
 	if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) &&
-	    (protocols & NFC_PROTO_JEWEL_MASK ||
-	     protocols & NFC_PROTO_MIFARE_MASK ||
-	     protocols & NFC_PROTO_ISO14443_MASK ||
-	     protocols & NFC_PROTO_NFC_DEP_MASK)) {
+	    (param->im_protocols & NFC_PROTO_JEWEL_MASK ||
+	     param->im_protocols & NFC_PROTO_MIFARE_MASK ||
+	     param->im_protocols & NFC_PROTO_ISO14443_MASK ||
+	     param->im_protocols & NFC_PROTO_NFC_DEP_MASK)) {
 		cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
 			NCI_NFC_A_PASSIVE_POLL_MODE;
 		cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
@@ -215,7 +222,7 @@
 	}
 
 	if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) &&
-	    (protocols & NFC_PROTO_ISO14443_B_MASK)) {
+	    (param->im_protocols & NFC_PROTO_ISO14443_B_MASK)) {
 		cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
 			NCI_NFC_B_PASSIVE_POLL_MODE;
 		cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
@@ -223,8 +230,8 @@
 	}
 
 	if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) &&
-	    (protocols & NFC_PROTO_FELICA_MASK ||
-	     protocols & NFC_PROTO_NFC_DEP_MASK)) {
+	    (param->im_protocols & NFC_PROTO_FELICA_MASK ||
+	     param->im_protocols & NFC_PROTO_NFC_DEP_MASK)) {
 		cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
 			NCI_NFC_F_PASSIVE_POLL_MODE;
 		cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
@@ -232,13 +239,25 @@
 	}
 
 	if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) &&
-	    (protocols & NFC_PROTO_ISO15693_MASK)) {
+	    (param->im_protocols & NFC_PROTO_ISO15693_MASK)) {
 		cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
 			NCI_NFC_V_PASSIVE_POLL_MODE;
 		cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
 		cmd.num_disc_configs++;
 	}
 
+	if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS - 1) &&
+	    (param->tm_protocols & NFC_PROTO_NFC_DEP_MASK)) {
+		cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
+			NCI_NFC_A_PASSIVE_LISTEN_MODE;
+		cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
+		cmd.num_disc_configs++;
+		cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
+			NCI_NFC_F_PASSIVE_LISTEN_MODE;
+		cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
+		cmd.num_disc_configs++;
+	}
+
 	nci_send_cmd(ndev, NCI_OP_RF_DISCOVER_CMD,
 		     (1 + (cmd.num_disc_configs * sizeof(struct disc_config))),
 		     &cmd);
@@ -280,7 +299,7 @@
 {
 	struct nci_rf_deactivate_cmd cmd;
 
-	cmd.type = NCI_DEACTIVATE_TYPE_IDLE_MODE;
+	cmd.type = opt;
 
 	nci_send_cmd(ndev, NCI_OP_RF_DEACTIVATE_CMD,
 		     sizeof(struct nci_rf_deactivate_cmd), &cmd);
@@ -441,6 +460,7 @@
 {
 	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
 	struct nci_set_config_param param;
+	int rc;
 
 	param.val = nfc_get_local_general_bytes(nfc_dev, &param.len);
 	if ((param.val == NULL) || (param.len == 0))
@@ -451,14 +471,45 @@
 
 	param.id = NCI_PN_ATR_REQ_GEN_BYTES;
 
+	rc = nci_request(ndev, nci_set_config_req, (unsigned long)&param,
+			 msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT));
+	if (rc)
+		return rc;
+
+	param.id = NCI_LN_ATR_RES_GEN_BYTES;
+
 	return nci_request(ndev, nci_set_config_req, (unsigned long)&param,
 			   msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT));
 }
 
+static int nci_set_listen_parameters(struct nfc_dev *nfc_dev)
+{
+	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
+	int rc;
+	__u8 val;
+
+	val = NCI_LA_SEL_INFO_NFC_DEP_MASK;
+
+	rc = nci_set_config(ndev, NCI_LA_SEL_INFO, 1, &val);
+	if (rc)
+		return rc;
+
+	val = NCI_LF_PROTOCOL_TYPE_NFC_DEP_MASK;
+
+	rc = nci_set_config(ndev, NCI_LF_PROTOCOL_TYPE, 1, &val);
+	if (rc)
+		return rc;
+
+	val = NCI_LF_CON_BITR_F_212 | NCI_LF_CON_BITR_F_424;
+
+	return nci_set_config(ndev, NCI_LF_CON_BITR_F, 1, &val);
+}
+
 static int nci_start_poll(struct nfc_dev *nfc_dev,
 			  __u32 im_protocols, __u32 tm_protocols)
 {
 	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
+	struct nci_rf_discover_param param;
 	int rc;
 
 	if ((atomic_read(&ndev->state) == NCI_DISCOVERY) ||
@@ -476,13 +527,14 @@
 	    (atomic_read(&ndev->state) == NCI_POLL_ACTIVE)) {
 		pr_debug("target active or w4 select, implicitly deactivate\n");
 
-		rc = nci_request(ndev, nci_rf_deactivate_req, 0,
+		rc = nci_request(ndev, nci_rf_deactivate_req,
+				 NCI_DEACTIVATE_TYPE_IDLE_MODE,
 				 msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT));
 		if (rc)
 			return -EBUSY;
 	}
 
-	if (im_protocols & NFC_PROTO_NFC_DEP_MASK) {
+	if ((im_protocols | tm_protocols) & NFC_PROTO_NFC_DEP_MASK) {
 		rc = nci_set_local_general_bytes(nfc_dev);
 		if (rc) {
 			pr_err("failed to set local general bytes\n");
@@ -490,7 +542,15 @@
 		}
 	}
 
-	rc = nci_request(ndev, nci_rf_discover_req, im_protocols,
+	if (tm_protocols & NFC_PROTO_NFC_DEP_MASK) {
+		rc = nci_set_listen_parameters(nfc_dev);
+		if (rc)
+			pr_err("failed to set listen parameters\n");
+	}
+
+	param.im_protocols = im_protocols;
+	param.tm_protocols = tm_protocols;
+	rc = nci_request(ndev, nci_rf_discover_req, (unsigned long)&param,
 			 msecs_to_jiffies(NCI_RF_DISC_TIMEOUT));
 
 	if (!rc)
@@ -509,7 +569,7 @@
 		return;
 	}
 
-	nci_request(ndev, nci_rf_deactivate_req, 0,
+	nci_request(ndev, nci_rf_deactivate_req, NCI_DEACTIVATE_TYPE_IDLE_MODE,
 		    msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT));
 }
 
@@ -594,7 +654,8 @@
 	ndev->target_active_prot = 0;
 
 	if (atomic_read(&ndev->state) == NCI_POLL_ACTIVE) {
-		nci_request(ndev, nci_rf_deactivate_req, 0,
+		nci_request(ndev, nci_rf_deactivate_req,
+			    NCI_DEACTIVATE_TYPE_SLEEP_MODE,
 			    msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT));
 	}
 }
@@ -622,9 +683,24 @@
 
 static int nci_dep_link_down(struct nfc_dev *nfc_dev)
 {
+	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
+	int rc;
+
 	pr_debug("entry\n");
 
-	nci_deactivate_target(nfc_dev, NULL);
+	if (nfc_dev->rf_mode == NFC_RF_INITIATOR) {
+		nci_deactivate_target(nfc_dev, NULL);
+	} else {
+		if (atomic_read(&ndev->state) == NCI_LISTEN_ACTIVE ||
+		    atomic_read(&ndev->state) == NCI_DISCOVERY) {
+			nci_request(ndev, nci_rf_deactivate_req, 0,
+				msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT));
+		}
+
+		rc = nfc_tm_deactivated(nfc_dev);
+		if (rc)
+			pr_err("error when signaling tm deactivation\n");
+	}
 
 	return 0;
 }
@@ -658,18 +734,58 @@
 	return rc;
 }
 
+static int nci_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb)
+{
+	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
+	int rc;
+
+	rc = nci_send_data(ndev, NCI_STATIC_RF_CONN_ID, skb);
+	if (rc)
+		pr_err("unable to send data\n");
+
+	return rc;
+}
+
 static int nci_enable_se(struct nfc_dev *nfc_dev, u32 se_idx)
 {
+	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
+
+	if (ndev->ops->enable_se)
+		return ndev->ops->enable_se(ndev, se_idx);
+
 	return 0;
 }
 
 static int nci_disable_se(struct nfc_dev *nfc_dev, u32 se_idx)
 {
+	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
+
+	if (ndev->ops->disable_se)
+		return ndev->ops->disable_se(ndev, se_idx);
+
 	return 0;
 }
 
 static int nci_discover_se(struct nfc_dev *nfc_dev)
 {
+	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
+
+	if (ndev->ops->discover_se)
+		return ndev->ops->discover_se(ndev);
+
+	return 0;
+}
+
+static int nci_se_io(struct nfc_dev *nfc_dev, u32 se_idx,
+		     u8 *apdu, size_t apdu_length,
+		     se_io_cb_t cb, void *cb_context)
+{
+	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
+
+	if (ndev->ops->se_io)
+		return ndev->ops->se_io(ndev, se_idx, apdu,
+				apdu_length, cb, cb_context);
+
 	return 0;
 }
 
@@ -683,9 +799,11 @@
 	.activate_target = nci_activate_target,
 	.deactivate_target = nci_deactivate_target,
 	.im_transceive = nci_transceive,
+	.tm_send = nci_tm_send,
 	.enable_se = nci_enable_se,
 	.disable_se = nci_disable_se,
 	.discover_se = nci_discover_se,
+	.se_io = nci_se_io,
 };
 
 /* ---- Interface to NCI drivers ---- */
diff --git a/net/nfc/nci/data.c b/net/nfc/nci/data.c
index 427ef2c..a2de2a8 100644
--- a/net/nfc/nci/data.c
+++ b/net/nfc/nci/data.c
@@ -3,6 +3,7 @@
  *  NFC Controller (NFCC) and a Device Host (DH).
  *
  *  Copyright (C) 2011 Texas Instruments, Inc.
+ *  Copyright (C) 2014 Marvell International Ltd.
  *
  *  Written by Ilan Elias <ilane@ti.com>
  *
@@ -184,11 +185,16 @@
 
 static void nci_add_rx_data_frag(struct nci_dev *ndev,
 				 struct sk_buff *skb,
-				 __u8 pbf)
+				 __u8 pbf, __u8 status)
 {
 	int reassembly_len;
 	int err = 0;
 
+	if (status) {
+		err = status;
+		goto exit;
+	}
+
 	if (ndev->rx_data_reassembly) {
 		reassembly_len = ndev->rx_data_reassembly->len;
 
@@ -223,13 +229,24 @@
 	}
 
 exit:
-	nci_data_exchange_complete(ndev, skb, err);
+	if (ndev->nfc_dev->rf_mode == NFC_RF_INITIATOR) {
+		nci_data_exchange_complete(ndev, skb, err);
+	} else if (ndev->nfc_dev->rf_mode == NFC_RF_TARGET) {
+		/* Data received in Target mode, forward to nfc core */
+		err = nfc_tm_data_received(ndev->nfc_dev, skb);
+		if (err)
+			pr_err("unable to handle received data\n");
+	} else {
+		pr_err("rf mode unknown\n");
+		kfree_skb(skb);
+	}
 }
 
 /* Rx Data packet */
 void nci_rx_data_packet(struct nci_dev *ndev, struct sk_buff *skb)
 {
 	__u8 pbf = nci_pbf(skb->data);
+	__u8 status = 0;
 
 	pr_debug("len %d\n", skb->len);
 
@@ -247,8 +264,9 @@
 	    ndev->target_active_prot == NFC_PROTO_ISO15693) {
 		/* frame I/F => remove the status byte */
 		pr_debug("frame I/F => remove the status byte\n");
+		status = skb->data[skb->len - 1];
 		skb_trim(skb, (skb->len - 1));
 	}
 
-	nci_add_rx_data_frag(ndev, skb, pbf);
+	nci_add_rx_data_frag(ndev, skb, pbf, nci_to_errno(status));
 }
diff --git a/net/nfc/nci/ntf.c b/net/nfc/nci/ntf.c
index 205b35f6..22e453c 100644
--- a/net/nfc/nci/ntf.c
+++ b/net/nfc/nci/ntf.c
@@ -103,7 +103,7 @@
 			struct rf_tech_specific_params_nfca_poll *nfca_poll,
 						     __u8 *data)
 {
-	nfca_poll->sens_res = __le16_to_cpu(*((__u16 *)data));
+	nfca_poll->sens_res = __le16_to_cpu(*((__le16 *)data));
 	data += 2;
 
 	nfca_poll->nfcid1_len = min_t(__u8, *data++, NFC_NFCID1_MAXSIZE);
@@ -167,7 +167,19 @@
 	return data;
 }
 
-__u32 nci_get_prop_rf_protocol(struct nci_dev *ndev, __u8 rf_protocol)
+static __u8 *nci_extract_rf_params_nfcf_passive_listen(struct nci_dev *ndev,
+			struct rf_tech_specific_params_nfcf_listen *nfcf_listen,
+						     __u8 *data)
+{
+	nfcf_listen->local_nfcid2_len = min_t(__u8, *data++,
+					      NFC_NFCID2_MAXSIZE);
+	memcpy(nfcf_listen->local_nfcid2, data, nfcf_listen->local_nfcid2_len);
+	data += nfcf_listen->local_nfcid2_len;
+
+	return data;
+}
+
+static __u32 nci_get_prop_rf_protocol(struct nci_dev *ndev, __u8 rf_protocol)
 {
 	if (ndev->ops->get_rfprotocol)
 		return ndev->ops->get_rfprotocol(ndev, rf_protocol);
@@ -401,17 +413,29 @@
 			struct nci_rf_intf_activated_ntf *ntf, __u8 *data)
 {
 	struct activation_params_poll_nfc_dep *poll;
+	struct activation_params_listen_nfc_dep *listen;
 
 	switch (ntf->activation_rf_tech_and_mode) {
 	case NCI_NFC_A_PASSIVE_POLL_MODE:
 	case NCI_NFC_F_PASSIVE_POLL_MODE:
 		poll = &ntf->activation_params.poll_nfc_dep;
-		poll->atr_res_len = min_t(__u8, *data++, 63);
+		poll->atr_res_len = min_t(__u8, *data++,
+					  NFC_ATR_RES_MAXSIZE - 2);
 		pr_debug("atr_res_len %d\n", poll->atr_res_len);
 		if (poll->atr_res_len > 0)
 			memcpy(poll->atr_res, data, poll->atr_res_len);
 		break;
 
+	case NCI_NFC_A_PASSIVE_LISTEN_MODE:
+	case NCI_NFC_F_PASSIVE_LISTEN_MODE:
+		listen = &ntf->activation_params.listen_nfc_dep;
+		listen->atr_req_len = min_t(__u8, *data++,
+					    NFC_ATR_REQ_MAXSIZE - 2);
+		pr_debug("atr_req_len %d\n", listen->atr_req_len);
+		if (listen->atr_req_len > 0)
+			memcpy(listen->atr_req, data, listen->atr_req_len);
+		break;
+
 	default:
 		pr_err("unsupported activation_rf_tech_and_mode 0x%x\n",
 		       ntf->activation_rf_tech_and_mode);
@@ -444,6 +468,48 @@
 	nfc_targets_found(ndev->nfc_dev, ndev->targets, ndev->n_targets);
 }
 
+static int nci_store_general_bytes_nfc_dep(struct nci_dev *ndev,
+		struct nci_rf_intf_activated_ntf *ntf)
+{
+	ndev->remote_gb_len = 0;
+
+	if (ntf->activation_params_len <= 0)
+		return NCI_STATUS_OK;
+
+	switch (ntf->activation_rf_tech_and_mode) {
+	case NCI_NFC_A_PASSIVE_POLL_MODE:
+	case NCI_NFC_F_PASSIVE_POLL_MODE:
+		ndev->remote_gb_len = min_t(__u8,
+			(ntf->activation_params.poll_nfc_dep.atr_res_len
+						- NFC_ATR_RES_GT_OFFSET),
+			NFC_ATR_RES_GB_MAXSIZE);
+		memcpy(ndev->remote_gb,
+		       (ntf->activation_params.poll_nfc_dep.atr_res
+						+ NFC_ATR_RES_GT_OFFSET),
+		       ndev->remote_gb_len);
+		break;
+
+	case NCI_NFC_A_PASSIVE_LISTEN_MODE:
+	case NCI_NFC_F_PASSIVE_LISTEN_MODE:
+		ndev->remote_gb_len = min_t(__u8,
+			(ntf->activation_params.listen_nfc_dep.atr_req_len
+						- NFC_ATR_REQ_GT_OFFSET),
+			NFC_ATR_REQ_GB_MAXSIZE);
+		memcpy(ndev->remote_gb,
+		       (ntf->activation_params.listen_nfc_dep.atr_req
+						+ NFC_ATR_REQ_GT_OFFSET),
+		       ndev->remote_gb_len);
+		break;
+
+	default:
+		pr_err("unsupported activation_rf_tech_and_mode 0x%x\n",
+		       ntf->activation_rf_tech_and_mode);
+		return NCI_STATUS_RF_PROTOCOL_ERROR;
+	}
+
+	return NCI_STATUS_OK;
+}
+
 static void nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev,
 					     struct sk_buff *skb)
 {
@@ -493,6 +559,16 @@
 				&(ntf.rf_tech_specific_params.nfcv_poll), data);
 			break;
 
+		case NCI_NFC_A_PASSIVE_LISTEN_MODE:
+			/* no RF technology specific parameters */
+			break;
+
+		case NCI_NFC_F_PASSIVE_LISTEN_MODE:
+			data = nci_extract_rf_params_nfcf_passive_listen(ndev,
+				&(ntf.rf_tech_specific_params.nfcf_listen),
+				data);
+			break;
+
 		default:
 			pr_err("unsupported activation_rf_tech_and_mode 0x%x\n",
 			       ntf.activation_rf_tech_and_mode);
@@ -546,32 +622,39 @@
 
 		/* store general bytes to be reported later in dep_link_up */
 		if (ntf.rf_interface == NCI_RF_INTERFACE_NFC_DEP) {
-			ndev->remote_gb_len = 0;
-
-			if (ntf.activation_params_len > 0) {
-				/* ATR_RES general bytes at offset 15 */
-				ndev->remote_gb_len = min_t(__u8,
-					(ntf.activation_params
-					.poll_nfc_dep.atr_res_len
-					- NFC_ATR_RES_GT_OFFSET),
-					NFC_MAX_GT_LEN);
-				memcpy(ndev->remote_gb,
-				       (ntf.activation_params.poll_nfc_dep
-				       .atr_res + NFC_ATR_RES_GT_OFFSET),
-				       ndev->remote_gb_len);
-			}
+			err = nci_store_general_bytes_nfc_dep(ndev, &ntf);
+			if (err != NCI_STATUS_OK)
+				pr_err("unable to store general bytes\n");
 		}
 	}
 
-	if (atomic_read(&ndev->state) == NCI_DISCOVERY) {
-		/* A single target was found and activated automatically */
-		atomic_set(&ndev->state, NCI_POLL_ACTIVE);
-		if (err == NCI_STATUS_OK)
-			nci_target_auto_activated(ndev, &ntf);
-	} else {	/* ndev->state == NCI_W4_HOST_SELECT */
-		/* A selected target was activated, so complete the request */
-		atomic_set(&ndev->state, NCI_POLL_ACTIVE);
-		nci_req_complete(ndev, err);
+	if (!(ntf.activation_rf_tech_and_mode & NCI_RF_TECH_MODE_LISTEN_MASK)) {
+		/* Poll mode */
+		if (atomic_read(&ndev->state) == NCI_DISCOVERY) {
+			/* A single target was found and activated
+			 * automatically */
+			atomic_set(&ndev->state, NCI_POLL_ACTIVE);
+			if (err == NCI_STATUS_OK)
+				nci_target_auto_activated(ndev, &ntf);
+		} else {	/* ndev->state == NCI_W4_HOST_SELECT */
+			/* A selected target was activated, so complete the
+			 * request */
+			atomic_set(&ndev->state, NCI_POLL_ACTIVE);
+			nci_req_complete(ndev, err);
+		}
+	} else {
+		/* Listen mode */
+		atomic_set(&ndev->state, NCI_LISTEN_ACTIVE);
+		if (err == NCI_STATUS_OK &&
+		    ntf.rf_protocol == NCI_RF_PROTOCOL_NFC_DEP) {
+			err = nfc_tm_activated(ndev->nfc_dev,
+					       NFC_PROTO_NFC_DEP_MASK,
+					       NFC_COMM_PASSIVE,
+					       ndev->remote_gb,
+					       ndev->remote_gb_len);
+			if (err != NCI_STATUS_OK)
+				pr_err("error when signaling tm activation\n");
+		}
 	}
 }
 
@@ -595,8 +678,21 @@
 	if (test_bit(NCI_DATA_EXCHANGE, &ndev->flags))
 		nci_data_exchange_complete(ndev, NULL, -EIO);
 
-	nci_clear_target_list(ndev);
-	atomic_set(&ndev->state, NCI_IDLE);
+	switch (ntf->type) {
+	case NCI_DEACTIVATE_TYPE_IDLE_MODE:
+		nci_clear_target_list(ndev);
+		atomic_set(&ndev->state, NCI_IDLE);
+		break;
+	case NCI_DEACTIVATE_TYPE_SLEEP_MODE:
+	case NCI_DEACTIVATE_TYPE_SLEEP_AF_MODE:
+		atomic_set(&ndev->state, NCI_W4_HOST_SELECT);
+		break;
+	case NCI_DEACTIVATE_TYPE_DISCOVERY:
+		nci_clear_target_list(ndev);
+		atomic_set(&ndev->state, NCI_DISCOVERY);
+		break;
+	}
+
 	nci_req_complete(ndev, NCI_STATUS_OK);
 }
 
diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c
index 43cb1c1..44989fc 100644
--- a/net/nfc/netlink.c
+++ b/net/nfc/netlink.c
@@ -810,6 +810,31 @@
 	return rc;
 }
 
+static int nfc_genl_activate_target(struct sk_buff *skb, struct genl_info *info)
+{
+	struct nfc_dev *dev;
+	u32 device_idx, target_idx, protocol;
+	int rc;
+
+	if (!info->attrs[NFC_ATTR_DEVICE_INDEX])
+		return -EINVAL;
+
+	device_idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
+
+	dev = nfc_get_device(device_idx);
+	if (!dev)
+		return -ENODEV;
+
+	target_idx = nla_get_u32(info->attrs[NFC_ATTR_TARGET_INDEX]);
+	protocol = nla_get_u32(info->attrs[NFC_ATTR_PROTOCOLS]);
+
+	nfc_deactivate_target(dev, target_idx);
+	rc = nfc_activate_target(dev, target_idx, protocol);
+
+	nfc_put_device(dev);
+	return 0;
+}
+
 static int nfc_genl_dep_link_up(struct sk_buff *skb, struct genl_info *info)
 {
 	struct nfc_dev *dev;
@@ -1285,6 +1310,51 @@
 	return 0;
 }
 
+static int nfc_se_io(struct nfc_dev *dev, u32 se_idx,
+		     u8 *apdu, size_t apdu_length,
+		     se_io_cb_t cb, void *cb_context)
+{
+	struct nfc_se *se;
+	int rc;
+
+	pr_debug("%s se index %d\n", dev_name(&dev->dev), se_idx);
+
+	device_lock(&dev->dev);
+
+	if (!device_is_registered(&dev->dev)) {
+		rc = -ENODEV;
+		goto error;
+	}
+
+	if (!dev->dev_up) {
+		rc = -ENODEV;
+		goto error;
+	}
+
+	if (!dev->ops->se_io) {
+		rc = -EOPNOTSUPP;
+		goto error;
+	}
+
+	se = nfc_find_se(dev, se_idx);
+	if (!se) {
+		rc = -EINVAL;
+		goto error;
+	}
+
+	if (se->state != NFC_SE_ENABLED) {
+		rc = -ENODEV;
+		goto error;
+	}
+
+	rc = dev->ops->se_io(dev, se_idx, apdu,
+			apdu_length, cb, cb_context);
+
+error:
+	device_unlock(&dev->dev);
+	return rc;
+}
+
 struct se_io_ctx {
 	u32 dev_idx;
 	u32 se_idx;
@@ -1367,7 +1437,7 @@
 	ctx->dev_idx = dev_idx;
 	ctx->se_idx = se_idx;
 
-	return dev->ops->se_io(dev, se_idx, apdu, apdu_len, se_io_cb, ctx);
+	return nfc_se_io(dev, se_idx, apdu, apdu_len, se_io_cb, ctx);
 }
 
 static const struct genl_ops nfc_genl_ops[] = {
@@ -1455,6 +1525,11 @@
 		.doit = nfc_genl_se_io,
 		.policy = nfc_genl_policy,
 	},
+	{
+		.cmd = NFC_CMD_ACTIVATE_TARGET,
+		.doit = nfc_genl_activate_target,
+		.policy = nfc_genl_policy,
+	},
 };