Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next into for-davem

Conflicts:
	net/nfc/netlink.c

Signed-off-by: John W. Linville <linville@tuxdriver.com>
diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt
index e45fa5c..e824c1e 100644
--- a/Documentation/feature-removal-schedule.txt
+++ b/Documentation/feature-removal-schedule.txt
@@ -508,18 +508,6 @@
 
 ----------------------------
 
-What:	Removing the pn544 raw driver.
-When:	3.6
-Why:	With the introduction of the NFC HCI and SHDL kernel layers, pn544.c
-	is being replaced by pn544_hci.c which is accessible through the netlink
-	and socket NFC APIs. Moreover, pn544.c is outdated and does not seem to
-	work properly with the latest Android stacks.
-	Having 2 drivers for the same hardware is confusing and as such we
-	should only keep the one following the kernel NFC APIs.
-Who:	Samuel Ortiz <sameo@linux.intel.com>
-
-----------------------------
-
 What:	setitimer accepts user NULL pointer (value)
 When:	3.6
 Why:	setitimer is not returning -EFAULT if user pointer is NULL. This
diff --git a/MAINTAINERS b/MAINTAINERS
index 53cc13c..eb30279 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4797,6 +4797,7 @@
 M:	Aloisio Almeida Jr <aloisio.almeida@openbossa.org>
 M:	Samuel Ortiz <sameo@linux.intel.com>
 L:	linux-wireless@vger.kernel.org
+L:	linux-nfc@lists.01.org (moderated for non-subscribers)
 S:	Maintained
 F:	net/nfc/
 F:	include/linux/nfc.h
diff --git a/drivers/bcma/host_pci.c b/drivers/bcma/host_pci.c
index f7b0af7..b6b4b5e 100644
--- a/drivers/bcma/host_pci.c
+++ b/drivers/bcma/host_pci.c
@@ -273,6 +273,7 @@
 	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4331) },
 	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4353) },
 	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4357) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4358) },
 	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4359) },
 	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4727) },
 	{ 0, },
diff --git a/drivers/bcma/sprom.c b/drivers/bcma/sprom.c
index 9ea4627..0d546b6 100644
--- a/drivers/bcma/sprom.c
+++ b/drivers/bcma/sprom.c
@@ -507,7 +507,9 @@
 		/* for these chips OTP is always available */
 		present = true;
 		break;
+	case BCMA_CHIP_ID_BCM43227:
 	case BCMA_CHIP_ID_BCM43228:
+	case BCMA_CHIP_ID_BCM43428:
 		present = chip_status & BCMA_CC_CHIPST_43228_OTP_PRESENT;
 		break;
 	default:
diff --git a/drivers/bluetooth/bluecard_cs.c b/drivers/bluetooth/bluecard_cs.c
index 0c0838d..0d26851 100644
--- a/drivers/bluetooth/bluecard_cs.c
+++ b/drivers/bluetooth/bluecard_cs.c
@@ -681,7 +681,7 @@
 	case HCI_SCODATA_PKT:
 		hdev->stat.sco_tx++;
 		break;
-	};
+	}
 
 	/* Prepend skb with frame type */
 	memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c
index 03b3acb..3f4bfc8 100644
--- a/drivers/bluetooth/btmrvl_sdio.c
+++ b/drivers/bluetooth/btmrvl_sdio.c
@@ -600,8 +600,7 @@
 exit:
 	if (ret) {
 		hdev->stat.err_rx++;
-		if (skb)
-			kfree_skb(skb);
+		kfree_skb(skb);
 	}
 
 	return ret;
diff --git a/drivers/bluetooth/btuart_cs.c b/drivers/bluetooth/btuart_cs.c
index 2f510a8..35a553a 100644
--- a/drivers/bluetooth/btuart_cs.c
+++ b/drivers/bluetooth/btuart_cs.c
@@ -446,7 +446,7 @@
 	case HCI_SCODATA_PKT:
 		hdev->stat.sco_tx++;
 		break;
-	};
+	}
 
 	/* Prepend skb with frame type */
 	memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index e5921d6..debda27 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -96,11 +96,12 @@
 	{ USB_DEVICE(0x0c10, 0x0000) },
 
 	/* Broadcom BCM20702A0 */
+	{ USB_DEVICE(0x04ca, 0x2003) },
 	{ USB_DEVICE(0x0489, 0xe042) },
 	{ USB_DEVICE(0x413c, 0x8197) },
 
 	/* Foxconn - Hon Hai */
-	{ USB_DEVICE(0x0489, 0xe033) },
+	{ USB_VENDOR_AND_INTERFACE_INFO(0x0489, 0xff, 0x01, 0x01) },
 
 	/*Broadcom devices with vendor specific id */
 	{ USB_VENDOR_AND_INTERFACE_INFO(0x0a5c, 0xff, 0x01, 0x01) },
diff --git a/drivers/bluetooth/btwilink.c b/drivers/bluetooth/btwilink.c
index 4ad7b35..60abf59 100644
--- a/drivers/bluetooth/btwilink.c
+++ b/drivers/bluetooth/btwilink.c
@@ -358,21 +358,7 @@
 	},
 };
 
-/* ------- Module Init/Exit interfaces ------ */
-static int __init btwilink_init(void)
-{
-	BT_INFO("Bluetooth Driver for TI WiLink - Version %s", VERSION);
-
-	return platform_driver_register(&btwilink_driver);
-}
-
-static void __exit btwilink_exit(void)
-{
-	platform_driver_unregister(&btwilink_driver);
-}
-
-module_init(btwilink_init);
-module_exit(btwilink_exit);
+module_platform_driver(btwilink_driver);
 
 /* ------ Module Info ------ */
 
diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
index 74e0966..c8abce3 100644
--- a/drivers/bluetooth/hci_ldisc.c
+++ b/drivers/bluetooth/hci_ldisc.c
@@ -531,7 +531,7 @@
 	default:
 		err = n_tty_ioctl_helper(tty, file, cmd, arg);
 		break;
-	};
+	}
 
 	return err;
 }
diff --git a/drivers/bluetooth/hci_ll.c b/drivers/bluetooth/hci_ll.c
index ff6d589..cfc7679 100644
--- a/drivers/bluetooth/hci_ll.c
+++ b/drivers/bluetooth/hci_ll.c
@@ -481,7 +481,7 @@
 			hu->hdev->stat.err_rx++;
 			ptr++; count--;
 			continue;
-		};
+		}
 
 		ptr++; count--;
 
diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c
index 3f72595..d8b7aed 100644
--- a/drivers/bluetooth/hci_vhci.c
+++ b/drivers/bluetooth/hci_vhci.c
@@ -156,7 +156,7 @@
 	case HCI_SCODATA_PKT:
 		data->hdev->stat.sco_tx++;
 		break;
-	};
+	}
 
 	return total;
 }
diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h
index 6169fbd..4521342 100644
--- a/drivers/net/wireless/ath/ath.h
+++ b/drivers/net/wireless/ath/ath.h
@@ -159,6 +159,7 @@
 
 	bool btcoex_enabled;
 	bool disable_ani;
+	bool antenna_diversity;
 };
 
 struct sk_buff *ath_rxbuf_alloc(struct ath_common *common,
diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c
index a0a202d..9fd6d9a 100644
--- a/drivers/net/wireless/ath/ath5k/base.c
+++ b/drivers/net/wireless/ath/ath5k/base.c
@@ -2446,6 +2446,7 @@
 	hw->flags = IEEE80211_HW_RX_INCLUDES_FCS |
 			IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
 			IEEE80211_HW_SIGNAL_DBM |
+			IEEE80211_HW_MFP_CAPABLE |
 			IEEE80211_HW_REPORTS_TX_ACK_STATUS;
 
 	hw->wiphy->interface_modes =
diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
index df61a09..7a28538 100644
--- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c
+++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
@@ -489,6 +489,9 @@
 	if (ath5k_modparam_nohwcrypt)
 		return -EOPNOTSUPP;
 
+	if (key->flags & IEEE80211_KEY_FLAG_RX_MGMT)
+		return -EOPNOTSUPP;
+
 	if (vif->type == NL80211_IFTYPE_ADHOC &&
 	    (key->cipher == WLAN_CIPHER_SUITE_TKIP ||
 	     key->cipher == WLAN_CIPHER_SUITE_CCMP) &&
@@ -523,7 +526,7 @@
 			if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
 				key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
 			if (key->cipher == WLAN_CIPHER_SUITE_CCMP)
-				key->flags |= IEEE80211_KEY_FLAG_SW_MGMT;
+				key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX;
 			ret = 0;
 		}
 		break;
diff --git a/drivers/net/wireless/ath/ath5k/phy.c b/drivers/net/wireless/ath/ath5k/phy.c
index 01c90ed..ab363f3 100644
--- a/drivers/net/wireless/ath/ath5k/phy.c
+++ b/drivers/net/wireless/ath/ath5k/phy.c
@@ -1975,11 +1975,13 @@
 			spur_delta_phase = (spur_offset << 18) / 25;
 			spur_freq_sigma_delta = (spur_delta_phase >> 10);
 			symbol_width = AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz / 2;
+			break;
 		case AR5K_BWMODE_5MHZ:
 			/* Both sample_freq and chip_freq are 10MHz (?) */
 			spur_delta_phase = (spur_offset << 19) / 25;
 			spur_freq_sigma_delta = (spur_delta_phase >> 10);
 			symbol_width = AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz / 4;
+			break;
 		default:
 			if (channel->band == IEEE80211_BAND_5GHZ) {
 				/* Both sample_freq and chip_freq are 40MHz */
diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c
index 86aeef4..7089f81 100644
--- a/drivers/net/wireless/ath/ath6kl/cfg80211.c
+++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c
@@ -1488,7 +1488,7 @@
 }
 
 static struct wireless_dev *ath6kl_cfg80211_add_iface(struct wiphy *wiphy,
-						      char *name,
+						      const char *name,
 						      enum nl80211_iftype type,
 						      u32 *flags,
 						      struct vif_params *params)
@@ -3477,7 +3477,7 @@
 	ar->num_vif--;
 }
 
-struct wireless_dev *ath6kl_interface_add(struct ath6kl *ar, char *name,
+struct wireless_dev *ath6kl_interface_add(struct ath6kl *ar, const char *name,
 					  enum nl80211_iftype type,
 					  u8 fw_vif_idx, u8 nw_type)
 {
diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.h b/drivers/net/wireless/ath/ath6kl/cfg80211.h
index 56b1ebe..780f777 100644
--- a/drivers/net/wireless/ath/ath6kl/cfg80211.h
+++ b/drivers/net/wireless/ath/ath6kl/cfg80211.h
@@ -25,7 +25,7 @@
 	ATH6KL_CFG_SUSPEND_SCHED_SCAN,
 };
 
-struct wireless_dev *ath6kl_interface_add(struct ath6kl *ar, char *name,
+struct wireless_dev *ath6kl_interface_add(struct ath6kl *ar, const char *name,
 					  enum nl80211_iftype type,
 					  u8 fw_vif_idx, u8 nw_type);
 void ath6kl_cfg80211_ch_switch_notify(struct ath6kl_vif *vif, int freq,
diff --git a/drivers/net/wireless/ath/ath9k/antenna.c b/drivers/net/wireless/ath/ath9k/antenna.c
index bbcfeb3..664844c 100644
--- a/drivers/net/wireless/ath/ath9k/antenna.c
+++ b/drivers/net/wireless/ath/ath9k/antenna.c
@@ -311,6 +311,9 @@
 					  struct ath_ant_comb *antcomb,
 					  int alt_ratio)
 {
+	ant_conf->main_gaintb = 0;
+	ant_conf->alt_gaintb = 0;
+
 	if (ant_conf->div_group == 0) {
 		/* Adjust the fast_div_bias based on main and alt lna conf */
 		switch ((ant_conf->main_lna_conf << 4) |
@@ -360,18 +363,12 @@
 			ant_conf->alt_lna_conf) {
 		case 0x01: /* A-B LNA2 */
 			ant_conf->fast_div_bias = 0x1;
-			ant_conf->main_gaintb = 0;
-			ant_conf->alt_gaintb = 0;
 			break;
 		case 0x02: /* A-B LNA1 */
 			ant_conf->fast_div_bias = 0x1;
-			ant_conf->main_gaintb = 0;
-			ant_conf->alt_gaintb = 0;
 			break;
 		case 0x03: /* A-B A+B */
 			ant_conf->fast_div_bias = 0x1;
-			ant_conf->main_gaintb = 0;
-			ant_conf->alt_gaintb = 0;
 			break;
 		case 0x10: /* LNA2 A-B */
 			if (!(antcomb->scan) &&
@@ -379,13 +376,9 @@
 				ant_conf->fast_div_bias = 0x3f;
 			else
 				ant_conf->fast_div_bias = 0x1;
-			ant_conf->main_gaintb = 0;
-			ant_conf->alt_gaintb = 0;
 			break;
 		case 0x12: /* LNA2 LNA1 */
 			ant_conf->fast_div_bias = 0x1;
-			ant_conf->main_gaintb = 0;
-			ant_conf->alt_gaintb = 0;
 			break;
 		case 0x13: /* LNA2 A+B */
 			if (!(antcomb->scan) &&
@@ -393,8 +386,6 @@
 				ant_conf->fast_div_bias = 0x3f;
 			else
 				ant_conf->fast_div_bias = 0x1;
-			ant_conf->main_gaintb = 0;
-			ant_conf->alt_gaintb = 0;
 			break;
 		case 0x20: /* LNA1 A-B */
 			if (!(antcomb->scan) &&
@@ -402,13 +393,9 @@
 				ant_conf->fast_div_bias = 0x3f;
 			else
 				ant_conf->fast_div_bias = 0x1;
-			ant_conf->main_gaintb = 0;
-			ant_conf->alt_gaintb = 0;
 			break;
 		case 0x21: /* LNA1 LNA2 */
 			ant_conf->fast_div_bias = 0x1;
-			ant_conf->main_gaintb = 0;
-			ant_conf->alt_gaintb = 0;
 			break;
 		case 0x23: /* LNA1 A+B */
 			if (!(antcomb->scan) &&
@@ -416,23 +403,15 @@
 				ant_conf->fast_div_bias = 0x3f;
 			else
 				ant_conf->fast_div_bias = 0x1;
-			ant_conf->main_gaintb = 0;
-			ant_conf->alt_gaintb = 0;
 			break;
 		case 0x30: /* A+B A-B */
 			ant_conf->fast_div_bias = 0x1;
-			ant_conf->main_gaintb = 0;
-			ant_conf->alt_gaintb = 0;
 			break;
 		case 0x31: /* A+B LNA2 */
 			ant_conf->fast_div_bias = 0x1;
-			ant_conf->main_gaintb = 0;
-			ant_conf->alt_gaintb = 0;
 			break;
 		case 0x32: /* A+B LNA1 */
 			ant_conf->fast_div_bias = 0x1;
-			ant_conf->main_gaintb = 0;
-			ant_conf->alt_gaintb = 0;
 			break;
 		default:
 			break;
@@ -443,18 +422,12 @@
 				ant_conf->alt_lna_conf) {
 		case 0x01: /* A-B LNA2 */
 			ant_conf->fast_div_bias = 0x1;
-			ant_conf->main_gaintb = 0;
-			ant_conf->alt_gaintb = 0;
 			break;
 		case 0x02: /* A-B LNA1 */
 			ant_conf->fast_div_bias = 0x1;
-			ant_conf->main_gaintb = 0;
-			ant_conf->alt_gaintb = 0;
 			break;
 		case 0x03: /* A-B A+B */
 			ant_conf->fast_div_bias = 0x1;
-			ant_conf->main_gaintb = 0;
-			ant_conf->alt_gaintb = 0;
 			break;
 		case 0x10: /* LNA2 A-B */
 			if (!(antcomb->scan) &&
@@ -462,13 +435,9 @@
 				ant_conf->fast_div_bias = 0x1;
 			else
 				ant_conf->fast_div_bias = 0x2;
-			ant_conf->main_gaintb = 0;
-			ant_conf->alt_gaintb = 0;
 			break;
 		case 0x12: /* LNA2 LNA1 */
 			ant_conf->fast_div_bias = 0x1;
-			ant_conf->main_gaintb = 0;
-			ant_conf->alt_gaintb = 0;
 			break;
 		case 0x13: /* LNA2 A+B */
 			if (!(antcomb->scan) &&
@@ -476,8 +445,6 @@
 				ant_conf->fast_div_bias = 0x1;
 			else
 				ant_conf->fast_div_bias = 0x2;
-			ant_conf->main_gaintb = 0;
-			ant_conf->alt_gaintb = 0;
 			break;
 		case 0x20: /* LNA1 A-B */
 			if (!(antcomb->scan) &&
@@ -485,13 +452,9 @@
 				ant_conf->fast_div_bias = 0x1;
 			else
 				ant_conf->fast_div_bias = 0x2;
-			ant_conf->main_gaintb = 0;
-			ant_conf->alt_gaintb = 0;
 			break;
 		case 0x21: /* LNA1 LNA2 */
 			ant_conf->fast_div_bias = 0x1;
-			ant_conf->main_gaintb = 0;
-			ant_conf->alt_gaintb = 0;
 			break;
 		case 0x23: /* LNA1 A+B */
 			if (!(antcomb->scan) &&
@@ -499,23 +462,77 @@
 				ant_conf->fast_div_bias = 0x1;
 			else
 				ant_conf->fast_div_bias = 0x2;
-			ant_conf->main_gaintb = 0;
-			ant_conf->alt_gaintb = 0;
 			break;
 		case 0x30: /* A+B A-B */
 			ant_conf->fast_div_bias = 0x1;
-			ant_conf->main_gaintb = 0;
-			ant_conf->alt_gaintb = 0;
 			break;
 		case 0x31: /* A+B LNA2 */
 			ant_conf->fast_div_bias = 0x1;
-			ant_conf->main_gaintb = 0;
-			ant_conf->alt_gaintb = 0;
 			break;
 		case 0x32: /* A+B LNA1 */
 			ant_conf->fast_div_bias = 0x1;
-			ant_conf->main_gaintb = 0;
-			ant_conf->alt_gaintb = 0;
+			break;
+		default:
+			break;
+		}
+	} else if (ant_conf->div_group == 3) {
+		switch ((ant_conf->main_lna_conf << 4) |
+			ant_conf->alt_lna_conf) {
+		case 0x01: /* A-B LNA2 */
+			ant_conf->fast_div_bias = 0x1;
+			break;
+		case 0x02: /* A-B LNA1 */
+			ant_conf->fast_div_bias = 0x39;
+			break;
+		case 0x03: /* A-B A+B */
+			ant_conf->fast_div_bias = 0x1;
+			break;
+		case 0x10: /* LNA2 A-B */
+			if ((antcomb->scan == 0) &&
+			    (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
+				ant_conf->fast_div_bias = 0x3f;
+			} else {
+				ant_conf->fast_div_bias = 0x1;
+			}
+			break;
+		case 0x12: /* LNA2 LNA1 */
+			ant_conf->fast_div_bias = 0x39;
+			break;
+		case 0x13: /* LNA2 A+B */
+			if ((antcomb->scan == 0) &&
+			    (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
+				ant_conf->fast_div_bias = 0x3f;
+			} else {
+				ant_conf->fast_div_bias = 0x1;
+			}
+			break;
+		case 0x20: /* LNA1 A-B */
+			if ((antcomb->scan == 0) &&
+			    (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
+				ant_conf->fast_div_bias = 0x3f;
+			} else {
+				ant_conf->fast_div_bias = 0x4;
+			}
+			break;
+		case 0x21: /* LNA1 LNA2 */
+			ant_conf->fast_div_bias = 0x6;
+			break;
+		case 0x23: /* LNA1 A+B */
+			if ((antcomb->scan == 0) &&
+			    (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
+				ant_conf->fast_div_bias = 0x3f;
+			} else {
+				ant_conf->fast_div_bias = 0x6;
+			}
+			break;
+		case 0x30: /* A+B A-B */
+			ant_conf->fast_div_bias = 0x1;
+			break;
+		case 0x31: /* A+B LNA2 */
+			ant_conf->fast_div_bias = 0x6;
+			break;
+		case 0x32: /* A+B LNA1 */
+			ant_conf->fast_div_bias = 0x1;
 			break;
 		default:
 			break;
@@ -759,6 +776,7 @@
 void ath_ant_comb_update(struct ath_softc *sc)
 {
 	struct ath_hw *ah = sc->sc_ah;
+	struct ath_common *common = ath9k_hw_common(ah);
 	struct ath_hw_antcomb_conf div_ant_conf;
 	u8 lna_conf;
 
@@ -773,4 +791,7 @@
 	div_ant_conf.alt_lna_conf = lna_conf;
 
 	ath9k_hw_antdiv_comb_conf_set(ah, &div_ant_conf);
+
+	if (common->antenna_diversity)
+		ath9k_hw_antctrl_shared_chain_lnadiv(ah, true);
 }
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
index b5659cb..e65aad0 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
@@ -3562,9 +3562,9 @@
 
 static void ar9003_hw_ant_ctrl_apply(struct ath_hw *ah, bool is2ghz)
 {
+	struct ath9k_hw_capabilities *pCap = &ah->caps;
 	int chain;
 	u32 regval;
-	u32 ant_div_ctl1;
 	static const u32 switch_chain_reg[AR9300_MAX_CHAINS] = {
 			AR_PHY_SWITCH_CHAIN_0,
 			AR_PHY_SWITCH_CHAIN_1,
@@ -3629,6 +3629,16 @@
 		/* enable_lnadiv */
 		regval &= (~AR_PHY_ANT_DIV_LNADIV);
 		regval |= ((value >> 6) & 0x1) << AR_PHY_ANT_DIV_LNADIV_S;
+
+		if (AR_SREV_9565(ah)) {
+			if (ah->shared_chain_lnadiv) {
+				regval |= (1 << AR_PHY_ANT_SW_RX_PROT_S);
+			} else {
+				regval &= ~(1 << AR_PHY_ANT_DIV_LNADIV_S);
+				regval &= ~(1 << AR_PHY_ANT_SW_RX_PROT_S);
+			}
+		}
+
 		REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval);
 
 		/*enable fast_div */
@@ -3636,9 +3646,8 @@
 		regval &= (~AR_FAST_DIV_ENABLE);
 		regval |= ((value >> 7) & 0x1) << AR_FAST_DIV_ENABLE_S;
 		REG_WRITE(ah, AR_PHY_CCK_DETECT, regval);
-		ant_div_ctl1 = ah->eep_ops->get_eeprom(ah, EEP_ANT_DIV_CTL1);
-		/* check whether antenna diversity is enabled */
-		if ((ant_div_ctl1 >> 0x6) == 0x3) {
+
+		if (pCap->hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) {
 			regval = REG_READ(ah, AR_PHY_MC_GAIN_CTRL);
 			/*
 			 * clear bits 25-30 main_lnaconf, alt_lnaconf,
@@ -3655,10 +3664,7 @@
 				   AR_PHY_ANT_DIV_ALT_LNACONF_S);
 			REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval);
 		}
-
-
 	}
-
 }
 
 static void ar9003_hw_drive_strength_apply(struct ath_hw *ah)
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.c b/drivers/net/wireless/ath/ath9k/ar9003_mci.c
index b2e39e8..8dbb60b 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_mci.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.c
@@ -1027,6 +1027,7 @@
 
 		if (!(mci->config & ATH_MCI_CONFIG_DISABLE_OSLA))
 			ar9003_mci_osla_setup(ah, true);
+		REG_WRITE(ah, AR_SELFGEN_MASK, 0x02);
 	} else {
 		ar9003_mci_send_lna_take(ah, true);
 		udelay(5);
@@ -1235,6 +1236,10 @@
 	case MCI_STATE_NEED_FTP_STOMP:
 		value = !(mci->config & ATH_MCI_CONFIG_DISABLE_FTP_STOMP);
 		break;
+	case MCI_STATE_NEED_FLUSH_BT_INFO:
+		value = (!mci->unhalt_bt_gpm && mci->need_flush_btinfo) ? 1 : 0;
+		mci->need_flush_btinfo = false;
+		break;
 	default:
 		break;
 	}
@@ -1284,7 +1289,7 @@
 	}
 	REG_WRITE(ah, AR_DIAG_SW, (diag_sw | BIT(27) | BIT(19) | BIT(18)));
 	lna_ctrl = REG_READ(ah, AR_OBS_BUS_CTRL) & 0x3;
-	bt_sleep = REG_READ(ah, AR_MCI_RX_STATUS) & AR_MCI_RX_REMOTE_SLEEP;
+	bt_sleep = MS(REG_READ(ah, AR_MCI_RX_STATUS), AR_MCI_RX_REMOTE_SLEEP);
 
 	REG_WRITE(ah, AR_BTCOEX_CTRL2, btcoex_ctrl2);
 	REG_WRITE(ah, AR_DIAG_SW, diag_sw);
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.h b/drivers/net/wireless/ath/ath9k/ar9003_mci.h
index f3bef8d..30acf28 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_mci.h
+++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.h
@@ -200,6 +200,7 @@
 	MCI_STATE_RECOVER_RX,
 	MCI_STATE_NEED_FTP_STOMP,
 	MCI_STATE_DEBUG,
+	MCI_STATE_NEED_FLUSH_BT_INFO,
 	MCI_STATE_MAX
 };
 
@@ -211,7 +212,8 @@
 	MCI_GPM_COEX_WLAN_CHANNELS,
 	MCI_GPM_COEX_BT_PROFILE_INFO,
 	MCI_GPM_COEX_BT_STATUS_UPDATE,
-	MCI_GPM_COEX_BT_UPDATE_FLAGS
+	MCI_GPM_COEX_BT_UPDATE_FLAGS,
+	MCI_GPM_COEX_NOOP,
 };
 
 #define MCI_GPM_NOMORE  0
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
index 0d800c6..fc67844 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
@@ -605,9 +605,6 @@
 
 	if ((ah->caps.hw_caps & ATH9K_HW_CAP_APM) && (tx == 0x7))
 		REG_WRITE(ah, AR_SELFGEN_MASK, 0x3);
-	else if (AR_SREV_9462(ah))
-		/* xxx only when MCI support is enabled */
-		REG_WRITE(ah, AR_SELFGEN_MASK, 0x3);
 	else
 		REG_WRITE(ah, AR_SELFGEN_MASK, tx);
 
@@ -1294,6 +1291,9 @@
 	} else if (AR_SREV_9485(ah)) {
 		antconf->lna1_lna2_delta = -9;
 		antconf->div_group = 2;
+	} else if (AR_SREV_9565(ah)) {
+		antconf->lna1_lna2_delta = -3;
+		antconf->div_group = 3;
 	} else {
 		antconf->lna1_lna2_delta = -3;
 		antconf->div_group = 0;
@@ -1325,6 +1325,65 @@
 	REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval);
 }
 
+static void ar9003_hw_antctrl_shared_chain_lnadiv(struct ath_hw *ah,
+						  bool enable)
+{
+	u8 ant_div_ctl1;
+	u32 regval;
+
+	if (!AR_SREV_9565(ah))
+		return;
+
+	ah->shared_chain_lnadiv = enable;
+	ant_div_ctl1 = ah->eep_ops->get_eeprom(ah, EEP_ANT_DIV_CTL1);
+
+	regval = REG_READ(ah, AR_PHY_MC_GAIN_CTRL);
+	regval &= (~AR_ANT_DIV_CTRL_ALL);
+	regval |= (ant_div_ctl1 & 0x3f) << AR_ANT_DIV_CTRL_ALL_S;
+	regval &= ~AR_PHY_ANT_DIV_LNADIV;
+	regval |= ((ant_div_ctl1 >> 6) & 0x1) << AR_PHY_ANT_DIV_LNADIV_S;
+
+	if (enable)
+		regval |= AR_ANT_DIV_ENABLE;
+
+	REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval);
+
+	regval = REG_READ(ah, AR_PHY_CCK_DETECT);
+	regval &= ~AR_FAST_DIV_ENABLE;
+	regval |= ((ant_div_ctl1 >> 7) & 0x1) << AR_FAST_DIV_ENABLE_S;
+
+	if (enable)
+		regval |= AR_FAST_DIV_ENABLE;
+
+	REG_WRITE(ah, AR_PHY_CCK_DETECT, regval);
+
+	if (enable) {
+		REG_SET_BIT(ah, AR_PHY_MC_GAIN_CTRL,
+			    (1 << AR_PHY_ANT_SW_RX_PROT_S));
+		if (IS_CHAN_2GHZ(ah->curchan))
+			REG_SET_BIT(ah, AR_PHY_RESTART,
+				    AR_PHY_RESTART_ENABLE_DIV_M2FLAG);
+		REG_SET_BIT(ah, AR_BTCOEX_WL_LNADIV,
+			    AR_BTCOEX_WL_LNADIV_FORCE_ON);
+	} else {
+		REG_CLR_BIT(ah, AR_PHY_MC_GAIN_CTRL, AR_ANT_DIV_ENABLE);
+		REG_CLR_BIT(ah, AR_PHY_MC_GAIN_CTRL,
+			    (1 << AR_PHY_ANT_SW_RX_PROT_S));
+		REG_CLR_BIT(ah, AR_PHY_CCK_DETECT, AR_FAST_DIV_ENABLE);
+		REG_CLR_BIT(ah, AR_BTCOEX_WL_LNADIV,
+			    AR_BTCOEX_WL_LNADIV_FORCE_ON);
+
+		regval = REG_READ(ah, AR_PHY_MC_GAIN_CTRL);
+		regval &= ~(AR_PHY_ANT_DIV_MAIN_LNACONF |
+			AR_PHY_ANT_DIV_ALT_LNACONF |
+			AR_PHY_ANT_DIV_MAIN_GAINTB |
+			AR_PHY_ANT_DIV_ALT_GAINTB);
+		regval |= (AR_PHY_ANT_DIV_LNA1 << AR_PHY_ANT_DIV_MAIN_LNACONF_S);
+		regval |= (AR_PHY_ANT_DIV_LNA2 << AR_PHY_ANT_DIV_ALT_LNACONF_S);
+		REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval);
+	}
+}
+
 static int ar9003_hw_fast_chan_change(struct ath_hw *ah,
 				      struct ath9k_channel *chan,
 				      u8 *ini_reloaded)
@@ -1423,6 +1482,7 @@
 
 	ops->antdiv_comb_conf_get = ar9003_hw_antdiv_comb_conf_get;
 	ops->antdiv_comb_conf_set = ar9003_hw_antdiv_comb_conf_set;
+	ops->antctrl_shared_chain_lnadiv = ar9003_hw_antctrl_shared_chain_lnadiv;
 
 	ar9003_hw_set_nf_limits(ah);
 	ar9003_hw_set_radar_conf(ah);
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.h b/drivers/net/wireless/ath/ath9k/ar9003_phy.h
index fdabc9a..9a48e3d 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_phy.h
+++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.h
@@ -282,6 +282,8 @@
 
 #define AR_PHY_ANT_FAST_DIV_BIAS                0x00007e00
 #define AR_PHY_ANT_FAST_DIV_BIAS_S              9
+#define AR_PHY_ANT_SW_RX_PROT                   0x00800000
+#define AR_PHY_ANT_SW_RX_PROT_S                 23
 #define AR_PHY_ANT_DIV_LNADIV                   0x01000000
 #define AR_PHY_ANT_DIV_LNADIV_S                 24
 #define AR_PHY_ANT_DIV_ALT_LNACONF              0x06000000
@@ -422,6 +424,8 @@
 #define AR_PHY_FIND_SIG_RELSTEP        0x1f
 #define AR_PHY_FIND_SIG_RELSTEP_S         0
 #define AR_PHY_FIND_SIG_RELSTEP_SIGN_BIT  5
+#define AR_PHY_RESTART_ENABLE_DIV_M2FLAG 0x00200000
+#define AR_PHY_RESTART_ENABLE_DIV_M2FLAG_S 21
 #define AR_PHY_RESTART_DIV_GC   0x001C0000
 #define AR_PHY_RESTART_DIV_GC_S 18
 #define AR_PHY_RESTART_ENA      0x01
@@ -1261,4 +1265,24 @@
 #define AR_PHY_CL_TAB_CL_GAIN_MOD		0x1f
 #define AR_PHY_CL_TAB_CL_GAIN_MOD_S		0
 
+#define AR_BTCOEX_WL_LNADIV                                0x1a64
+#define AR_BTCOEX_WL_LNADIV_PREDICTED_PERIOD               0x00003FFF
+#define AR_BTCOEX_WL_LNADIV_PREDICTED_PERIOD_S             0
+#define AR_BTCOEX_WL_LNADIV_DPDT_IGNORE_PRIORITY           0x00004000
+#define AR_BTCOEX_WL_LNADIV_DPDT_IGNORE_PRIORITY_S         14
+#define AR_BTCOEX_WL_LNADIV_FORCE_ON                       0x00008000
+#define AR_BTCOEX_WL_LNADIV_FORCE_ON_S                     15
+#define AR_BTCOEX_WL_LNADIV_MODE_OPTION                    0x00030000
+#define AR_BTCOEX_WL_LNADIV_MODE_OPTION_S                  16
+#define AR_BTCOEX_WL_LNADIV_MODE                           0x007c0000
+#define AR_BTCOEX_WL_LNADIV_MODE_S                         18
+#define AR_BTCOEX_WL_LNADIV_ALLOWED_TX_ANTDIV_WL_TX_REQ    0x00800000
+#define AR_BTCOEX_WL_LNADIV_ALLOWED_TX_ANTDIV_WL_TX_REQ_S  23
+#define AR_BTCOEX_WL_LNADIV_DISABLE_TX_ANTDIV_ENABLE       0x01000000
+#define AR_BTCOEX_WL_LNADIV_DISABLE_TX_ANTDIV_ENABLE_S     24
+#define AR_BTCOEX_WL_LNADIV_CONTINUOUS_BT_ACTIVE_PROTECT   0x02000000
+#define AR_BTCOEX_WL_LNADIV_CONTINUOUS_BT_ACTIVE_PROTECT_S 25
+#define AR_BTCOEX_WL_LNADIV_BT_INACTIVE_THRESHOLD          0xFC000000
+#define AR_BTCOEX_WL_LNADIV_BT_INACTIVE_THRESHOLD_S        26
+
 #endif  /* AR9003_PHY_H */
diff --git a/drivers/net/wireless/ath/ath9k/ar9565_1p0_initvals.h b/drivers/net/wireless/ath/ath9k/ar9565_1p0_initvals.h
index fa9e093..843e79f 100644
--- a/drivers/net/wireless/ath/ath9k/ar9565_1p0_initvals.h
+++ b/drivers/net/wireless/ath/ath9k/ar9565_1p0_initvals.h
@@ -58,8 +58,6 @@
 	{0x00008040, 0x00000000},
 	{0x00008044, 0x00000000},
 	{0x00008048, 0x00000000},
-	{0x0000804c, 0xffffffff},
-	{0x00008050, 0xffffffff},
 	{0x00008054, 0x00000000},
 	{0x00008058, 0x00000000},
 	{0x0000805c, 0x000fc78f},
@@ -246,7 +244,7 @@
 	{0x00009e50, 0x00ff03f1},
 	{0x00009e54, 0xe4c355c7},
 	{0x00009e5c, 0xe9198724},
-	{0x00009fc0, 0x823e4788},
+	{0x00009fc0, 0x823e4fc8},
 	{0x00009fc4, 0x0001efb5},
 	{0x00009fcc, 0x40000014},
 	{0x0000a20c, 0x00000000},
@@ -291,7 +289,7 @@
 	{0x0000a3ec, 0x20202020},
 	{0x0000a3f0, 0x00000000},
 	{0x0000a3f4, 0x00000006},
-	{0x0000a3f8, 0x0cdbd380},
+	{0x0000a3f8, 0x0c9bd380},
 	{0x0000a3fc, 0x000f0f01},
 	{0x0000a400, 0x8fa91f01},
 	{0x0000a404, 0x00000000},
@@ -355,11 +353,11 @@
 	{0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c},
 	{0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce},
 	{0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021},
-	{0x00009e3c, 0xcf946220, 0xcf946220, 0xcf946222, 0xcf946222},
+	{0x00009e3c, 0xcf946222, 0xcf946222, 0xcf946222, 0xcf946222},
 	{0x00009e44, 0xfe321e27, 0xfe321e27, 0xfe291e27, 0xfe291e27},
 	{0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012},
 	{0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000},
-	{0x0000a204, 0x033187c0, 0x033187c4, 0x033187c4, 0x033187c0},
+	{0x0000a204, 0x07318fc0, 0x07318fc4, 0x07318fc4, 0x07318fc0},
 	{0x0000a208, 0x00000104, 0x00000104, 0x00000004, 0x00000004},
 	{0x0000a22c, 0x01026a2f, 0x01026a27, 0x01026a2f, 0x01026a2f},
 	{0x0000a230, 0x0000400a, 0x00004014, 0x00004016, 0x0000400b},
@@ -375,9 +373,9 @@
 	{0x0000a284, 0x00000000, 0x00000000, 0x00000150, 0x00000150},
 	{0x0000a288, 0x00100510, 0x00100510, 0x00100510, 0x00100510},
 	{0x0000a28c, 0x00021551, 0x00021551, 0x00021551, 0x00021551},
-	{0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18},
+	{0x0000a2c4, 0x00058d18, 0x00058d18, 0x00058d18, 0x00058d18},
 	{0x0000a2d0, 0x00071982, 0x00071982, 0x00071982, 0x00071982},
-	{0x0000a2d8, 0x7999a83a, 0x7999a83a, 0x7999a83a, 0x7999a83a},
+	{0x0000a2d8, 0x7999a83b, 0x7999a83b, 0x7999a83b, 0x7999a83b},
 	{0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
 	{0x0000ae04, 0x00800000, 0x00800000, 0x00800000, 0x00800000},
 	{0x0000ae18, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
@@ -417,7 +415,7 @@
 	{0x00016144, 0x02084080},
 	{0x00016148, 0x000080c0},
 	{0x00016280, 0x050a0001},
-	{0x00016284, 0x3d841400},
+	{0x00016284, 0x3d841440},
 	{0x00016288, 0x00000000},
 	{0x0001628c, 0xe3000000},
 	{0x00016290, 0xa1004080},
@@ -840,27 +838,27 @@
 	{0x0000a0b4, 0x00000000},
 	{0x0000a0b8, 0x00000000},
 	{0x0000a0bc, 0x00000000},
-	{0x0000a0c0, 0x301f3000},
-	{0x0000a0c4, 0x41004101},
-	{0x0000a0c8, 0x411e411f},
-	{0x0000a0cc, 0x411c411d},
-	{0x0000a0d0, 0x42434244},
-	{0x0000a0d4, 0x42414242},
-	{0x0000a0d8, 0x425f4240},
-	{0x0000a0dc, 0x5342425e},
-	{0x0000a0e0, 0x53405341},
-	{0x0000a0e4, 0x535e535f},
-	{0x0000a0e8, 0x7402535d},
-	{0x0000a0ec, 0x74007401},
-	{0x0000a0f0, 0x741e741f},
-	{0x0000a0f4, 0x7522741d},
-	{0x0000a0f8, 0x75207521},
-	{0x0000a0fc, 0x753e753f},
-	{0x0000a100, 0x76617662},
-	{0x0000a104, 0x767f7660},
-	{0x0000a108, 0x767d767e},
-	{0x0000a10c, 0x77e277e3},
-	{0x0000a110, 0x77e077e1},
+	{0x0000a0c0, 0x00bf00a0},
+	{0x0000a0c4, 0x11a011a1},
+	{0x0000a0c8, 0x11be11bf},
+	{0x0000a0cc, 0x11bc11bd},
+	{0x0000a0d0, 0x22632264},
+	{0x0000a0d4, 0x22612262},
+	{0x0000a0d8, 0x227f2260},
+	{0x0000a0dc, 0x4322227e},
+	{0x0000a0e0, 0x43204321},
+	{0x0000a0e4, 0x433e433f},
+	{0x0000a0e8, 0x4462433d},
+	{0x0000a0ec, 0x44604461},
+	{0x0000a0f0, 0x447e447f},
+	{0x0000a0f4, 0x5582447d},
+	{0x0000a0f8, 0x55805581},
+	{0x0000a0fc, 0x559e559f},
+	{0x0000a100, 0x66816682},
+	{0x0000a104, 0x669f6680},
+	{0x0000a108, 0x669d669e},
+	{0x0000a10c, 0x77627763},
+	{0x0000a110, 0x77607761},
 	{0x0000a114, 0x00000000},
 	{0x0000a118, 0x00000000},
 	{0x0000a11c, 0x00000000},
@@ -872,27 +870,27 @@
 	{0x0000a134, 0x00000000},
 	{0x0000a138, 0x00000000},
 	{0x0000a13c, 0x00000000},
-	{0x0000a140, 0x301f3000},
-	{0x0000a144, 0x41004101},
-	{0x0000a148, 0x411e411f},
-	{0x0000a14c, 0x411c411d},
-	{0x0000a150, 0x42434244},
-	{0x0000a154, 0x42414242},
-	{0x0000a158, 0x425f4240},
-	{0x0000a15c, 0x5342425e},
-	{0x0000a160, 0x53405341},
-	{0x0000a164, 0x535e535f},
-	{0x0000a168, 0x7402535d},
-	{0x0000a16c, 0x74007401},
-	{0x0000a170, 0x741e741f},
-	{0x0000a174, 0x7522741d},
-	{0x0000a178, 0x75207521},
-	{0x0000a17c, 0x753e753f},
-	{0x0000a180, 0x76617662},
-	{0x0000a184, 0x767f7660},
-	{0x0000a188, 0x767d767e},
-	{0x0000a18c, 0x77e277e3},
-	{0x0000a190, 0x77e077e1},
+	{0x0000a140, 0x00bf00a0},
+	{0x0000a144, 0x11a011a1},
+	{0x0000a148, 0x11be11bf},
+	{0x0000a14c, 0x11bc11bd},
+	{0x0000a150, 0x22632264},
+	{0x0000a154, 0x22612262},
+	{0x0000a158, 0x227f2260},
+	{0x0000a15c, 0x4322227e},
+	{0x0000a160, 0x43204321},
+	{0x0000a164, 0x433e433f},
+	{0x0000a168, 0x4462433d},
+	{0x0000a16c, 0x44604461},
+	{0x0000a170, 0x447e447f},
+	{0x0000a174, 0x5582447d},
+	{0x0000a178, 0x55805581},
+	{0x0000a17c, 0x559e559f},
+	{0x0000a180, 0x66816682},
+	{0x0000a184, 0x669f6680},
+	{0x0000a188, 0x669d669e},
+	{0x0000a18c, 0x77627763},
+	{0x0000a190, 0x77607761},
 	{0x0000a194, 0x00000000},
 	{0x0000a198, 0x00000000},
 	{0x0000a19c, 0x00000000},
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index 96b8331..8e27f4f 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -537,6 +537,7 @@
 #ifdef CONFIG_MAC80211_LEDS
 void ath_init_leds(struct ath_softc *sc);
 void ath_deinit_leds(struct ath_softc *sc);
+void ath_fill_led_pin(struct ath_softc *sc);
 #else
 static inline void ath_init_leds(struct ath_softc *sc)
 {
@@ -545,6 +546,9 @@
 static inline void ath_deinit_leds(struct ath_softc *sc)
 {
 }
+static inline void ath_fill_led_pin(struct ath_softc *sc)
+{
+}
 #endif
 
 /*******************************/
@@ -596,8 +600,6 @@
 	int main_conf;
 	enum ath9k_ant_div_comb_lna_conf first_quick_scan_conf;
 	enum ath9k_ant_div_comb_lna_conf second_quick_scan_conf;
-	int first_bias;
-	int second_bias;
 	bool first_ratio;
 	bool second_ratio;
 	unsigned long scan_start_time;
diff --git a/drivers/net/wireless/ath/ath9k/gpio.c b/drivers/net/wireless/ath/ath9k/gpio.c
index 45f2422..bf7d29e 100644
--- a/drivers/net/wireless/ath/ath9k/gpio.c
+++ b/drivers/net/wireless/ath/ath9k/gpio.c
@@ -44,25 +44,6 @@
 	if (AR_SREV_9100(sc->sc_ah))
 		return;
 
-	if (sc->sc_ah->led_pin < 0) {
-		if (AR_SREV_9287(sc->sc_ah))
-			sc->sc_ah->led_pin = ATH_LED_PIN_9287;
-		else if (AR_SREV_9485(sc->sc_ah))
-			sc->sc_ah->led_pin = ATH_LED_PIN_9485;
-		else if (AR_SREV_9300(sc->sc_ah))
-			sc->sc_ah->led_pin = ATH_LED_PIN_9300;
-		else if (AR_SREV_9462(sc->sc_ah) || AR_SREV_9565(sc->sc_ah))
-			sc->sc_ah->led_pin = ATH_LED_PIN_9462;
-		else
-			sc->sc_ah->led_pin = ATH_LED_PIN_DEF;
-	}
-
-	/* Configure gpio 1 for output */
-	ath9k_hw_cfg_output(sc->sc_ah, sc->sc_ah->led_pin,
-			    AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
-	/* LED off, active low */
-	ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 1);
-
 	if (!led_blink)
 		sc->led_cdev.default_trigger =
 			ieee80211_get_radio_led_name(sc->hw);
@@ -78,6 +59,31 @@
 
 	sc->led_registered = true;
 }
+
+void ath_fill_led_pin(struct ath_softc *sc)
+{
+	struct ath_hw *ah = sc->sc_ah;
+
+	if (AR_SREV_9100(ah) || (ah->led_pin >= 0))
+		return;
+
+	if (AR_SREV_9287(ah))
+		ah->led_pin = ATH_LED_PIN_9287;
+	else if (AR_SREV_9485(sc->sc_ah))
+		ah->led_pin = ATH_LED_PIN_9485;
+	else if (AR_SREV_9300(sc->sc_ah))
+		ah->led_pin = ATH_LED_PIN_9300;
+	else if (AR_SREV_9462(sc->sc_ah) || AR_SREV_9565(sc->sc_ah))
+		ah->led_pin = ATH_LED_PIN_9462;
+	else
+		ah->led_pin = ATH_LED_PIN_DEF;
+
+	/* Configure gpio 1 for output */
+	ath9k_hw_cfg_output(ah, ah->led_pin, AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
+
+	/* LED off, active low */
+	ath9k_hw_set_gpio(ah, ah->led_pin, 1);
+}
 #endif
 
 /*******************/
@@ -314,8 +320,10 @@
 	ath_dbg(ath9k_hw_common(ah), BTCOEX, "Starting btcoex timers\n");
 
 	/* make sure duty cycle timer is also stopped when resuming */
-	if (btcoex->hw_timer_enabled)
+	if (btcoex->hw_timer_enabled) {
 		ath9k_gen_timer_stop(sc->sc_ah, btcoex->no_stomp_timer);
+		btcoex->hw_timer_enabled = false;
+	}
 
 	btcoex->bt_priority_cnt = 0;
 	btcoex->bt_priority_time = jiffies;
@@ -336,18 +344,20 @@
 
 	del_timer_sync(&btcoex->period_timer);
 
-	if (btcoex->hw_timer_enabled)
+	if (btcoex->hw_timer_enabled) {
 		ath9k_gen_timer_stop(ah, btcoex->no_stomp_timer);
-
-	btcoex->hw_timer_enabled = false;
+		btcoex->hw_timer_enabled = false;
+	}
 }
 
 void ath9k_btcoex_stop_gen_timer(struct ath_softc *sc)
 {
 	struct ath_btcoex *btcoex = &sc->btcoex;
 
-	if (btcoex->hw_timer_enabled)
+	if (btcoex->hw_timer_enabled) {
 		ath9k_gen_timer_stop(sc->sc_ah, btcoex->no_stomp_timer);
+		btcoex->hw_timer_enabled = false;
+	}
 }
 
 u16 ath9k_btcoex_aggr_limit(struct ath_softc *sc, u32 max_4ms_framelen)
diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c
index ee6e50a..924c461 100644
--- a/drivers/net/wireless/ath/ath9k/hif_usb.c
+++ b/drivers/net/wireless/ath/ath9k/hif_usb.c
@@ -1072,14 +1072,15 @@
  */
 static void ath9k_hif_usb_firmware_fail(struct hif_device_usb *hif_dev)
 {
-	struct device *parent = hif_dev->udev->dev.parent;
+	struct device *dev = &hif_dev->udev->dev;
+	struct device *parent = dev->parent;
 
 	complete(&hif_dev->fw_done);
 
 	if (parent)
 		device_lock(parent);
 
-	device_release_driver(&hif_dev->udev->dev);
+	device_release_driver(dev);
 
 	if (parent)
 		device_unlock(parent);
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
index 61d096e..ca78e33 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
@@ -1445,7 +1445,7 @@
 				key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
 			if (priv->ah->sw_mgmt_crypto &&
 			    key->cipher == WLAN_CIPHER_SUITE_CCMP)
-				key->flags |= IEEE80211_KEY_FLAG_SW_MGMT;
+				key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX;
 			ret = 0;
 		}
 		break;
diff --git a/drivers/net/wireless/ath/ath9k/hw-ops.h b/drivers/net/wireless/ath/ath9k/hw-ops.h
index 265bf77..0f2b97f 100644
--- a/drivers/net/wireless/ath/ath9k/hw-ops.h
+++ b/drivers/net/wireless/ath/ath9k/hw-ops.h
@@ -78,6 +78,13 @@
 	ath9k_hw_ops(ah)->antdiv_comb_conf_set(ah, antconf);
 }
 
+static inline void ath9k_hw_antctrl_shared_chain_lnadiv(struct ath_hw *ah,
+							bool enable)
+{
+	if (ath9k_hw_ops(ah)->antctrl_shared_chain_lnadiv)
+		ath9k_hw_ops(ah)->antctrl_shared_chain_lnadiv(ah, enable);
+}
+
 /* Private hardware call ops */
 
 /* PHY ops */
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index 99cab44..2cc08a7 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -24,6 +24,7 @@
 #include "rc.h"
 #include "ar9003_mac.h"
 #include "ar9003_mci.h"
+#include "ar9003_phy.h"
 #include "debug.h"
 #include "ath9k.h"
 
@@ -1733,12 +1734,12 @@
 	if (!ret)
 		goto fail;
 
-	ath9k_hw_loadnf(ah, ah->curchan);
-	ath9k_hw_start_nfcal(ah, true);
-
 	if (ath9k_hw_mci_is_enabled(ah))
 		ar9003_mci_2g5g_switch(ah, false);
 
+	ath9k_hw_loadnf(ah, ah->curchan);
+	ath9k_hw_start_nfcal(ah, true);
+
 	if (AR_SREV_9271(ah))
 		ar9002_hw_load_ani_reg(ah, chan);
 
@@ -2025,6 +2026,9 @@
 
 	ath9k_hw_apply_gpio_override(ah);
 
+	if (AR_SREV_9565(ah) && ah->shared_chain_lnadiv)
+		REG_SET_BIT(ah, AR_BTCOEX_WL_LNADIV, AR_BTCOEX_WL_LNADIV_FORCE_ON);
+
 	return 0;
 }
 EXPORT_SYMBOL(ath9k_hw_reset);
@@ -2539,7 +2543,7 @@
 	}
 
 
-	if (AR_SREV_9330(ah) || AR_SREV_9485(ah)) {
+	if (AR_SREV_9330(ah) || AR_SREV_9485(ah) || AR_SREV_9565(ah)) {
 		ant_div_ctl1 = ah->eep_ops->get_eeprom(ah, EEP_ANT_DIV_CTL1);
 		/*
 		 * enable the diversity-combining algorithm only when
diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h
index 0d17ce0..17203b5 100644
--- a/drivers/net/wireless/ath/ath9k/hw.h
+++ b/drivers/net/wireless/ath/ath9k/hw.h
@@ -687,7 +687,7 @@
 			struct ath_hw_antcomb_conf *antconf);
 	void (*antdiv_comb_conf_set)(struct ath_hw *ah,
 			struct ath_hw_antcomb_conf *antconf);
-
+	void (*antctrl_shared_chain_lnadiv)(struct ath_hw *hw, bool enable);
 };
 
 struct ath_nf_limits {
@@ -731,6 +731,7 @@
 	bool aspm_enabled;
 	bool is_monitoring;
 	bool need_an_top2_fixup;
+	bool shared_chain_lnadiv;
 	u16 tx_trig_level;
 
 	u32 nf_regs[6];
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index f3ce5ca..fad3ccd 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -46,6 +46,10 @@
 module_param_named(btcoex_enable, ath9k_btcoex_enable, int, 0444);
 MODULE_PARM_DESC(btcoex_enable, "Enable wifi-BT coexistence");
 
+static int ath9k_enable_diversity;
+module_param_named(enable_diversity, ath9k_enable_diversity, int, 0444);
+MODULE_PARM_DESC(enable_diversity, "Enable Antenna diversity for AR9565");
+
 bool is_ath9k_unloaded;
 /* We use the hw_value as an index into our private channel structure */
 
@@ -546,6 +550,14 @@
 	common->debug_mask = ath9k_debug;
 	common->btcoex_enabled = ath9k_btcoex_enable == 1;
 	common->disable_ani = false;
+
+	/*
+	 * Enable Antenna diversity only when BTCOEX is disabled
+	 * and the user manually requests the feature.
+	 */
+	if (!common->btcoex_enabled && ath9k_enable_diversity)
+		common->antenna_diversity = 1;
+
 	spin_lock_init(&common->cc_lock);
 
 	spin_lock_init(&sc->sc_serial_rw);
@@ -597,6 +609,7 @@
 
 	ath9k_cmn_init_crypto(sc->sc_ah);
 	ath9k_init_misc(sc);
+	ath_fill_led_pin(sc);
 
 	if (common->bus_ops->aspm_init)
 		common->bus_ops->aspm_init(common);
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 3923ad9..31ab82e 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -1406,7 +1406,7 @@
 				key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
 			if (sc->sc_ah->sw_mgmt_crypto &&
 			    key->cipher == WLAN_CIPHER_SUITE_CCMP)
-				key->flags |= IEEE80211_KEY_FLAG_SW_MGMT;
+				key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX;
 			ret = 0;
 		}
 		break;
diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c
index 8f0e8d9..8f51e9e 100644
--- a/drivers/net/wireless/ath/ath9k/mci.c
+++ b/drivers/net/wireless/ath/ath9k/mci.c
@@ -80,6 +80,7 @@
 	struct ath_mci_profile_info *info, *tinfo;
 
 	mci->aggr_limit = 0;
+	mci->num_mgmt = 0;
 
 	if (list_empty(&mci->info))
 		return;
@@ -120,7 +121,14 @@
 	if (mci_hw->config & ATH_MCI_CONFIG_DISABLE_TUNING)
 		goto skip_tuning;
 
+	mci->aggr_limit = 0;
 	btcoex->duty_cycle = ath_mci_duty_cycle[num_profile];
+	btcoex->btcoex_period = ATH_MCI_DEF_BT_PERIOD;
+	if (NUM_PROF(mci))
+		btcoex->bt_stomp_type = ATH_BTCOEX_STOMP_LOW;
+	else
+		btcoex->bt_stomp_type = mci->num_mgmt ? ATH_BTCOEX_STOMP_ALL :
+							ATH_BTCOEX_STOMP_LOW;
 
 	if (num_profile == 1) {
 		info = list_first_entry(&mci->info,
@@ -132,7 +140,8 @@
 			else if (info->T == 6) {
 				mci->aggr_limit = 6;
 				btcoex->duty_cycle = 30;
-			}
+			} else
+				mci->aggr_limit = 6;
 			ath_dbg(common, MCI,
 				"Single SCO, aggregation limit %d 1/4 ms\n",
 				mci->aggr_limit);
@@ -241,8 +250,8 @@
 	ath_mci_update_scheme(sc);
 }
 
-static void ath_mci_process_profile(struct ath_softc *sc,
-				    struct ath_mci_profile_info *info)
+static u8 ath_mci_process_profile(struct ath_softc *sc,
+				  struct ath_mci_profile_info *info)
 {
 	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
 	struct ath_btcoex *btcoex = &sc->btcoex;
@@ -268,25 +277,15 @@
 
 	if (info->start) {
 		if (!entry && !ath_mci_add_profile(common, mci, info))
-			return;
+			return 0;
 	} else
 		ath_mci_del_profile(common, mci, entry);
 
-	btcoex->btcoex_period = ATH_MCI_DEF_BT_PERIOD;
-	mci->aggr_limit = mci->num_sco ? 6 : 0;
-
-	btcoex->duty_cycle = ath_mci_duty_cycle[NUM_PROF(mci)];
-	if (NUM_PROF(mci))
-		btcoex->bt_stomp_type = ATH_BTCOEX_STOMP_LOW;
-	else
-		btcoex->bt_stomp_type = mci->num_mgmt ? ATH_BTCOEX_STOMP_ALL :
-							ATH_BTCOEX_STOMP_LOW;
-
-	ieee80211_queue_work(sc->hw, &sc->mci_work);
+	return 1;
 }
 
-static void ath_mci_process_status(struct ath_softc *sc,
-				   struct ath_mci_profile_status *status)
+static u8 ath_mci_process_status(struct ath_softc *sc,
+				 struct ath_mci_profile_status *status)
 {
 	struct ath_btcoex *btcoex = &sc->btcoex;
 	struct ath_mci_profile *mci = &btcoex->mci;
@@ -295,14 +294,14 @@
 
 	/* Link status type are not handled */
 	if (status->is_link)
-		return;
+		return 0;
 
 	info.conn_handle = status->conn_handle;
 	if (ath_mci_find_profile(mci, &info))
-		return;
+		return 0;
 
 	if (status->conn_handle >= ATH_MCI_MAX_PROFILE)
-		return;
+		return 0;
 
 	if (status->is_critical)
 		__set_bit(status->conn_handle, mci->status);
@@ -316,7 +315,9 @@
 	} while (++i < ATH_MCI_MAX_PROFILE);
 
 	if (old_num_mgmt != mci->num_mgmt)
-		ieee80211_queue_work(sc->hw, &sc->mci_work);
+		return 1;
+
+	return 0;
 }
 
 static void ath_mci_msg(struct ath_softc *sc, u8 opcode, u8 *rx_payload)
@@ -325,9 +326,16 @@
 	struct ath_mci_profile_info profile_info;
 	struct ath_mci_profile_status profile_status;
 	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-	u8 major, minor;
+	u8 major, minor, update_scheme = 0;
 	u32 seq_num;
 
+	if (ar9003_mci_state(ah, MCI_STATE_NEED_FLUSH_BT_INFO) &&
+	    ar9003_mci_state(ah, MCI_STATE_ENABLE)) {
+		ath_dbg(common, MCI, "(MCI) Need to flush BT profiles\n");
+		ath_mci_flush_profile(&sc->btcoex.mci);
+		ar9003_mci_state(ah, MCI_STATE_SEND_STATUS_QUERY);
+	}
+
 	switch (opcode) {
 	case MCI_GPM_COEX_VERSION_QUERY:
 		ar9003_mci_state(ah, MCI_STATE_SEND_WLAN_COEX_VERSION);
@@ -353,7 +361,7 @@
 			break;
 		}
 
-		ath_mci_process_profile(sc, &profile_info);
+		update_scheme += ath_mci_process_profile(sc, &profile_info);
 		break;
 	case MCI_GPM_COEX_BT_STATUS_UPDATE:
 		profile_status.is_link = *(rx_payload +
@@ -369,12 +377,14 @@
 			profile_status.is_link, profile_status.conn_handle,
 			profile_status.is_critical, seq_num);
 
-		ath_mci_process_status(sc, &profile_status);
+		update_scheme += ath_mci_process_status(sc, &profile_status);
 		break;
 	default:
 		ath_dbg(common, MCI, "Unknown GPM COEX message = 0x%02x\n", opcode);
 		break;
 	}
+	if (update_scheme)
+		ieee80211_queue_work(sc->hw, &sc->mci_work);
 }
 
 int ath_mci_setup(struct ath_softc *sc)
@@ -568,9 +578,11 @@
 	}
 
 	if ((mci_int & AR_MCI_INTERRUPT_RX_INVALID_HDR) ||
-	    (mci_int & AR_MCI_INTERRUPT_CONT_INFO_TIMEOUT))
+	    (mci_int & AR_MCI_INTERRUPT_CONT_INFO_TIMEOUT)) {
 		mci_int &= ~(AR_MCI_INTERRUPT_RX_INVALID_HDR |
 			     AR_MCI_INTERRUPT_CONT_INFO_TIMEOUT);
+		ath_mci_msg(sc, MCI_GPM_COEX_NOOP, NULL);
+	}
 }
 
 void ath_mci_enable(struct ath_softc *sc)
diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c
index a8f6126..c0c5996 100644
--- a/drivers/net/wireless/ath/ath9k/pci.c
+++ b/drivers/net/wireless/ath/ath9k/pci.c
@@ -128,8 +128,9 @@
 	if (!parent)
 		return;
 
-	if (ath9k_hw_get_btcoex_scheme(ah) != ATH_BTCOEX_CFG_NONE) {
-		/* Bluetooth coexistance requires disabling ASPM. */
+	if ((ath9k_hw_get_btcoex_scheme(ah) != ATH_BTCOEX_CFG_NONE) &&
+	    (AR_SREV_9285(ah))) {
+		/* Bluetooth coexistance requires disabling ASPM for AR9285. */
 		pci_read_config_byte(pdev, pos + PCI_EXP_LNKCTL, &aspm);
 		aspm &= ~(PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1);
 		pci_write_config_byte(pdev, pos + PCI_EXP_LNKCTL, aspm);
diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c
index 4b12c347..27ed80b5 100644
--- a/drivers/net/wireless/ath/ath9k/rc.c
+++ b/drivers/net/wireless/ath/ath9k/rc.c
@@ -1222,11 +1222,14 @@
 			caps |= WLAN_RC_TS_FLAG | WLAN_RC_DS_FLAG;
 		else if (sta->ht_cap.mcs.rx_mask[1])
 			caps |= WLAN_RC_DS_FLAG;
-		if (sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)
+		if (sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) {
 			caps |= WLAN_RC_40_FLAG;
-		if (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40 ||
-		    sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20)
-			caps |= WLAN_RC_SGI_FLAG;
+			if (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40)
+				caps |= WLAN_RC_SGI_FLAG;
+		} else {
+			if (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20)
+				caps |= WLAN_RC_SGI_FLAG;
+		}
 	}
 
 	return caps;
diff --git a/drivers/net/wireless/ath/carl9170/mac.c b/drivers/net/wireless/ath/carl9170/mac.c
index f867628..e3b1b6e 100644
--- a/drivers/net/wireless/ath/carl9170/mac.c
+++ b/drivers/net/wireless/ath/carl9170/mac.c
@@ -304,7 +304,8 @@
 	struct ath_common *common = &ar->common;
 	u8 *mac_addr, *bssid;
 	u32 cam_mode = AR9170_MAC_CAM_DEFAULTS;
-	u32 enc_mode = AR9170_MAC_ENCRYPTION_DEFAULTS;
+	u32 enc_mode = AR9170_MAC_ENCRYPTION_DEFAULTS |
+		AR9170_MAC_ENCRYPTION_MGMT_RX_SOFTWARE;
 	u32 rx_ctrl = AR9170_MAC_RX_CTRL_DEAGG |
 		      AR9170_MAC_RX_CTRL_SHORT_FILTER;
 	u32 sniffer = AR9170_MAC_SNIFFER_DEFAULTS;
diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c
index 18554ab..67997b3 100644
--- a/drivers/net/wireless/ath/carl9170/main.c
+++ b/drivers/net/wireless/ath/carl9170/main.c
@@ -1149,6 +1149,7 @@
 		break;
 	case WLAN_CIPHER_SUITE_CCMP:
 		ktype = AR9170_ENC_ALG_AESCCMP;
+		key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX;
 		break;
 	default:
 		return -EOPNOTSUPP;
@@ -1780,6 +1781,7 @@
 	hw->wiphy->interface_modes = 0;
 
 	hw->flags |= IEEE80211_HW_RX_INCLUDES_FCS |
+		     IEEE80211_HW_MFP_CAPABLE |
 		     IEEE80211_HW_REPORTS_TX_ACK_STATUS |
 		     IEEE80211_HW_SUPPORTS_PS |
 		     IEEE80211_HW_PS_NULLFUNC_STACK |
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
index 8e7e692..3b2c4c2 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
@@ -185,7 +185,7 @@
 	return err;
 }
 
-static int
+int
 brcmf_sdio_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr,
 			void *data, bool write)
 {
@@ -249,7 +249,9 @@
 	int retval;
 
 	brcmf_dbg(INFO, "addr:0x%08x\n", addr);
+	sdio_claim_host(sdiodev->func[1]);
 	retval = brcmf_sdio_regrw_helper(sdiodev, addr, &data, false);
+	sdio_release_host(sdiodev->func[1]);
 	brcmf_dbg(INFO, "data:0x%02x\n", data);
 
 	if (ret)
@@ -264,7 +266,9 @@
 	int retval;
 
 	brcmf_dbg(INFO, "addr:0x%08x\n", addr);
+	sdio_claim_host(sdiodev->func[1]);
 	retval = brcmf_sdio_regrw_helper(sdiodev, addr, &data, false);
+	sdio_release_host(sdiodev->func[1]);
 	brcmf_dbg(INFO, "data:0x%08x\n", data);
 
 	if (ret)
@@ -279,7 +283,9 @@
 	int retval;
 
 	brcmf_dbg(INFO, "addr:0x%08x, data:0x%02x\n", addr, data);
+	sdio_claim_host(sdiodev->func[1]);
 	retval = brcmf_sdio_regrw_helper(sdiodev, addr, &data, true);
+	sdio_release_host(sdiodev->func[1]);
 
 	if (ret)
 		*ret = retval;
@@ -291,7 +297,9 @@
 	int retval;
 
 	brcmf_dbg(INFO, "addr:0x%08x, data:0x%08x\n", addr, data);
+	sdio_claim_host(sdiodev->func[1]);
 	retval = brcmf_sdio_regrw_helper(sdiodev, addr, &data, true);
+	sdio_release_host(sdiodev->func[1]);
 
 	if (ret)
 		*ret = retval;
@@ -356,15 +364,20 @@
 	brcmf_dbg(INFO, "fun = %d, addr = 0x%x, size = %d\n",
 		  fn, addr, pkt->len);
 
+	sdio_claim_host(sdiodev->func[1]);
+
 	width = (flags & SDIO_REQ_4BYTE) ? 4 : 2;
 	err = brcmf_sdcard_recv_prepare(sdiodev, fn, flags, width, &addr);
 	if (err)
-		return err;
+		goto done;
 
 	incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC;
 	err = brcmf_sdioh_request_buffer(sdiodev, incr_fix, SDIOH_READ,
 					 fn, addr, pkt);
 
+done:
+	sdio_release_host(sdiodev->func[1]);
+
 	return err;
 }
 
@@ -378,15 +391,20 @@
 	brcmf_dbg(INFO, "fun = %d, addr = 0x%x, size = %d\n",
 		  fn, addr, pktq->qlen);
 
+	sdio_claim_host(sdiodev->func[1]);
+
 	width = (flags & SDIO_REQ_4BYTE) ? 4 : 2;
 	err = brcmf_sdcard_recv_prepare(sdiodev, fn, flags, width, &addr);
 	if (err)
-		return err;
+		goto done;
 
 	incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC;
 	err = brcmf_sdioh_request_chain(sdiodev, incr_fix, SDIOH_READ, fn, addr,
 					pktq);
 
+done:
+	sdio_release_host(sdiodev->func[1]);
+
 	return err;
 }
 
@@ -428,10 +446,12 @@
 	if (flags & SDIO_REQ_ASYNC)
 		return -ENOTSUPP;
 
+	sdio_claim_host(sdiodev->func[1]);
+
 	if (bar0 != sdiodev->sbwad) {
 		err = brcmf_sdcard_set_sbaddr_window(sdiodev, bar0);
 		if (err)
-			return err;
+			goto done;
 
 		sdiodev->sbwad = bar0;
 	}
@@ -443,8 +463,13 @@
 	if (width == 4)
 		addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
 
-	return brcmf_sdioh_request_buffer(sdiodev, incr_fix, SDIOH_WRITE, fn,
-					  addr, pkt);
+	err = brcmf_sdioh_request_buffer(sdiodev, incr_fix, SDIOH_WRITE, fn,
+					 addr, pkt);
+
+done:
+	sdio_release_host(sdiodev->func[1]);
+
+	return err;
 }
 
 int brcmf_sdcard_rwdata(struct brcmf_sdio_dev *sdiodev, uint rw, u32 addr,
@@ -485,8 +510,10 @@
 	brcmf_dbg(TRACE, "Enter\n");
 
 	/* issue abort cmd52 command through F0 */
+	sdio_claim_host(sdiodev->func[1]);
 	brcmf_sdioh_request_byte(sdiodev, SDIOH_WRITE, SDIO_FUNC_0,
 				 SDIO_CCCR_ABORT, &t_func);
+	sdio_release_host(sdiodev->func[1]);
 
 	brcmf_dbg(TRACE, "Exit\n");
 	return 0;
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c
index e0b313c..70f21a43 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c
@@ -103,7 +103,6 @@
 	if (regaddr == SDIO_CCCR_IOEx) {
 		sdfunc = sdiodev->func[2];
 		if (sdfunc) {
-			sdio_claim_host(sdfunc);
 			if (*byte & SDIO_FUNC_ENABLE_2) {
 				/* Enable Function 2 */
 				err_ret = sdio_enable_func(sdfunc);
@@ -119,7 +118,6 @@
 						  "Disable F2 failed:%d\n",
 						  err_ret);
 			}
-			sdio_release_host(sdfunc);
 		}
 	} else if ((regaddr == SDIO_CCCR_ABORT) ||
 		   (regaddr == SDIO_CCCR_IENx)) {
@@ -128,17 +126,13 @@
 		if (!sdfunc)
 			return -ENOMEM;
 		sdfunc->num = 0;
-		sdio_claim_host(sdfunc);
 		sdio_writeb(sdfunc, *byte, regaddr, &err_ret);
-		sdio_release_host(sdfunc);
 		kfree(sdfunc);
 	} else if (regaddr < 0xF0) {
 		brcmf_dbg(ERROR, "F0 Wr:0x%02x: write disallowed\n", regaddr);
 		err_ret = -EPERM;
 	} else {
-		sdio_claim_host(sdfunc);
 		sdio_f0_writeb(sdfunc, *byte, regaddr, &err_ret);
-		sdio_release_host(sdfunc);
 	}
 
 	return err_ret;
@@ -159,7 +153,6 @@
 		/* handle F0 separately */
 		err_ret = brcmf_sdioh_f0_write_byte(sdiodev, regaddr, byte);
 	} else {
-		sdio_claim_host(sdiodev->func[func]);
 		if (rw) /* CMD52 Write */
 			sdio_writeb(sdiodev->func[func], *byte, regaddr,
 				    &err_ret);
@@ -170,7 +163,6 @@
 			*byte = sdio_readb(sdiodev->func[func], regaddr,
 					   &err_ret);
 		}
-		sdio_release_host(sdiodev->func[func]);
 	}
 
 	if (err_ret)
@@ -197,8 +189,6 @@
 	brcmf_pm_resume_wait(sdiodev, &sdiodev->request_word_wait);
 	if (brcmf_pm_resume_error(sdiodev))
 		return -EIO;
-	/* Claim host controller */
-	sdio_claim_host(sdiodev->func[func]);
 
 	if (rw) {		/* CMD52 Write */
 		if (nbytes == 4)
@@ -219,9 +209,6 @@
 			brcmf_dbg(ERROR, "Invalid nbytes: %d\n", nbytes);
 	}
 
-	/* Release host controller */
-	sdio_release_host(sdiodev->func[func]);
-
 	if (err_ret)
 		brcmf_dbg(ERROR, "Failed to %s word, Err: 0x%08x\n",
 			  rw ? "write" : "read", err_ret);
@@ -275,9 +262,6 @@
 	if (brcmf_pm_resume_error(sdiodev))
 		return -EIO;
 
-	/* Claim host controller */
-	sdio_claim_host(sdiodev->func[func]);
-
 	skb_queue_walk(pktq, pkt) {
 		uint pkt_len = pkt->len;
 		pkt_len += 3;
@@ -300,9 +284,6 @@
 		SGCount++;
 	}
 
-	/* Release host controller */
-	sdio_release_host(sdiodev->func[func]);
-
 	brcmf_dbg(TRACE, "Exit\n");
 	return err_ret;
 }
@@ -328,9 +309,6 @@
 	if (brcmf_pm_resume_error(sdiodev))
 		return -EIO;
 
-	/* Claim host controller */
-	sdio_claim_host(sdiodev->func[func]);
-
 	pkt_len += 3;
 	pkt_len &= (uint)~3;
 
@@ -344,9 +322,6 @@
 			  write ? "TX" : "RX", pkt, addr, pkt_len);
 	}
 
-	/* Release host controller */
-	sdio_release_host(sdiodev->func[func]);
-
 	return status;
 }
 
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h
index 4766d9f..55e489d 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h
@@ -682,10 +682,6 @@
 
 extern void brcmf_del_if(struct brcmf_pub *drvr, int ifidx);
 
-/* Send packet to dongle via data channel */
-extern int brcmf_sendpkt(struct brcmf_pub *drvr, int ifidx,\
-			 struct sk_buff *pkt);
-
 extern void brcmf_c_pktfilter_offload_set(struct brcmf_pub *drvr, char *arg);
 extern void brcmf_c_pktfilter_offload_enable(struct brcmf_pub *drvr, char *arg,
 					     int enable, int master_mode);
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c
index f6b862d..fbecde7 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c
@@ -80,8 +80,10 @@
 	strncpy(buf, name, buflen);
 
 	/* append data onto the end of the name string */
-	memcpy(&buf[len], data, datalen);
-	len += datalen;
+	if (data && datalen) {
+		memcpy(&buf[len], data, datalen);
+		len += datalen;
+	}
 
 	return len;
 }
@@ -431,13 +433,7 @@
 	}
 
 	/* show any appended data */
-	if (datalen) {
-		buf = (unsigned char *) event_data;
-		brcmf_dbg(EVENT, " data (%d) : ", datalen);
-		for (i = 0; i < datalen; i++)
-			brcmf_dbg(EVENT, " 0x%02x ", *buf++);
-		brcmf_dbg(EVENT, "\n");
-	}
+	brcmf_dbg_hex_dump(datalen, event_data, datalen, "Received data");
 }
 #endif				/* DEBUG */
 
@@ -528,8 +524,9 @@
 	}
 
 #ifdef DEBUG
-	brcmf_c_show_host_event(event, event_data);
-#endif				/* DEBUG */
+	if (BRCMF_EVENT_ON())
+		brcmf_c_show_host_event(event, event_data);
+#endif /* DEBUG */
 
 	return 0;
 }
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h
index b784920..fb508c2 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h
@@ -55,6 +55,7 @@
 #define BRCMF_HDRS_ON()		(brcmf_msg_level & BRCMF_HDRS_VAL)
 #define BRCMF_BYTES_ON()	(brcmf_msg_level & BRCMF_BYTES_VAL)
 #define BRCMF_GLOM_ON()		(brcmf_msg_level & BRCMF_GLOM_VAL)
+#define BRCMF_EVENT_ON()	(brcmf_msg_level & BRCMF_EVENT_VAL)
 
 #else	/* (defined DEBUG) || (defined DEBUG) */
 
@@ -65,6 +66,7 @@
 #define BRCMF_HDRS_ON()		0
 #define BRCMF_BYTES_ON()	0
 #define BRCMF_GLOM_ON()		0
+#define BRCMF_EVENT_ON()	0
 
 #endif				/* defined(DEBUG) */
 
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
index b08f3474..d7c76ce 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
@@ -272,30 +272,6 @@
 	schedule_work(&drvr->multicast_work);
 }
 
-int brcmf_sendpkt(struct brcmf_pub *drvr, int ifidx, struct sk_buff *pktbuf)
-{
-	/* Reject if down */
-	if (!drvr->bus_if->drvr_up || (drvr->bus_if->state == BRCMF_BUS_DOWN))
-		return -ENODEV;
-
-	/* Update multicast statistic */
-	if (pktbuf->len >= ETH_ALEN) {
-		u8 *pktdata = (u8 *) (pktbuf->data);
-		struct ethhdr *eh = (struct ethhdr *)pktdata;
-
-		if (is_multicast_ether_addr(eh->h_dest))
-			drvr->tx_multicast++;
-		if (ntohs(eh->h_proto) == ETH_P_PAE)
-			atomic_inc(&drvr->pend_8021x_cnt);
-	}
-
-	/* If the protocol uses a data header, apply it */
-	brcmf_proto_hdrpush(drvr, ifidx, pktbuf);
-
-	/* Use bus module to send data frame */
-	return drvr->bus_if->brcmf_bus_txdata(drvr->dev, pktbuf);
-}
-
 static int brcmf_netdev_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 {
 	int ret;
@@ -338,7 +314,22 @@
 		}
 	}
 
-	ret = brcmf_sendpkt(drvr, ifp->idx, skb);
+	/* Update multicast statistic */
+	if (skb->len >= ETH_ALEN) {
+		u8 *pktdata = (u8 *)(skb->data);
+		struct ethhdr *eh = (struct ethhdr *)pktdata;
+
+		if (is_multicast_ether_addr(eh->h_dest))
+			drvr->tx_multicast++;
+		if (ntohs(eh->h_proto) == ETH_P_PAE)
+			atomic_inc(&drvr->pend_8021x_cnt);
+	}
+
+	/* If the protocol uses a data header, apply it */
+	brcmf_proto_hdrpush(drvr, ifp->idx, skb);
+
+	/* Use bus module to send data frame */
+	ret =  drvr->bus_if->brcmf_bus_txdata(drvr->dev, skb);
 
 done:
 	if (ret)
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
index 4580ff3..3564686 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
@@ -482,6 +482,15 @@
 	__le32 brpt_addr;
 };
 
+/* SDIO read frame info */
+struct brcmf_sdio_read {
+	u8 seq_num;
+	u8 channel;
+	u16 len;
+	u16 len_left;
+	u16 len_nxtfrm;
+	u8 dat_offset;
+};
 
 /* misc chip info needed by some of the routines */
 /* Private data for SDIO bus interaction */
@@ -494,9 +503,8 @@
 	u32 ramsize;		/* Size of RAM in SOCRAM (bytes) */
 
 	u32 hostintmask;	/* Copy of Host Interrupt Mask */
-	u32 intstatus;	/* Intstatus bits (events) pending */
-	bool dpc_sched;		/* Indicates DPC schedule (intrpt rcvd) */
-	bool fcstate;		/* State of dongle flow-control */
+	atomic_t intstatus;	/* Intstatus bits (events) pending */
+	atomic_t fcstate;	/* State of dongle flow-control */
 
 	uint blocksize;		/* Block size of SDIO transfers */
 	uint roundup;		/* Max roundup limit */
@@ -508,9 +516,11 @@
 
 	u8 hdrbuf[MAX_HDR_READ + BRCMF_SDALIGN];
 	u8 *rxhdr;		/* Header of current rx frame (in hdrbuf) */
-	u16 nextlen;		/* Next Read Len from last header */
 	u8 rx_seq;		/* Receive sequence number (expected) */
+	struct brcmf_sdio_read cur_read;
+				/* info of current read frame */
 	bool rxskip;		/* Skip receive (awaiting NAK ACK) */
+	bool rxpending;		/* Data frame pending in dongle */
 
 	uint rxbound;		/* Rx frames to read before resched */
 	uint txbound;		/* Tx frames to send before resched */
@@ -531,7 +541,7 @@
 
 	bool intr;		/* Use interrupts */
 	bool poll;		/* Use polling */
-	bool ipend;		/* Device interrupt is pending */
+	atomic_t ipend;		/* Device interrupt is pending */
 	uint spurious;		/* Count of spurious interrupts */
 	uint pollrate;		/* Ticks between device polls */
 	uint polltick;		/* Tick counter */
@@ -549,12 +559,9 @@
 	s32 idleclock;	/* How to set bus driver when idle */
 	s32 sd_rxchain;
 	bool use_rxchain;	/* If brcmf should use PKT chains */
-	bool sleeping;		/* Is SDIO bus sleeping? */
 	bool rxflow_mode;	/* Rx flow control mode */
 	bool rxflow;		/* Is rx flow control on */
 	bool alp_only;		/* Don't use HT clock (ALP only) */
-/* Field to decide if rx of control frames happen in rxbuf or lb-pool */
-	bool usebufpool;
 
 	u8 *ctrl_frame_buf;
 	u32 ctrl_frame_len;
@@ -570,8 +577,8 @@
 	bool wd_timer_valid;
 	uint save_ms;
 
-	struct task_struct *dpc_tsk;
-	struct completion dpc_wait;
+	struct workqueue_struct *brcmf_wq;
+	struct work_struct datawork;
 	struct list_head dpc_tsklst;
 	spinlock_t dpc_tl_lock;
 
@@ -657,15 +664,6 @@
 
 #define HOSTINTMASK		(I_HMB_SW_MASK | I_CHIPACTIVE)
 
-/* Packet free applicable unconditionally for sdio and sdspi.
- * Conditional if bufpool was present for gspi bus.
- */
-static void brcmf_sdbrcm_pktfree2(struct brcmf_sdio *bus, struct sk_buff *pkt)
-{
-	if (bus->usebufpool)
-		brcmu_pkt_buf_free_skb(pkt);
-}
-
 /* Turn backplane clock on or off */
 static int brcmf_sdbrcm_htclk(struct brcmf_sdio *bus, bool on, bool pendok)
 {
@@ -853,81 +851,6 @@
 	return 0;
 }
 
-static int brcmf_sdbrcm_bussleep(struct brcmf_sdio *bus, bool sleep)
-{
-	int ret;
-
-	brcmf_dbg(INFO, "request %s (currently %s)\n",
-		  sleep ? "SLEEP" : "WAKE",
-		  bus->sleeping ? "SLEEP" : "WAKE");
-
-	/* Done if we're already in the requested state */
-	if (sleep == bus->sleeping)
-		return 0;
-
-	/* Going to sleep: set the alarm and turn off the lights... */
-	if (sleep) {
-		/* Don't sleep if something is pending */
-		if (bus->dpc_sched || bus->rxskip || pktq_len(&bus->txq))
-			return -EBUSY;
-
-		/* Make sure the controller has the bus up */
-		brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
-
-		/* Tell device to start using OOB wakeup */
-		ret = w_sdreg32(bus, SMB_USE_OOB,
-				offsetof(struct sdpcmd_regs, tosbmailbox));
-		if (ret != 0)
-			brcmf_dbg(ERROR, "CANNOT SIGNAL CHIP, WILL NOT WAKE UP!!\n");
-
-		/* Turn off our contribution to the HT clock request */
-		brcmf_sdbrcm_clkctl(bus, CLK_SDONLY, false);
-
-		brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
-				 SBSDIO_FORCE_HW_CLKREQ_OFF, NULL);
-
-		/* Isolate the bus */
-		brcmf_sdio_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL,
-				 SBSDIO_DEVCTL_PADS_ISO, NULL);
-
-		/* Change state */
-		bus->sleeping = true;
-
-	} else {
-		/* Waking up: bus power up is ok, set local state */
-
-		brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
-				 0, NULL);
-
-		/* Make sure the controller has the bus up */
-		brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
-
-		/* Send misc interrupt to indicate OOB not needed */
-		ret = w_sdreg32(bus, 0,
-				offsetof(struct sdpcmd_regs, tosbmailboxdata));
-		if (ret == 0)
-			ret = w_sdreg32(bus, SMB_DEV_INT,
-				offsetof(struct sdpcmd_regs, tosbmailbox));
-
-		if (ret != 0)
-			brcmf_dbg(ERROR, "CANNOT SIGNAL CHIP TO CLEAR OOB!!\n");
-
-		/* Make sure we have SD bus access */
-		brcmf_sdbrcm_clkctl(bus, CLK_SDONLY, false);
-
-		/* Change state */
-		bus->sleeping = false;
-	}
-
-	return 0;
-}
-
-static void bus_wake(struct brcmf_sdio *bus)
-{
-	if (bus->sleeping)
-		brcmf_sdbrcm_bussleep(bus, false);
-}
-
 static u32 brcmf_sdbrcm_hostmail(struct brcmf_sdio *bus)
 {
 	u32 intstatus = 0;
@@ -1056,7 +979,7 @@
 	}
 
 	/* Clear partial in any case */
-	bus->nextlen = 0;
+	bus->cur_read.len = 0;
 
 	/* If we can't reach the device, signal failure */
 	if (err)
@@ -1108,6 +1031,96 @@
 	}
 }
 
+static bool brcmf_sdio_hdparser(struct brcmf_sdio *bus, u8 *header,
+				struct brcmf_sdio_read *rd)
+{
+	u16 len, checksum;
+	u8 rx_seq, fc, tx_seq_max;
+
+	/*
+	 * 4 bytes hardware header (frame tag)
+	 * Byte 0~1: Frame length
+	 * Byte 2~3: Checksum, bit-wise inverse of frame length
+	 */
+	len = get_unaligned_le16(header);
+	checksum = get_unaligned_le16(header + sizeof(u16));
+	/* All zero means no more to read */
+	if (!(len | checksum)) {
+		bus->rxpending = false;
+		return false;
+	}
+	if ((u16)(~(len ^ checksum))) {
+		brcmf_dbg(ERROR, "HW header checksum error\n");
+		bus->sdcnt.rx_badhdr++;
+		brcmf_sdbrcm_rxfail(bus, false, false);
+		return false;
+	}
+	if (len < SDPCM_HDRLEN) {
+		brcmf_dbg(ERROR, "HW header length error\n");
+		return false;
+	}
+	rd->len = len;
+
+	/*
+	 * 8 bytes hardware header
+	 * Byte 0: Rx sequence number
+	 * Byte 1: 4 MSB Channel number, 4 LSB arbitrary flag
+	 * Byte 2: Length of next data frame
+	 * Byte 3: Data offset
+	 * Byte 4: Flow control bits
+	 * Byte 5: Maximum Sequence number allow for Tx
+	 * Byte 6~7: Reserved
+	 */
+	rx_seq = SDPCM_PACKET_SEQUENCE(&header[SDPCM_FRAMETAG_LEN]);
+	rd->channel = SDPCM_PACKET_CHANNEL(&header[SDPCM_FRAMETAG_LEN]);
+	if (len > MAX_RX_DATASZ && rd->channel != SDPCM_CONTROL_CHANNEL) {
+		brcmf_dbg(ERROR, "HW header length too long\n");
+		bus->sdiodev->bus_if->dstats.rx_errors++;
+		bus->sdcnt.rx_toolong++;
+		brcmf_sdbrcm_rxfail(bus, false, false);
+		rd->len = 0;
+		return false;
+	}
+	rd->dat_offset = SDPCM_DOFFSET_VALUE(&header[SDPCM_FRAMETAG_LEN]);
+	if (rd->dat_offset < SDPCM_HDRLEN || rd->dat_offset > rd->len) {
+		brcmf_dbg(ERROR, "seq %d: bad data offset\n", rx_seq);
+		bus->sdcnt.rx_badhdr++;
+		brcmf_sdbrcm_rxfail(bus, false, false);
+		rd->len = 0;
+		return false;
+	}
+	if (rd->seq_num != rx_seq) {
+		brcmf_dbg(ERROR, "seq %d: sequence number error, expect %d\n",
+			  rx_seq, rd->seq_num);
+		bus->sdcnt.rx_badseq++;
+		rd->seq_num = rx_seq;
+	}
+	rd->len_nxtfrm = header[SDPCM_FRAMETAG_LEN + SDPCM_NEXTLEN_OFFSET];
+	if (rd->len_nxtfrm << 4 > MAX_RX_DATASZ) {
+		/* only warm for NON glom packet */
+		if (rd->channel != SDPCM_GLOM_CHANNEL)
+			brcmf_dbg(ERROR, "seq %d: next length error\n", rx_seq);
+		rd->len_nxtfrm = 0;
+	}
+	fc = SDPCM_FCMASK_VALUE(&header[SDPCM_FRAMETAG_LEN]);
+	if (bus->flowcontrol != fc) {
+		if (~bus->flowcontrol & fc)
+			bus->sdcnt.fc_xoff++;
+		if (bus->flowcontrol & ~fc)
+			bus->sdcnt.fc_xon++;
+		bus->sdcnt.fc_rcvd++;
+		bus->flowcontrol = fc;
+	}
+	tx_seq_max = SDPCM_WINDOW_VALUE(&header[SDPCM_FRAMETAG_LEN]);
+	if ((u8)(tx_seq_max - bus->tx_seq) > 0x40) {
+		brcmf_dbg(ERROR, "seq %d: max tx seq number error\n", rx_seq);
+		tx_seq_max = bus->tx_seq + 2;
+	}
+	bus->tx_max = tx_seq_max;
+
+	return true;
+}
+
 static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
 {
 	u16 dlen, totlen;
@@ -1122,6 +1135,7 @@
 
 	int ifidx = 0;
 	bool usechain = bus->use_rxchain;
+	u16 next_len;
 
 	/* If packets, issue read(s) and send up packet chain */
 	/* Return sequence numbers consumed? */
@@ -1185,10 +1199,10 @@
 		if (pnext) {
 			brcmf_dbg(GLOM, "allocated %d-byte packet chain for %d subframes\n",
 				  totlen, num);
-			if (BRCMF_GLOM_ON() && bus->nextlen &&
-			    totlen != bus->nextlen) {
+			if (BRCMF_GLOM_ON() && bus->cur_read.len &&
+			    totlen != bus->cur_read.len) {
 				brcmf_dbg(GLOM, "glomdesc mismatch: nextlen %d glomdesc %d rxseq %d\n",
-					  bus->nextlen, totlen, rxseq);
+					  bus->cur_read.len, totlen, rxseq);
 			}
 			pfirst = pnext = NULL;
 		} else {
@@ -1199,7 +1213,7 @@
 		/* Done with descriptor packet */
 		brcmu_pkt_buf_free_skb(bus->glomd);
 		bus->glomd = NULL;
-		bus->nextlen = 0;
+		bus->cur_read.len = 0;
 	}
 
 	/* Ok -- either we just generated a packet chain,
@@ -1272,12 +1286,13 @@
 
 		chan = SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]);
 		seq = SDPCM_PACKET_SEQUENCE(&dptr[SDPCM_FRAMETAG_LEN]);
-		bus->nextlen = dptr[SDPCM_FRAMETAG_LEN + SDPCM_NEXTLEN_OFFSET];
-		if ((bus->nextlen << 4) > MAX_RX_DATASZ) {
+		next_len = dptr[SDPCM_FRAMETAG_LEN + SDPCM_NEXTLEN_OFFSET];
+		if ((next_len << 4) > MAX_RX_DATASZ) {
 			brcmf_dbg(INFO, "nextlen too large (%d) seq %d\n",
-				  bus->nextlen, seq);
-			bus->nextlen = 0;
+				  next_len, seq);
+			next_len = 0;
 		}
+		bus->cur_read.len = next_len << 4;
 		doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]);
 		txmax = SDPCM_WINDOW_VALUE(&dptr[SDPCM_FRAMETAG_LEN]);
 
@@ -1378,7 +1393,7 @@
 				bus->sdcnt.rxglomfail++;
 				brcmf_sdbrcm_free_glom(bus);
 			}
-			bus->nextlen = 0;
+			bus->cur_read.len = 0;
 			return 0;
 		}
 
@@ -1573,422 +1588,166 @@
 	}
 }
 
-static void
-brcmf_alloc_pkt_and_read(struct brcmf_sdio *bus, u16 rdlen,
-			 struct sk_buff **pkt, u8 **rxbuf)
+static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)
 {
-	int sdret;		/* Return code from calls */
-
-	*pkt = brcmu_pkt_buf_get_skb(rdlen + BRCMF_SDALIGN);
-	if (*pkt == NULL)
-		return;
-
-	pkt_align(*pkt, rdlen, BRCMF_SDALIGN);
-	*rxbuf = (u8 *) ((*pkt)->data);
-	/* Read the entire frame */
-	sdret = brcmf_sdcard_recv_pkt(bus->sdiodev, bus->sdiodev->sbwad,
-				      SDIO_FUNC_2, F2SYNC, *pkt);
-	bus->sdcnt.f2rxdata++;
-
-	if (sdret < 0) {
-		brcmf_dbg(ERROR, "(nextlen): read %d bytes failed: %d\n",
-			  rdlen, sdret);
-		brcmu_pkt_buf_free_skb(*pkt);
-		bus->sdiodev->bus_if->dstats.rx_errors++;
-		/* Force retry w/normal header read.
-		 * Don't attempt NAK for
-		 * gSPI
-		 */
-		brcmf_sdbrcm_rxfail(bus, true, true);
-		*pkt = NULL;
-	}
-}
-
-/* Checks the header */
-static int
-brcmf_check_rxbuf(struct brcmf_sdio *bus, struct sk_buff *pkt, u8 *rxbuf,
-		  u8 rxseq, u16 nextlen, u16 *len)
-{
-	u16 check;
-	bool len_consistent;	/* Result of comparing readahead len and
-				   len from hw-hdr */
-
-	memcpy(bus->rxhdr, rxbuf, SDPCM_HDRLEN);
-
-	/* Extract hardware header fields */
-	*len = get_unaligned_le16(bus->rxhdr);
-	check = get_unaligned_le16(bus->rxhdr + sizeof(u16));
-
-	/* All zeros means readahead info was bad */
-	if (!(*len | check)) {
-		brcmf_dbg(INFO, "(nextlen): read zeros in HW header???\n");
-		goto fail;
-	}
-
-	/* Validate check bytes */
-	if ((u16)~(*len ^ check)) {
-		brcmf_dbg(ERROR, "(nextlen): HW hdr error: nextlen/len/check 0x%04x/0x%04x/0x%04x\n",
-			  nextlen, *len, check);
-		bus->sdcnt.rx_badhdr++;
-		brcmf_sdbrcm_rxfail(bus, false, false);
-		goto fail;
-	}
-
-	/* Validate frame length */
-	if (*len < SDPCM_HDRLEN) {
-		brcmf_dbg(ERROR, "(nextlen): HW hdr length invalid: %d\n",
-			  *len);
-		goto fail;
-	}
-
-	/* Check for consistency with readahead info */
-	len_consistent = (nextlen != (roundup(*len, 16) >> 4));
-	if (len_consistent) {
-		/* Mismatch, force retry w/normal
-			header (may be >4K) */
-		brcmf_dbg(ERROR, "(nextlen): mismatch, nextlen %d len %d rnd %d; expected rxseq %d\n",
-			  nextlen, *len, roundup(*len, 16),
-			  rxseq);
-		brcmf_sdbrcm_rxfail(bus, true, true);
-		goto fail;
-	}
-
-	return 0;
-
-fail:
-	brcmf_sdbrcm_pktfree2(bus, pkt);
-	return -EINVAL;
-}
-
-/* Return true if there may be more frames to read */
-static uint
-brcmf_sdbrcm_readframes(struct brcmf_sdio *bus, uint maxframes, bool *finished)
-{
-	u16 len, check;	/* Extracted hardware header fields */
-	u8 chan, seq, doff;	/* Extracted software header fields */
-	u8 fcbits;		/* Extracted fcbits from software header */
-
 	struct sk_buff *pkt;		/* Packet for event or data frames */
 	u16 pad;		/* Number of pad bytes to read */
-	u16 rdlen;		/* Total number of bytes to read */
-	u8 rxseq;		/* Next sequence number to expect */
 	uint rxleft = 0;	/* Remaining number of frames allowed */
 	int sdret;		/* Return code from calls */
-	u8 txmax;		/* Maximum tx sequence offered */
-	u8 *rxbuf;
 	int ifidx = 0;
 	uint rxcount = 0;	/* Total frames read */
+	struct brcmf_sdio_read *rd = &bus->cur_read, rd_new;
+	u8 head_read = 0;
 
 	brcmf_dbg(TRACE, "Enter\n");
 
 	/* Not finished unless we encounter no more frames indication */
-	*finished = false;
+	bus->rxpending = true;
 
-	for (rxseq = bus->rx_seq, rxleft = maxframes;
+	for (rd->seq_num = bus->rx_seq, rxleft = maxframes;
 	     !bus->rxskip && rxleft &&
 	     bus->sdiodev->bus_if->state != BRCMF_BUS_DOWN;
-	     rxseq++, rxleft--) {
+	     rd->seq_num++, rxleft--) {
 
 		/* Handle glomming separately */
 		if (bus->glomd || !skb_queue_empty(&bus->glom)) {
 			u8 cnt;
 			brcmf_dbg(GLOM, "calling rxglom: glomd %p, glom %p\n",
 				  bus->glomd, skb_peek(&bus->glom));
-			cnt = brcmf_sdbrcm_rxglom(bus, rxseq);
+			cnt = brcmf_sdbrcm_rxglom(bus, rd->seq_num);
 			brcmf_dbg(GLOM, "rxglom returned %d\n", cnt);
-			rxseq += cnt - 1;
+			rd->seq_num += cnt - 1;
 			rxleft = (rxleft > cnt) ? (rxleft - cnt) : 1;
 			continue;
 		}
 
-		/* Try doing single read if we can */
-		if (bus->nextlen) {
-			u16 nextlen = bus->nextlen;
-			bus->nextlen = 0;
-
-			rdlen = len = nextlen << 4;
-			brcmf_pad(bus, &pad, &rdlen);
-
-			/*
-			 * After the frame is received we have to
-			 * distinguish whether it is data
-			 * or non-data frame.
-			 */
-			brcmf_alloc_pkt_and_read(bus, rdlen, &pkt, &rxbuf);
-			if (pkt == NULL) {
-				/* Give up on data, request rtx of events */
-				brcmf_dbg(ERROR, "(nextlen): brcmf_alloc_pkt_and_read failed: len %d rdlen %d expected rxseq %d\n",
-					  len, rdlen, rxseq);
+		rd->len_left = rd->len;
+		/* read header first for unknow frame length */
+		if (!rd->len) {
+			sdret = brcmf_sdcard_recv_buf(bus->sdiodev,
+						      bus->sdiodev->sbwad,
+						      SDIO_FUNC_2, F2SYNC,
+						      bus->rxhdr,
+						      BRCMF_FIRSTREAD);
+			bus->sdcnt.f2rxhdrs++;
+			if (sdret < 0) {
+				brcmf_dbg(ERROR, "RXHEADER FAILED: %d\n",
+					  sdret);
+				bus->sdcnt.rx_hdrfail++;
+				brcmf_sdbrcm_rxfail(bus, true, true);
 				continue;
 			}
 
-			if (brcmf_check_rxbuf(bus, pkt, rxbuf, rxseq, nextlen,
-					      &len) < 0)
-				continue;
+			brcmf_dbg_hex_dump(BRCMF_BYTES_ON() || BRCMF_HDRS_ON(),
+					   bus->rxhdr, SDPCM_HDRLEN,
+					   "RxHdr:\n");
 
-			/* Extract software header fields */
-			chan = SDPCM_PACKET_CHANNEL(
-					&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
-			seq = SDPCM_PACKET_SEQUENCE(
-					&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
-			doff = SDPCM_DOFFSET_VALUE(
-					&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
-			txmax = SDPCM_WINDOW_VALUE(
-					&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
-
-			bus->nextlen =
-			    bus->rxhdr[SDPCM_FRAMETAG_LEN +
-				       SDPCM_NEXTLEN_OFFSET];
-			if ((bus->nextlen << 4) > MAX_RX_DATASZ) {
-				brcmf_dbg(INFO, "(nextlen): got frame w/nextlen too large (%d), seq %d\n",
-					  bus->nextlen, seq);
-				bus->nextlen = 0;
+			if (!brcmf_sdio_hdparser(bus, bus->rxhdr, rd)) {
+				if (!bus->rxpending)
+					break;
+				else
+					continue;
 			}
 
+			if (rd->channel == SDPCM_CONTROL_CHANNEL) {
+				brcmf_sdbrcm_read_control(bus, bus->rxhdr,
+							  rd->len,
+							  rd->dat_offset);
+				/* prepare the descriptor for the next read */
+				rd->len = rd->len_nxtfrm << 4;
+				rd->len_nxtfrm = 0;
+				/* treat all packet as event if we don't know */
+				rd->channel = SDPCM_EVENT_CHANNEL;
+				continue;
+			}
+			rd->len_left = rd->len > BRCMF_FIRSTREAD ?
+				       rd->len - BRCMF_FIRSTREAD : 0;
+			head_read = BRCMF_FIRSTREAD;
+		}
+
+		brcmf_pad(bus, &pad, &rd->len_left);
+
+		pkt = brcmu_pkt_buf_get_skb(rd->len_left + head_read +
+					    BRCMF_SDALIGN);
+		if (!pkt) {
+			/* Give up on data, request rtx of events */
+			brcmf_dbg(ERROR, "brcmu_pkt_buf_get_skb failed\n");
+			bus->sdiodev->bus_if->dstats.rx_dropped++;
+			brcmf_sdbrcm_rxfail(bus, false,
+					    RETRYCHAN(rd->channel));
+			continue;
+		}
+		skb_pull(pkt, head_read);
+		pkt_align(pkt, rd->len_left, BRCMF_SDALIGN);
+
+		sdret = brcmf_sdcard_recv_pkt(bus->sdiodev, bus->sdiodev->sbwad,
+					      SDIO_FUNC_2, F2SYNC, pkt);
+		bus->sdcnt.f2rxdata++;
+
+		if (sdret < 0) {
+			brcmf_dbg(ERROR, "read %d bytes from channel %d failed: %d\n",
+				  rd->len, rd->channel, sdret);
+			brcmu_pkt_buf_free_skb(pkt);
+			bus->sdiodev->bus_if->dstats.rx_errors++;
+			brcmf_sdbrcm_rxfail(bus, true,
+					    RETRYCHAN(rd->channel));
+			continue;
+		}
+
+		if (head_read) {
+			skb_push(pkt, head_read);
+			memcpy(pkt->data, bus->rxhdr, head_read);
+			head_read = 0;
+		} else {
+			memcpy(bus->rxhdr, pkt->data, SDPCM_HDRLEN);
+			rd_new.seq_num = rd->seq_num;
+			if (!brcmf_sdio_hdparser(bus, bus->rxhdr, &rd_new)) {
+				rd->len = 0;
+				brcmu_pkt_buf_free_skb(pkt);
+			}
 			bus->sdcnt.rx_readahead_cnt++;
-
-			/* Handle Flow Control */
-			fcbits = SDPCM_FCMASK_VALUE(
-					&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
-
-			if (bus->flowcontrol != fcbits) {
-				if (~bus->flowcontrol & fcbits)
-					bus->sdcnt.fc_xoff++;
-
-				if (bus->flowcontrol & ~fcbits)
-					bus->sdcnt.fc_xon++;
-
-				bus->sdcnt.fc_rcvd++;
-				bus->flowcontrol = fcbits;
+			if (rd->len != roundup(rd_new.len, 16)) {
+				brcmf_dbg(ERROR, "frame length mismatch:read %d, should be %d\n",
+					  rd->len,
+					  roundup(rd_new.len, 16) >> 4);
+				rd->len = 0;
+				brcmf_sdbrcm_rxfail(bus, true, true);
+				brcmu_pkt_buf_free_skb(pkt);
+				continue;
 			}
+			rd->len_nxtfrm = rd_new.len_nxtfrm;
+			rd->channel = rd_new.channel;
+			rd->dat_offset = rd_new.dat_offset;
 
-			/* Check and update sequence number */
-			if (rxseq != seq) {
-				brcmf_dbg(INFO, "(nextlen): rx_seq %d, expected %d\n",
-					  seq, rxseq);
-				bus->sdcnt.rx_badseq++;
-				rxseq = seq;
-			}
-
-			/* Check window for sanity */
-			if ((u8) (txmax - bus->tx_seq) > 0x40) {
-				brcmf_dbg(ERROR, "got unlikely tx max %d with tx_seq %d\n",
-					  txmax, bus->tx_seq);
-				txmax = bus->tx_seq + 2;
-			}
-			bus->tx_max = txmax;
-
-			brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_DATA_ON(),
-					   rxbuf, len, "Rx Data:\n");
 			brcmf_dbg_hex_dump(!(BRCMF_BYTES_ON() &&
 					     BRCMF_DATA_ON()) &&
 					   BRCMF_HDRS_ON(),
 					   bus->rxhdr, SDPCM_HDRLEN,
 					   "RxHdr:\n");
 
-			if (chan == SDPCM_CONTROL_CHANNEL) {
-				brcmf_dbg(ERROR, "(nextlen): readahead on control packet %d?\n",
-					  seq);
+			if (rd_new.channel == SDPCM_CONTROL_CHANNEL) {
+				brcmf_dbg(ERROR, "readahead on control packet %d?\n",
+					  rd_new.seq_num);
 				/* Force retry w/normal header read */
-				bus->nextlen = 0;
+				rd->len = 0;
 				brcmf_sdbrcm_rxfail(bus, false, true);
-				brcmf_sdbrcm_pktfree2(bus, pkt);
+				brcmu_pkt_buf_free_skb(pkt);
 				continue;
 			}
-
-			/* Validate data offset */
-			if ((doff < SDPCM_HDRLEN) || (doff > len)) {
-				brcmf_dbg(ERROR, "(nextlen): bad data offset %d: HW len %d min %d\n",
-					  doff, len, SDPCM_HDRLEN);
-				brcmf_sdbrcm_rxfail(bus, false, false);
-				brcmf_sdbrcm_pktfree2(bus, pkt);
-				continue;
-			}
-
-			/* All done with this one -- now deliver the packet */
-			goto deliver;
 		}
 
-		/* Read frame header (hardware and software) */
-		sdret = brcmf_sdcard_recv_buf(bus->sdiodev, bus->sdiodev->sbwad,
-					      SDIO_FUNC_2, F2SYNC, bus->rxhdr,
-					      BRCMF_FIRSTREAD);
-		bus->sdcnt.f2rxhdrs++;
-
-		if (sdret < 0) {
-			brcmf_dbg(ERROR, "RXHEADER FAILED: %d\n", sdret);
-			bus->sdcnt.rx_hdrfail++;
-			brcmf_sdbrcm_rxfail(bus, true, true);
-			continue;
-		}
-		brcmf_dbg_hex_dump(BRCMF_BYTES_ON() || BRCMF_HDRS_ON(),
-				   bus->rxhdr, SDPCM_HDRLEN, "RxHdr:\n");
-
-
-		/* Extract hardware header fields */
-		len = get_unaligned_le16(bus->rxhdr);
-		check = get_unaligned_le16(bus->rxhdr + sizeof(u16));
-
-		/* All zeros means no more frames */
-		if (!(len | check)) {
-			*finished = true;
-			break;
-		}
-
-		/* Validate check bytes */
-		if ((u16) ~(len ^ check)) {
-			brcmf_dbg(ERROR, "HW hdr err: len/check 0x%04x/0x%04x\n",
-				  len, check);
-			bus->sdcnt.rx_badhdr++;
-			brcmf_sdbrcm_rxfail(bus, false, false);
-			continue;
-		}
-
-		/* Validate frame length */
-		if (len < SDPCM_HDRLEN) {
-			brcmf_dbg(ERROR, "HW hdr length invalid: %d\n", len);
-			continue;
-		}
-
-		/* Extract software header fields */
-		chan = SDPCM_PACKET_CHANNEL(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
-		seq = SDPCM_PACKET_SEQUENCE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
-		doff = SDPCM_DOFFSET_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
-		txmax = SDPCM_WINDOW_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
-
-		/* Validate data offset */
-		if ((doff < SDPCM_HDRLEN) || (doff > len)) {
-			brcmf_dbg(ERROR, "Bad data offset %d: HW len %d, min %d seq %d\n",
-				  doff, len, SDPCM_HDRLEN, seq);
-			bus->sdcnt.rx_badhdr++;
-			brcmf_sdbrcm_rxfail(bus, false, false);
-			continue;
-		}
-
-		/* Save the readahead length if there is one */
-		bus->nextlen =
-		    bus->rxhdr[SDPCM_FRAMETAG_LEN + SDPCM_NEXTLEN_OFFSET];
-		if ((bus->nextlen << 4) > MAX_RX_DATASZ) {
-			brcmf_dbg(INFO, "(nextlen): got frame w/nextlen too large (%d), seq %d\n",
-				  bus->nextlen, seq);
-			bus->nextlen = 0;
-		}
-
-		/* Handle Flow Control */
-		fcbits = SDPCM_FCMASK_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
-
-		if (bus->flowcontrol != fcbits) {
-			if (~bus->flowcontrol & fcbits)
-				bus->sdcnt.fc_xoff++;
-
-			if (bus->flowcontrol & ~fcbits)
-				bus->sdcnt.fc_xon++;
-
-			bus->sdcnt.fc_rcvd++;
-			bus->flowcontrol = fcbits;
-		}
-
-		/* Check and update sequence number */
-		if (rxseq != seq) {
-			brcmf_dbg(INFO, "rx_seq %d, expected %d\n", seq, rxseq);
-			bus->sdcnt.rx_badseq++;
-			rxseq = seq;
-		}
-
-		/* Check window for sanity */
-		if ((u8) (txmax - bus->tx_seq) > 0x40) {
-			brcmf_dbg(ERROR, "unlikely tx max %d with tx_seq %d\n",
-				  txmax, bus->tx_seq);
-			txmax = bus->tx_seq + 2;
-		}
-		bus->tx_max = txmax;
-
-		/* Call a separate function for control frames */
-		if (chan == SDPCM_CONTROL_CHANNEL) {
-			brcmf_sdbrcm_read_control(bus, bus->rxhdr, len, doff);
-			continue;
-		}
-
-		/* precondition: chan is either SDPCM_DATA_CHANNEL,
-		   SDPCM_EVENT_CHANNEL, SDPCM_TEST_CHANNEL or
-		   SDPCM_GLOM_CHANNEL */
-
-		/* Length to read */
-		rdlen = (len > BRCMF_FIRSTREAD) ? (len - BRCMF_FIRSTREAD) : 0;
-
-		/* May pad read to blocksize for efficiency */
-		if (bus->roundup && bus->blocksize &&
-			(rdlen > bus->blocksize)) {
-			pad = bus->blocksize - (rdlen % bus->blocksize);
-			if ((pad <= bus->roundup) && (pad < bus->blocksize) &&
-			    ((rdlen + pad + BRCMF_FIRSTREAD) < MAX_RX_DATASZ))
-				rdlen += pad;
-		} else if (rdlen % BRCMF_SDALIGN) {
-			rdlen += BRCMF_SDALIGN - (rdlen % BRCMF_SDALIGN);
-		}
-
-		/* Satisfy length-alignment requirements */
-		if (rdlen & (ALIGNMENT - 1))
-			rdlen = roundup(rdlen, ALIGNMENT);
-
-		if ((rdlen + BRCMF_FIRSTREAD) > MAX_RX_DATASZ) {
-			/* Too long -- skip this frame */
-			brcmf_dbg(ERROR, "too long: len %d rdlen %d\n",
-				  len, rdlen);
-			bus->sdiodev->bus_if->dstats.rx_errors++;
-			bus->sdcnt.rx_toolong++;
-			brcmf_sdbrcm_rxfail(bus, false, false);
-			continue;
-		}
-
-		pkt = brcmu_pkt_buf_get_skb(rdlen +
-					    BRCMF_FIRSTREAD + BRCMF_SDALIGN);
-		if (!pkt) {
-			/* Give up on data, request rtx of events */
-			brcmf_dbg(ERROR, "brcmu_pkt_buf_get_skb failed: rdlen %d chan %d\n",
-				  rdlen, chan);
-			bus->sdiodev->bus_if->dstats.rx_dropped++;
-			brcmf_sdbrcm_rxfail(bus, false, RETRYCHAN(chan));
-			continue;
-		}
-
-		/* Leave room for what we already read, and align remainder */
-		skb_pull(pkt, BRCMF_FIRSTREAD);
-		pkt_align(pkt, rdlen, BRCMF_SDALIGN);
-
-		/* Read the remaining frame data */
-		sdret = brcmf_sdcard_recv_pkt(bus->sdiodev, bus->sdiodev->sbwad,
-					      SDIO_FUNC_2, F2SYNC, pkt);
-		bus->sdcnt.f2rxdata++;
-
-		if (sdret < 0) {
-			brcmf_dbg(ERROR, "read %d %s bytes failed: %d\n", rdlen,
-				  ((chan == SDPCM_EVENT_CHANNEL) ? "event"
-				   : ((chan == SDPCM_DATA_CHANNEL) ? "data"
-				      : "test")), sdret);
-			brcmu_pkt_buf_free_skb(pkt);
-			bus->sdiodev->bus_if->dstats.rx_errors++;
-			brcmf_sdbrcm_rxfail(bus, true, RETRYCHAN(chan));
-			continue;
-		}
-
-		/* Copy the already-read portion */
-		skb_push(pkt, BRCMF_FIRSTREAD);
-		memcpy(pkt->data, bus->rxhdr, BRCMF_FIRSTREAD);
-
 		brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_DATA_ON(),
-				   pkt->data, len, "Rx Data:\n");
+				   pkt->data, rd->len, "Rx Data:\n");
 
-deliver:
 		/* Save superframe descriptor and allocate packet frame */
-		if (chan == SDPCM_GLOM_CHANNEL) {
+		if (rd->channel == SDPCM_GLOM_CHANNEL) {
 			if (SDPCM_GLOMDESC(&bus->rxhdr[SDPCM_FRAMETAG_LEN])) {
 				brcmf_dbg(GLOM, "glom descriptor, %d bytes:\n",
-					  len);
+					  rd->len);
 				brcmf_dbg_hex_dump(BRCMF_GLOM_ON(),
-						   pkt->data, len,
+						   pkt->data, rd->len,
 						   "Glom Data:\n");
-				__skb_trim(pkt, len);
+				__skb_trim(pkt, rd->len);
 				skb_pull(pkt, SDPCM_HDRLEN);
 				bus->glomd = pkt;
 			} else {
@@ -1996,12 +1755,23 @@
 					  "descriptor!\n", __func__);
 				brcmf_sdbrcm_rxfail(bus, false, false);
 			}
+			/* prepare the descriptor for the next read */
+			rd->len = rd->len_nxtfrm << 4;
+			rd->len_nxtfrm = 0;
+			/* treat all packet as event if we don't know */
+			rd->channel = SDPCM_EVENT_CHANNEL;
 			continue;
 		}
 
 		/* Fill in packet len and prio, deliver upward */
-		__skb_trim(pkt, len);
-		skb_pull(pkt, doff);
+		__skb_trim(pkt, rd->len);
+		skb_pull(pkt, rd->dat_offset);
+
+		/* prepare the descriptor for the next read */
+		rd->len = rd->len_nxtfrm << 4;
+		rd->len_nxtfrm = 0;
+		/* treat all packet as event if we don't know */
+		rd->channel = SDPCM_EVENT_CHANNEL;
 
 		if (pkt->len == 0) {
 			brcmu_pkt_buf_free_skb(pkt);
@@ -2019,17 +1789,17 @@
 		brcmf_rx_packet(bus->sdiodev->dev, ifidx, pkt);
 		down(&bus->sdsem);
 	}
+
 	rxcount = maxframes - rxleft;
 	/* Message if we hit the limit */
 	if (!rxleft)
-		brcmf_dbg(DATA, "hit rx limit of %d frames\n",
-			  maxframes);
+		brcmf_dbg(DATA, "hit rx limit of %d frames\n", maxframes);
 	else
 		brcmf_dbg(DATA, "processed %d frames\n", rxcount);
 	/* Back off rxseq if awaiting rtx, update rx_seq */
 	if (bus->rxskip)
-		rxseq--;
-	bus->rx_seq = rxseq;
+		rd->seq_num--;
+	bus->rx_seq = rd->seq_num;
 
 	return rxcount;
 }
@@ -2227,7 +1997,7 @@
 			if (ret != 0)
 				break;
 			if (intstatus & bus->hostintmask)
-				bus->ipend = true;
+				atomic_set(&bus->ipend, 1);
 		}
 	}
 
@@ -2259,16 +2029,8 @@
 		bus->watchdog_tsk = NULL;
 	}
 
-	if (bus->dpc_tsk && bus->dpc_tsk != current) {
-		send_sig(SIGTERM, bus->dpc_tsk, 1);
-		kthread_stop(bus->dpc_tsk);
-		bus->dpc_tsk = NULL;
-	}
-
 	down(&bus->sdsem);
 
-	bus_wake(bus);
-
 	/* Enable clock for device interrupts */
 	brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
 
@@ -2327,7 +2089,7 @@
 	unsigned long flags;
 
 	spin_lock_irqsave(&bus->sdiodev->irq_en_lock, flags);
-	if (!bus->sdiodev->irq_en && !bus->ipend) {
+	if (!bus->sdiodev->irq_en && !atomic_read(&bus->ipend)) {
 		enable_irq(bus->sdiodev->irq);
 		bus->sdiodev->irq_en = true;
 	}
@@ -2339,21 +2101,69 @@
 }
 #endif		/* CONFIG_BRCMFMAC_SDIO_OOB */
 
-static bool brcmf_sdbrcm_dpc(struct brcmf_sdio *bus)
+static inline void brcmf_sdbrcm_adddpctsk(struct brcmf_sdio *bus)
 {
-	u32 intstatus, newstatus = 0;
+	struct list_head *new_hd;
+	unsigned long flags;
+
+	if (in_interrupt())
+		new_hd = kzalloc(sizeof(struct list_head), GFP_ATOMIC);
+	else
+		new_hd = kzalloc(sizeof(struct list_head), GFP_KERNEL);
+	if (new_hd == NULL)
+		return;
+
+	spin_lock_irqsave(&bus->dpc_tl_lock, flags);
+	list_add_tail(new_hd, &bus->dpc_tsklst);
+	spin_unlock_irqrestore(&bus->dpc_tl_lock, flags);
+}
+
+static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus)
+{
+	u8 idx;
+	u32 addr;
+	unsigned long val;
+	int n, ret;
+
+	idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV);
+	addr = bus->ci->c_inf[idx].base +
+	       offsetof(struct sdpcmd_regs, intstatus);
+
+	ret = brcmf_sdio_regrw_helper(bus->sdiodev, addr, &val, false);
+	bus->sdcnt.f1regdata++;
+	if (ret != 0)
+		val = 0;
+
+	val &= bus->hostintmask;
+	atomic_set(&bus->fcstate, !!(val & I_HMB_FC_STATE));
+
+	/* Clear interrupts */
+	if (val) {
+		ret = brcmf_sdio_regrw_helper(bus->sdiodev, addr, &val, true);
+		bus->sdcnt.f1regdata++;
+	}
+
+	if (ret) {
+		atomic_set(&bus->intstatus, 0);
+	} else if (val) {
+		for_each_set_bit(n, &val, 32)
+			set_bit(n, (unsigned long *)&bus->intstatus.counter);
+	}
+
+	return ret;
+}
+
+static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus)
+{
+	u32 newstatus = 0;
+	unsigned long intstatus;
 	uint rxlimit = bus->rxbound;	/* Rx frames to read before resched */
 	uint txlimit = bus->txbound;	/* Tx frames to send before resched */
 	uint framecnt = 0;	/* Temporary counter of tx/rx frames */
-	bool rxdone = true;	/* Flag for no more read data */
-	bool resched = false;	/* Flag indicating resched wanted */
-	int err;
+	int err = 0, n;
 
 	brcmf_dbg(TRACE, "Enter\n");
 
-	/* Start with leftover status bits */
-	intstatus = bus->intstatus;
-
 	down(&bus->sdsem);
 
 	/* If waiting for HTAVAIL, check status */
@@ -2399,39 +2209,22 @@
 				bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN;
 			}
 			bus->clkstate = CLK_AVAIL;
-		} else {
-			goto clkwait;
 		}
 	}
 
-	bus_wake(bus);
-
 	/* Make sure backplane clock is on */
 	brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, true);
-	if (bus->clkstate == CLK_PENDING)
-		goto clkwait;
 
 	/* Pending interrupt indicates new device status */
-	if (bus->ipend) {
-		bus->ipend = false;
-		err = r_sdreg32(bus, &newstatus,
-				offsetof(struct sdpcmd_regs, intstatus));
-		bus->sdcnt.f1regdata++;
-		if (err != 0)
-			newstatus = 0;
-		newstatus &= bus->hostintmask;
-		bus->fcstate = !!(newstatus & I_HMB_FC_STATE);
-		if (newstatus) {
-			err = w_sdreg32(bus, newstatus,
-					offsetof(struct sdpcmd_regs,
-						 intstatus));
-			bus->sdcnt.f1regdata++;
-		}
+	if (atomic_read(&bus->ipend) > 0) {
+		atomic_set(&bus->ipend, 0);
+		sdio_claim_host(bus->sdiodev->func[1]);
+		err = brcmf_sdio_intr_rstatus(bus);
+		sdio_release_host(bus->sdiodev->func[1]);
 	}
 
-	/* Merge new bits with previous */
-	intstatus |= newstatus;
-	bus->intstatus = 0;
+	/* Start with leftover status bits */
+	intstatus = atomic_xchg(&bus->intstatus, 0);
 
 	/* Handle flow-control change: read new state in case our ack
 	 * crossed another change interrupt.  If change still set, assume
@@ -2445,8 +2238,8 @@
 		err = r_sdreg32(bus, &newstatus,
 				offsetof(struct sdpcmd_regs, intstatus));
 		bus->sdcnt.f1regdata += 2;
-		bus->fcstate =
-		    !!(newstatus & (I_HMB_FC_STATE | I_HMB_FC_CHANGE));
+		atomic_set(&bus->fcstate,
+			   !!(newstatus & (I_HMB_FC_STATE | I_HMB_FC_CHANGE)));
 		intstatus |= (newstatus & bus->hostintmask);
 	}
 
@@ -2483,32 +2276,34 @@
 		intstatus &= ~I_HMB_FRAME_IND;
 
 	/* On frame indication, read available frames */
-	if (PKT_AVAILABLE()) {
-		framecnt = brcmf_sdbrcm_readframes(bus, rxlimit, &rxdone);
-		if (rxdone || bus->rxskip)
+	if (PKT_AVAILABLE() && bus->clkstate == CLK_AVAIL) {
+		framecnt = brcmf_sdio_readframes(bus, rxlimit);
+		if (!bus->rxpending)
 			intstatus &= ~I_HMB_FRAME_IND;
 		rxlimit -= min(framecnt, rxlimit);
 	}
 
 	/* Keep still-pending events for next scheduling */
-	bus->intstatus = intstatus;
+	if (intstatus) {
+		for_each_set_bit(n, &intstatus, 32)
+			set_bit(n, (unsigned long *)&bus->intstatus.counter);
+	}
 
-clkwait:
 	brcmf_sdbrcm_clrintr(bus);
 
 	if (data_ok(bus) && bus->ctrl_frame_stat &&
 		(bus->clkstate == CLK_AVAIL)) {
-		int ret, i;
+		int i;
 
-		ret = brcmf_sdcard_send_buf(bus->sdiodev, bus->sdiodev->sbwad,
+		err = brcmf_sdcard_send_buf(bus->sdiodev, bus->sdiodev->sbwad,
 			SDIO_FUNC_2, F2SYNC, bus->ctrl_frame_buf,
 			(u32) bus->ctrl_frame_len);
 
-		if (ret < 0) {
+		if (err < 0) {
 			/* On failure, abort the command and
 				terminate the frame */
 			brcmf_dbg(INFO, "sdio error %d, abort command and terminate frame\n",
-				  ret);
+				  err);
 			bus->sdcnt.tx_sderrs++;
 
 			brcmf_sdcard_abort(bus->sdiodev, SDIO_FUNC_2);
@@ -2530,42 +2325,34 @@
 					break;
 			}
 
-		}
-		if (ret == 0)
+		} else {
 			bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP;
-
-		brcmf_dbg(INFO, "Return_dpc value is : %d\n", ret);
+		}
 		bus->ctrl_frame_stat = false;
 		brcmf_sdbrcm_wait_event_wakeup(bus);
 	}
 	/* Send queued frames (limit 1 if rx may still be pending) */
-	else if ((bus->clkstate == CLK_AVAIL) && !bus->fcstate &&
+	else if ((bus->clkstate == CLK_AVAIL) && !atomic_read(&bus->fcstate) &&
 		 brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && txlimit
 		 && data_ok(bus)) {
-		framecnt = rxdone ? txlimit : min(txlimit, bus->txminmax);
+		framecnt = bus->rxpending ? min(txlimit, bus->txminmax) :
+					    txlimit;
 		framecnt = brcmf_sdbrcm_sendfromq(bus, framecnt);
 		txlimit -= framecnt;
 	}
 
-	/* Resched if events or tx frames are pending,
-		 else await next interrupt */
-	/* On failed register access, all bets are off:
-		 no resched or interrupts */
 	if ((bus->sdiodev->bus_if->state == BRCMF_BUS_DOWN) || (err != 0)) {
 		brcmf_dbg(ERROR, "failed backplane access over SDIO, halting operation\n");
 		bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN;
-		bus->intstatus = 0;
-	} else if (bus->clkstate == CLK_PENDING) {
-		brcmf_dbg(INFO, "rescheduled due to CLK_PENDING awaiting I_CHIPACTIVE interrupt\n");
-		resched = true;
-	} else if (bus->intstatus || bus->ipend ||
-		(!bus->fcstate && brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol)
-		 && data_ok(bus)) || PKT_AVAILABLE()) {
-		resched = true;
+		atomic_set(&bus->intstatus, 0);
+	} else if (atomic_read(&bus->intstatus) ||
+		   atomic_read(&bus->ipend) > 0 ||
+		   (!atomic_read(&bus->fcstate) &&
+		    brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) &&
+		    data_ok(bus)) || PKT_AVAILABLE()) {
+		brcmf_sdbrcm_adddpctsk(bus);
 	}
 
-	bus->dpc_sched = resched;
-
 	/* If we're done for now, turn off clock request. */
 	if ((bus->clkstate != CLK_PENDING)
 	    && bus->idletime == BRCMF_IDLE_IMMEDIATE) {
@@ -2574,65 +2361,6 @@
 	}
 
 	up(&bus->sdsem);
-
-	return resched;
-}
-
-static inline void brcmf_sdbrcm_adddpctsk(struct brcmf_sdio *bus)
-{
-	struct list_head *new_hd;
-	unsigned long flags;
-
-	if (in_interrupt())
-		new_hd = kzalloc(sizeof(struct list_head), GFP_ATOMIC);
-	else
-		new_hd = kzalloc(sizeof(struct list_head), GFP_KERNEL);
-	if (new_hd == NULL)
-		return;
-
-	spin_lock_irqsave(&bus->dpc_tl_lock, flags);
-	list_add_tail(new_hd, &bus->dpc_tsklst);
-	spin_unlock_irqrestore(&bus->dpc_tl_lock, flags);
-}
-
-static int brcmf_sdbrcm_dpc_thread(void *data)
-{
-	struct brcmf_sdio *bus = (struct brcmf_sdio *) data;
-	struct list_head *cur_hd, *tmp_hd;
-	unsigned long flags;
-
-	allow_signal(SIGTERM);
-	/* Run until signal received */
-	while (1) {
-		if (kthread_should_stop())
-			break;
-
-		if (list_empty(&bus->dpc_tsklst))
-			if (wait_for_completion_interruptible(&bus->dpc_wait))
-				break;
-
-		spin_lock_irqsave(&bus->dpc_tl_lock, flags);
-		list_for_each_safe(cur_hd, tmp_hd, &bus->dpc_tsklst) {
-			spin_unlock_irqrestore(&bus->dpc_tl_lock, flags);
-
-			if (bus->sdiodev->bus_if->state == BRCMF_BUS_DOWN) {
-				/* after stopping the bus, exit thread */
-				brcmf_sdbrcm_bus_stop(bus->sdiodev->dev);
-				bus->dpc_tsk = NULL;
-				spin_lock_irqsave(&bus->dpc_tl_lock, flags);
-				break;
-			}
-
-			if (brcmf_sdbrcm_dpc(bus))
-				brcmf_sdbrcm_adddpctsk(bus);
-
-			spin_lock_irqsave(&bus->dpc_tl_lock, flags);
-			list_del(cur_hd);
-			kfree(cur_hd);
-		}
-		spin_unlock_irqrestore(&bus->dpc_tl_lock, flags);
-	}
-	return 0;
 }
 
 static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt)
@@ -2642,6 +2370,7 @@
 	struct brcmf_bus *bus_if = dev_get_drvdata(dev);
 	struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
 	struct brcmf_sdio *bus = sdiodev->bus;
+	unsigned long flags;
 
 	brcmf_dbg(TRACE, "Enter\n");
 
@@ -2680,13 +2409,15 @@
 	if (pktq_plen(&bus->txq, prec) > qcount[prec])
 		qcount[prec] = pktq_plen(&bus->txq, prec);
 #endif
-	/* Schedule DPC if needed to send queued packet(s) */
-	if (!bus->dpc_sched) {
-		bus->dpc_sched = true;
-		if (bus->dpc_tsk) {
-			brcmf_sdbrcm_adddpctsk(bus);
-			complete(&bus->dpc_wait);
-		}
+
+	spin_lock_irqsave(&bus->dpc_tl_lock, flags);
+	if (list_empty(&bus->dpc_tsklst)) {
+		spin_unlock_irqrestore(&bus->dpc_tl_lock, flags);
+
+		brcmf_sdbrcm_adddpctsk(bus);
+		queue_work(bus->brcmf_wq, &bus->datawork);
+	} else {
+		spin_unlock_irqrestore(&bus->dpc_tl_lock, flags);
 	}
 
 	return ret;
@@ -2707,6 +2438,8 @@
 	else
 		dsize = size;
 
+	sdio_claim_host(bus->sdiodev->func[1]);
+
 	/* Set the backplane window to include the start address */
 	bcmerror = brcmf_sdcard_set_sbaddr_window(bus->sdiodev, address);
 	if (bcmerror) {
@@ -2748,6 +2481,8 @@
 		brcmf_dbg(ERROR, "FAILED to set window back to 0x%x\n",
 			  bus->sdiodev->sbwad);
 
+	sdio_release_host(bus->sdiodev->func[1]);
+
 	return bcmerror;
 }
 
@@ -2882,6 +2617,7 @@
 	struct brcmf_bus *bus_if = dev_get_drvdata(dev);
 	struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
 	struct brcmf_sdio *bus = sdiodev->bus;
+	unsigned long flags;
 
 	brcmf_dbg(TRACE, "Enter\n");
 
@@ -2918,8 +2654,6 @@
 	/* Need to lock here to protect txseq and SDIO tx calls */
 	down(&bus->sdsem);
 
-	bus_wake(bus);
-
 	/* Make sure backplane clock is on */
 	brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
 
@@ -2967,9 +2701,15 @@
 		} while (ret < 0 && retries++ < TXRETRIES);
 	}
 
-	if ((bus->idletime == BRCMF_IDLE_IMMEDIATE) && !bus->dpc_sched) {
+	spin_lock_irqsave(&bus->dpc_tl_lock, flags);
+	if ((bus->idletime == BRCMF_IDLE_IMMEDIATE) &&
+	    list_empty(&bus->dpc_tsklst)) {
+		spin_unlock_irqrestore(&bus->dpc_tl_lock, flags);
+
 		bus->activity = false;
 		brcmf_sdbrcm_clkctl(bus, CLK_NONE, true);
+	} else {
+		spin_unlock_irqrestore(&bus->dpc_tl_lock, flags);
 	}
 
 	up(&bus->sdsem);
@@ -3774,23 +3514,20 @@
 	}
 	/* Count the interrupt call */
 	bus->sdcnt.intrcount++;
-	bus->ipend = true;
-
-	/* Shouldn't get this interrupt if we're sleeping? */
-	if (bus->sleeping) {
-		brcmf_dbg(ERROR, "INTERRUPT WHILE SLEEPING??\n");
-		return;
-	}
+	if (in_interrupt())
+		atomic_set(&bus->ipend, 1);
+	else
+		if (brcmf_sdio_intr_rstatus(bus)) {
+			brcmf_dbg(ERROR, "failed backplane access\n");
+			bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN;
+		}
 
 	/* Disable additional interrupts (is this needed now)? */
 	if (!bus->intr)
 		brcmf_dbg(ERROR, "isr w/o interrupt configured!\n");
 
-	bus->dpc_sched = true;
-	if (bus->dpc_tsk) {
-		brcmf_sdbrcm_adddpctsk(bus);
-		complete(&bus->dpc_wait);
-	}
+	brcmf_sdbrcm_adddpctsk(bus);
+	queue_work(bus->brcmf_wq, &bus->datawork);
 }
 
 static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus)
@@ -3798,13 +3535,10 @@
 #ifdef DEBUG
 	struct brcmf_bus *bus_if = dev_get_drvdata(bus->sdiodev->dev);
 #endif	/* DEBUG */
+	unsigned long flags;
 
 	brcmf_dbg(TIMER, "Enter\n");
 
-	/* Ignore the timer if simulating bus down */
-	if (bus->sleeping)
-		return false;
-
 	down(&bus->sdsem);
 
 	/* Poll period: check device if appropriate. */
@@ -3818,27 +3552,30 @@
 		if (!bus->intr ||
 		    (bus->sdcnt.intrcount == bus->sdcnt.lastintrs)) {
 
-			if (!bus->dpc_sched) {
+			spin_lock_irqsave(&bus->dpc_tl_lock, flags);
+			if (list_empty(&bus->dpc_tsklst)) {
 				u8 devpend;
+				spin_unlock_irqrestore(&bus->dpc_tl_lock,
+						       flags);
 				devpend = brcmf_sdio_regrb(bus->sdiodev,
 							   SDIO_CCCR_INTx,
 							   NULL);
 				intstatus =
 				    devpend & (INTR_STATUS_FUNC1 |
 					       INTR_STATUS_FUNC2);
+			} else {
+				spin_unlock_irqrestore(&bus->dpc_tl_lock,
+						       flags);
 			}
 
 			/* If there is something, make like the ISR and
 				 schedule the DPC */
 			if (intstatus) {
 				bus->sdcnt.pollcnt++;
-				bus->ipend = true;
+				atomic_set(&bus->ipend, 1);
 
-				bus->dpc_sched = true;
-				if (bus->dpc_tsk) {
-					brcmf_sdbrcm_adddpctsk(bus);
-					complete(&bus->dpc_wait);
-				}
+				brcmf_sdbrcm_adddpctsk(bus);
+				queue_work(bus->brcmf_wq, &bus->datawork);
 			}
 		}
 
@@ -3876,7 +3613,7 @@
 
 	up(&bus->sdsem);
 
-	return bus->ipend;
+	return (atomic_read(&bus->ipend) > 0);
 }
 
 static bool brcmf_sdbrcm_chipmatch(u16 chipid)
@@ -3892,6 +3629,26 @@
 	return false;
 }
 
+static void brcmf_sdio_dataworker(struct work_struct *work)
+{
+	struct brcmf_sdio *bus = container_of(work, struct brcmf_sdio,
+					      datawork);
+	struct list_head *cur_hd, *tmp_hd;
+	unsigned long flags;
+
+	spin_lock_irqsave(&bus->dpc_tl_lock, flags);
+	list_for_each_safe(cur_hd, tmp_hd, &bus->dpc_tsklst) {
+		spin_unlock_irqrestore(&bus->dpc_tl_lock, flags);
+
+		brcmf_sdbrcm_dpc(bus);
+
+		spin_lock_irqsave(&bus->dpc_tl_lock, flags);
+		list_del(cur_hd);
+		kfree(cur_hd);
+	}
+	spin_unlock_irqrestore(&bus->dpc_tl_lock, flags);
+}
+
 static void brcmf_sdbrcm_release_malloc(struct brcmf_sdio *bus)
 {
 	brcmf_dbg(TRACE, "Enter\n");
@@ -4024,7 +3781,6 @@
 			 SDIO_FUNC_ENABLE_1, NULL);
 
 	bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN;
-	bus->sleeping = false;
 	bus->rxflow = false;
 
 	/* Done with backplane-dependent accesses, can drop clock... */
@@ -4105,6 +3861,9 @@
 		/* De-register interrupt handler */
 		brcmf_sdio_intr_unregister(bus->sdiodev);
 
+		cancel_work_sync(&bus->datawork);
+		destroy_workqueue(bus->brcmf_wq);
+
 		if (bus->sdiodev->bus_if->drvr) {
 			brcmf_detach(bus->sdiodev->dev);
 			brcmf_sdbrcm_release_dongle(bus);
@@ -4144,8 +3903,6 @@
 	bus->rxbound = BRCMF_RXBOUND;
 	bus->txminmax = BRCMF_TXMINMAX;
 	bus->tx_seq = SDPCM_SEQUENCE_WRAP - 1;
-	bus->usebufpool = false;	/* Use bufpool if allocated,
-					 else use locally malloced rxbuf */
 
 	/* attempt to attach to the dongle */
 	if (!(brcmf_sdbrcm_probe_attach(bus, regsva))) {
@@ -4157,6 +3914,13 @@
 	init_waitqueue_head(&bus->ctrl_wait);
 	init_waitqueue_head(&bus->dcmd_resp_wait);
 
+	bus->brcmf_wq = create_singlethread_workqueue("brcmf_wq");
+	if (bus->brcmf_wq == NULL) {
+		brcmf_dbg(ERROR, "insufficient memory to create txworkqueue\n");
+		goto fail;
+	}
+	INIT_WORK(&bus->datawork, brcmf_sdio_dataworker);
+
 	/* Set up the watchdog timer */
 	init_timer(&bus->timer);
 	bus->timer.data = (unsigned long)bus;
@@ -4174,15 +3938,8 @@
 		bus->watchdog_tsk = NULL;
 	}
 	/* Initialize DPC thread */
-	init_completion(&bus->dpc_wait);
 	INIT_LIST_HEAD(&bus->dpc_tsklst);
 	spin_lock_init(&bus->dpc_tl_lock);
-	bus->dpc_tsk = kthread_run(brcmf_sdbrcm_dpc_thread,
-				   bus, "brcmf_dpc");
-	if (IS_ERR(bus->dpc_tsk)) {
-		pr_warn("brcmf_dpc thread failed to start\n");
-		bus->dpc_tsk = NULL;
-	}
 
 	/* Assign bus interface call back */
 	bus->sdiodev->bus_if->brcmf_bus_stop = brcmf_sdbrcm_bus_stop;
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h
index 29bf78d..0d30afd 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h
@@ -174,6 +174,8 @@
 			     u8 data, int *ret);
 extern void brcmf_sdio_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr,
 			     u32 data, int *ret);
+extern int brcmf_sdio_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr,
+				   void *data, bool write);
 
 /* Buffer transfer to/from device (client) core via cmd53.
  *   fn:       function number
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c
index c6d5aeb2..5fe6ec7 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c
@@ -81,10 +81,12 @@
 };
 
 struct brcmf_usb_image {
-	void *data;
-	u32 len;
+	struct list_head list;
+	s8 *fwname;
+	u8 *image;
+	int image_len;
 };
-static struct brcmf_usb_image g_image = { NULL, 0 };
+static struct list_head fw_image_list;
 
 struct intr_transfer_buf {
 	u32 notification;
@@ -132,8 +134,6 @@
 	wait_queue_head_t ctrl_wait;
 	ulong ctl_op;
 
-	bool rxctl_deferrespok;
-
 	struct urb *bulk_urb; /* used for FW download */
 	struct urb *intr_urb; /* URB for interrupt endpoint */
 	int intr_size;          /* Size of interrupt message */
@@ -299,17 +299,9 @@
 	devinfo->ctl_read.wLength = cpu_to_le16p(&size);
 	devinfo->ctl_urb->transfer_buffer_length = size;
 
-	if (devinfo->rxctl_deferrespok) {
-		/* BMAC model */
-		devinfo->ctl_read.bRequestType = USB_DIR_IN
-			| USB_TYPE_VENDOR | USB_RECIP_INTERFACE;
-		devinfo->ctl_read.bRequest = DL_DEFER_RESP_OK;
-	} else {
-		/* full dongle model */
-		devinfo->ctl_read.bRequestType = USB_DIR_IN
-			| USB_TYPE_CLASS | USB_RECIP_INTERFACE;
-		devinfo->ctl_read.bRequest = 1;
-	}
+	devinfo->ctl_read.bRequestType = USB_DIR_IN
+		| USB_TYPE_CLASS | USB_RECIP_INTERFACE;
+	devinfo->ctl_read.bRequest = 1;
 
 	usb_fill_control_urb(devinfo->ctl_urb,
 		devinfo->usbdev,
@@ -345,6 +337,7 @@
 	err = brcmf_usb_send_ctl(devinfo, buf, len);
 	if (err) {
 		brcmf_dbg(ERROR, "fail %d bytes: %d\n", err, len);
+		clear_bit(0, &devinfo->ctl_op);
 		return err;
 	}
 
@@ -375,6 +368,7 @@
 	err = brcmf_usb_recv_ctl(devinfo, buf, len);
 	if (err) {
 		brcmf_dbg(ERROR, "fail %d bytes: %d\n", err, len);
+		clear_bit(0, &devinfo->ctl_op);
 		return err;
 	}
 	devinfo->ctl_completed = false;
@@ -1152,10 +1146,6 @@
 {
 	brcmf_dbg(TRACE, "devinfo %p\n", devinfo);
 
-	/* store the image globally */
-	g_image.data = devinfo->image;
-	g_image.len = devinfo->image_len;
-
 	/* free the URBS */
 	brcmf_usb_free_q(&devinfo->rx_freeq, false);
 	brcmf_usb_free_q(&devinfo->tx_freeq, false);
@@ -1207,17 +1197,9 @@
 {
 	s8 *fwname;
 	const struct firmware *fw;
+	struct brcmf_usb_image *fw_image;
 	int err;
 
-	devinfo->image = g_image.data;
-	devinfo->image_len = g_image.len;
-
-	/*
-	 * if we have an image we can leave here.
-	 */
-	if (devinfo->image)
-		return 0;
-
 	switch (devinfo->bus_pub.devid) {
 	case 43143:
 		fwname = BRCMF_USB_43143_FW_NAME;
@@ -1235,6 +1217,14 @@
 		break;
 	}
 
+	list_for_each_entry(fw_image, &fw_image_list, list) {
+		if (fw_image->fwname == fwname) {
+			devinfo->image = fw_image->image;
+			devinfo->image_len = fw_image->image_len;
+			return 0;
+		}
+	}
+	/* fw image not yet loaded. Load it now and add to list */
 	err = request_firmware(&fw, fwname, devinfo->dev);
 	if (!fw) {
 		brcmf_dbg(ERROR, "fail to request firmware %s\n", fwname);
@@ -1245,14 +1235,24 @@
 		return -EINVAL;
 	}
 
-	devinfo->image = vmalloc(fw->size); /* plus nvram */
-	if (!devinfo->image)
+	fw_image = kzalloc(sizeof(*fw_image), GFP_ATOMIC);
+	if (!fw_image)
+		return -ENOMEM;
+	INIT_LIST_HEAD(&fw_image->list);
+	list_add_tail(&fw_image->list, &fw_image_list);
+	fw_image->fwname = fwname;
+	fw_image->image = vmalloc(fw->size);
+	if (!fw_image->image)
 		return -ENOMEM;
 
-	memcpy(devinfo->image, fw->data, fw->size);
-	devinfo->image_len = fw->size;
+	memcpy(fw_image->image, fw->data, fw->size);
+	fw_image->image_len = fw->size;
 
 	release_firmware(fw);
+
+	devinfo->image = fw_image->image;
+	devinfo->image_len = fw_image->image_len;
+
 	return 0;
 }
 
@@ -1304,8 +1304,6 @@
 		brcmf_dbg(ERROR, "usb_alloc_urb (ctl) failed\n");
 		goto error;
 	}
-	devinfo->rxctl_deferrespok = 0;
-
 	devinfo->bulk_urb = usb_alloc_urb(0, GFP_ATOMIC);
 	if (!devinfo->bulk_urb) {
 		brcmf_dbg(ERROR, "usb_alloc_urb (bulk) failed\n");
@@ -1340,10 +1338,8 @@
 	struct device *dev = devinfo->dev;
 
 	bus_pub = brcmf_usb_attach(devinfo, BRCMF_USB_NRXQ, BRCMF_USB_NTXQ);
-	if (!bus_pub) {
-		ret = -ENODEV;
-		goto fail;
-	}
+	if (!bus_pub)
+		return -ENODEV;
 
 	bus = kzalloc(sizeof(struct brcmf_bus), GFP_ATOMIC);
 	if (!bus) {
@@ -1596,15 +1592,25 @@
 	.disable_hub_initiated_lpm = 1,
 };
 
+static void brcmf_release_fw(struct list_head *q)
+{
+	struct brcmf_usb_image *fw_image, *next;
+
+	list_for_each_entry_safe(fw_image, next, q, list) {
+		vfree(fw_image->image);
+		list_del_init(&fw_image->list);
+	}
+}
+
+
 void brcmf_usb_exit(void)
 {
 	usb_deregister(&brcmf_usbdrvr);
-	vfree(g_image.data);
-	g_image.data = NULL;
-	g_image.len = 0;
+	brcmf_release_fw(&fw_image_list);
 }
 
 void brcmf_usb_init(void)
 {
+	INIT_LIST_HEAD(&fw_image_list);
 	usb_register(&brcmf_usbdrvr);
 }
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
index 65cf8f9..e40cfe8 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
@@ -36,6 +36,18 @@
 #include "dhd.h"
 #include "wl_cfg80211.h"
 
+#define BRCMF_SCAN_IE_LEN_MAX		2048
+#define BRCMF_PNO_VERSION		2
+#define BRCMF_PNO_TIME			30
+#define BRCMF_PNO_REPEAT		4
+#define BRCMF_PNO_FREQ_EXPO_MAX		3
+#define BRCMF_PNO_MAX_PFN_COUNT		16
+#define BRCMF_PNO_ENABLE_ADAPTSCAN_BIT	6
+#define BRCMF_PNO_HIDDEN_BIT		2
+#define BRCMF_PNO_WPA_AUTH_ANY		0xFFFFFFFF
+#define BRCMF_PNO_SCAN_COMPLETE		1
+#define BRCMF_PNO_SCAN_INCOMPLETE	0
+
 #define BRCMF_ASSOC_PARAMS_FIXED_SIZE \
 	(sizeof(struct brcmf_assoc_params_le) - sizeof(u16))
 
@@ -698,11 +710,11 @@
 	u32 n_channels;
 	s32 i;
 	s32 offset;
-	__le16 chanspec;
+	u16 chanspec;
 	u16 channel;
 	struct ieee80211_channel *req_channel;
 	char *ptr;
-	struct brcmf_ssid ssid;
+	struct brcmf_ssid_le ssid_le;
 
 	memcpy(params_le->bssid, ether_bcast, ETH_ALEN);
 	params_le->bss_type = DOT11_BSSTYPE_ANY;
@@ -745,13 +757,10 @@
 					chanspec |= WL_CHANSPEC_CTL_SB_UPPER;
 			}
 
-			params_le->channel_list[i] =
-				(channel & WL_CHANSPEC_CHAN_MASK) |
-				chanspec;
+			chanspec |= (channel & WL_CHANSPEC_CHAN_MASK);
 			WL_SCAN("Chan : %d, Channel spec: %x\n",
-				channel, params_le->channel_list[i]);
-			params_le->channel_list[i] =
-				cpu_to_le16(params_le->channel_list[i]);
+				channel, chanspec);
+			params_le->channel_list[i] = cpu_to_le16(chanspec);
 		}
 	} else {
 		WL_SCAN("Scanning all channels\n");
@@ -764,17 +773,18 @@
 		offset = roundup(offset, sizeof(u32));
 		ptr = (char *)params_le + offset;
 		for (i = 0; i < n_ssids; i++) {
-			memset(&ssid, 0, sizeof(ssid));
-			ssid.SSID_len = cpu_to_le32(request->ssids[i].ssid_len);
-			memcpy(ssid.SSID, request->ssids[i].ssid,
-					request->ssids[i].ssid_len);
-			if (!ssid.SSID_len)
+			memset(&ssid_le, 0, sizeof(ssid_le));
+			ssid_le.SSID_len =
+					cpu_to_le32(request->ssids[i].ssid_len);
+			memcpy(ssid_le.SSID, request->ssids[i].ssid,
+			       request->ssids[i].ssid_len);
+			if (!ssid_le.SSID_len)
 				WL_SCAN("%d: Broadcast scan\n", i);
 			else
 				WL_SCAN("%d: scan for  %s size =%d\n", i,
-				ssid.SSID, ssid.SSID_len);
-			memcpy(ptr, &ssid, sizeof(ssid));
-			ptr += sizeof(ssid);
+					ssid_le.SSID, ssid_le.SSID_len);
+			memcpy(ptr, &ssid_le, sizeof(ssid_le));
+			ptr += sizeof(ssid_le);
 		}
 	} else {
 		WL_SCAN("Broadcast scan %p\n", request->ssids);
@@ -832,7 +842,17 @@
 		if (err)
 			WL_ERR("Scan abort  failed\n");
 	}
-	if (scan_request) {
+	/*
+	 * e-scan can be initiated by scheduled scan
+	 * which takes precedence.
+	 */
+	if (cfg_priv->sched_escan) {
+		WL_SCAN("scheduled scan completed\n");
+		cfg_priv->sched_escan = false;
+		if (!aborted)
+			cfg80211_sched_scan_results(cfg_to_wiphy(cfg_priv));
+		brcmf_set_mpc(ndev, 1);
+	} else if (scan_request) {
 		WL_SCAN("ESCAN Completed scan: %s\n",
 				aborted ? "Aborted" : "Done");
 		cfg80211_scan_done(scan_request, aborted);
@@ -2591,11 +2611,13 @@
 	return err;
 }
 
-static void brcmf_term_iscan(struct brcmf_cfg80211_priv *cfg_priv)
+static void brcmf_abort_scanning(struct brcmf_cfg80211_priv *cfg_priv)
 {
 	struct brcmf_cfg80211_iscan_ctrl *iscan = cfg_to_iscan(cfg_priv);
+	struct escan_info *escan = &cfg_priv->escan_info;
 	struct brcmf_ssid ssid;
 
+	set_bit(WL_STATUS_SCAN_ABORTING, &cfg_priv->status);
 	if (cfg_priv->iscan_on) {
 		iscan->state = WL_ISCAN_STATE_IDLE;
 
@@ -2609,7 +2631,20 @@
 		/* Abort iscan running in FW */
 		memset(&ssid, 0, sizeof(ssid));
 		brcmf_run_iscan(iscan, &ssid, WL_SCAN_ACTION_ABORT);
+
+		if (cfg_priv->scan_request) {
+			/* Indidate scan abort to cfg80211 layer */
+			WL_INFO("Terminating scan in progress\n");
+			cfg80211_scan_done(cfg_priv->scan_request, true);
+			cfg_priv->scan_request = NULL;
+		}
 	}
+	if (cfg_priv->escan_on && cfg_priv->scan_request) {
+		escan->escan_state = WL_ESCAN_STATE_IDLE;
+		brcmf_notify_escan_complete(cfg_priv, escan->ndev, true, true);
+	}
+	clear_bit(WL_STATUS_SCANNING, &cfg_priv->status);
+	clear_bit(WL_STATUS_SCAN_ABORTING, &cfg_priv->status);
 }
 
 static void brcmf_notify_iscan_complete(struct brcmf_cfg80211_iscan_ctrl *iscan,
@@ -2840,10 +2875,13 @@
 		!memcmp(bss_info_le->SSID, bss->SSID, bss_info_le->SSID_len)) {
 		if ((bss->flags & WLC_BSS_RSSI_ON_CHANNEL) ==
 			(bss_info_le->flags & WLC_BSS_RSSI_ON_CHANNEL)) {
+			s16 bss_rssi = le16_to_cpu(bss->RSSI);
+			s16 bss_info_rssi = le16_to_cpu(bss_info_le->RSSI);
+
 			/* preserve max RSSI if the measurements are
 			* both on-channel or both off-channel
 			*/
-			if (bss_info_le->RSSI > bss->RSSI)
+			if (bss_info_rssi > bss_rssi)
 				bss->RSSI = bss_info_le->RSSI;
 		} else if ((bss->flags & WLC_BSS_RSSI_ON_CHANNEL) &&
 			(bss_info_le->flags & WLC_BSS_RSSI_ON_CHANNEL) == 0) {
@@ -2871,6 +2909,7 @@
 	u32 bi_length;
 	struct brcmf_scan_results *list;
 	u32 i;
+	bool aborted;
 
 	status = be32_to_cpu(e->status);
 
@@ -2943,16 +2982,9 @@
 			cfg_priv->bss_list = (struct brcmf_scan_results *)
 				cfg_priv->escan_info.escan_buf;
 			brcmf_inform_bss(cfg_priv);
-			if (status == BRCMF_E_STATUS_SUCCESS) {
-				WL_SCAN("ESCAN Completed\n");
-				brcmf_notify_escan_complete(cfg_priv, ndev,
-					false, false);
-			} else {
-				WL_ERR("ESCAN Aborted, Event 0x%x\n", status);
-				brcmf_notify_escan_complete(cfg_priv, ndev,
-					true, false);
-			}
-			brcmf_set_mpc(ndev, 1);
+			aborted = status != BRCMF_E_STATUS_SUCCESS;
+			brcmf_notify_escan_complete(cfg_priv, ndev, aborted,
+						    false);
 		} else
 			WL_ERR("Unexpected scan result 0x%x\n", status);
 	}
@@ -3037,18 +3069,10 @@
 		brcmf_delay(500);
 	}
 
-	set_bit(WL_STATUS_SCAN_ABORTING, &cfg_priv->status);
 	if (test_bit(WL_STATUS_READY, &cfg_priv->status))
-		brcmf_term_iscan(cfg_priv);
-
-	if (cfg_priv->scan_request) {
-		/* Indidate scan abort to cfg80211 layer */
-		WL_INFO("Terminating scan in progress\n");
-		cfg80211_scan_done(cfg_priv->scan_request, true);
-		cfg_priv->scan_request = NULL;
-	}
-	clear_bit(WL_STATUS_SCANNING, &cfg_priv->status);
-	clear_bit(WL_STATUS_SCAN_ABORTING, &cfg_priv->status);
+		brcmf_abort_scanning(cfg_priv);
+	else
+		clear_bit(WL_STATUS_SCANNING, &cfg_priv->status);
 
 	/* Turn off watchdog timer */
 	if (test_bit(WL_STATUS_READY, &cfg_priv->status))
@@ -3227,6 +3251,269 @@
 
 }
 
+/*
+ * PFN result doesn't have all the info which are
+ * required by the supplicant
+ * (For e.g IEs) Do a target Escan so that sched scan results are reported
+ * via wl_inform_single_bss in the required format. Escan does require the
+ * scan request in the form of cfg80211_scan_request. For timebeing, create
+ * cfg80211_scan_request one out of the received PNO event.
+ */
+static s32
+brcmf_notify_sched_scan_results(struct brcmf_cfg80211_priv *cfg_priv,
+				struct net_device *ndev,
+				const struct brcmf_event_msg *e, void *data)
+{
+	struct brcmf_pno_net_info_le *netinfo, *netinfo_start;
+	struct cfg80211_scan_request *request = NULL;
+	struct cfg80211_ssid *ssid = NULL;
+	struct ieee80211_channel *channel = NULL;
+	struct wiphy *wiphy = cfg_to_wiphy(cfg_priv);
+	int err = 0;
+	int channel_req = 0;
+	int band = 0;
+	struct brcmf_pno_scanresults_le *pfn_result;
+	u32 result_count;
+	u32 status;
+
+	WL_SCAN("Enter\n");
+
+	if (e->event_type == cpu_to_be32(BRCMF_E_PFN_NET_LOST)) {
+		WL_SCAN("PFN NET LOST event. Do Nothing\n");
+		return 0;
+	}
+
+	pfn_result = (struct brcmf_pno_scanresults_le *)data;
+	result_count = le32_to_cpu(pfn_result->count);
+	status = le32_to_cpu(pfn_result->status);
+
+	/*
+	 * PFN event is limited to fit 512 bytes so we may get
+	 * multiple NET_FOUND events. For now place a warning here.
+	 */
+	WARN_ON(status != BRCMF_PNO_SCAN_COMPLETE);
+	WL_SCAN("PFN NET FOUND event. count: %d\n", result_count);
+	if (result_count > 0) {
+		int i;
+
+		request = kzalloc(sizeof(*request), GFP_KERNEL);
+		ssid = kzalloc(sizeof(*ssid) * result_count, GFP_KERNEL);
+		channel = kzalloc(sizeof(*channel) * result_count, GFP_KERNEL);
+		if (!request || !ssid || !channel) {
+			err = -ENOMEM;
+			goto out_err;
+		}
+
+		request->wiphy = wiphy;
+		data += sizeof(struct brcmf_pno_scanresults_le);
+		netinfo_start = (struct brcmf_pno_net_info_le *)data;
+
+		for (i = 0; i < result_count; i++) {
+			netinfo = &netinfo_start[i];
+			if (!netinfo) {
+				WL_ERR("Invalid netinfo ptr. index: %d\n", i);
+				err = -EINVAL;
+				goto out_err;
+			}
+
+			WL_SCAN("SSID:%s Channel:%d\n",
+			netinfo->SSID, netinfo->channel);
+			memcpy(ssid[i].ssid, netinfo->SSID, netinfo->SSID_len);
+			ssid[i].ssid_len = netinfo->SSID_len;
+			request->n_ssids++;
+
+			channel_req = netinfo->channel;
+			if (channel_req <= CH_MAX_2G_CHANNEL)
+				band = NL80211_BAND_2GHZ;
+			else
+				band = NL80211_BAND_5GHZ;
+			channel[i].center_freq =
+				ieee80211_channel_to_frequency(channel_req,
+							       band);
+			channel[i].band = band;
+			channel[i].flags |= IEEE80211_CHAN_NO_HT40;
+			request->channels[i] = &channel[i];
+			request->n_channels++;
+		}
+
+		/* assign parsed ssid array */
+		if (request->n_ssids)
+			request->ssids = &ssid[0];
+
+		if (test_bit(WL_STATUS_SCANNING, &cfg_priv->status)) {
+			/* Abort any on-going scan */
+			brcmf_abort_scanning(cfg_priv);
+		}
+
+		set_bit(WL_STATUS_SCANNING, &cfg_priv->status);
+		err = brcmf_do_escan(cfg_priv, wiphy, ndev, request);
+		if (err) {
+			clear_bit(WL_STATUS_SCANNING, &cfg_priv->status);
+			goto out_err;
+		}
+		cfg_priv->sched_escan = true;
+		cfg_priv->scan_request = request;
+	} else {
+		WL_ERR("FALSE PNO Event. (pfn_count == 0)\n");
+		goto out_err;
+	}
+
+	kfree(ssid);
+	kfree(channel);
+	kfree(request);
+	return 0;
+
+out_err:
+	kfree(ssid);
+	kfree(channel);
+	kfree(request);
+	cfg80211_sched_scan_stopped(wiphy);
+	return err;
+}
+
+#ifndef CONFIG_BRCMISCAN
+static int brcmf_dev_pno_clean(struct net_device *ndev)
+{
+	char iovbuf[128];
+	int ret;
+
+	/* Disable pfn */
+	ret = brcmf_dev_intvar_set(ndev, "pfn", 0);
+	if (ret == 0) {
+		/* clear pfn */
+		ret = brcmf_dev_iovar_setbuf(ndev, "pfnclear", NULL, 0,
+					     iovbuf, sizeof(iovbuf));
+	}
+	if (ret < 0)
+		WL_ERR("failed code %d\n", ret);
+
+	return ret;
+}
+
+static int brcmf_dev_pno_config(struct net_device *ndev)
+{
+	struct brcmf_pno_param_le pfn_param;
+	char iovbuf[128];
+
+	memset(&pfn_param, 0, sizeof(pfn_param));
+	pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION);
+
+	/* set extra pno params */
+	pfn_param.flags = cpu_to_le16(1 << BRCMF_PNO_ENABLE_ADAPTSCAN_BIT);
+	pfn_param.repeat = BRCMF_PNO_REPEAT;
+	pfn_param.exp = BRCMF_PNO_FREQ_EXPO_MAX;
+
+	/* set up pno scan fr */
+	pfn_param.scan_freq = cpu_to_le32(BRCMF_PNO_TIME);
+
+	return brcmf_dev_iovar_setbuf(ndev, "pfn_set",
+				      &pfn_param, sizeof(pfn_param),
+				      iovbuf, sizeof(iovbuf));
+}
+
+static int
+brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy,
+				struct net_device *ndev,
+				struct cfg80211_sched_scan_request *request)
+{
+	char iovbuf[128];
+	struct brcmf_cfg80211_priv *cfg_priv = wiphy_priv(wiphy);
+	struct brcmf_pno_net_param_le pfn;
+	int i;
+	int ret = 0;
+
+	WL_SCAN("Enter n_match_sets:%d   n_ssids:%d\n",
+		request->n_match_sets, request->n_ssids);
+	if (test_bit(WL_STATUS_SCANNING, &cfg_priv->status)) {
+		WL_ERR("Scanning already : status (%lu)\n", cfg_priv->status);
+		return -EAGAIN;
+	}
+
+	if (!request || !request->n_ssids || !request->n_match_sets) {
+		WL_ERR("Invalid sched scan req!! n_ssids:%d\n",
+		       request->n_ssids);
+		return -EINVAL;
+	}
+
+	if (request->n_ssids > 0) {
+		for (i = 0; i < request->n_ssids; i++) {
+			/* Active scan req for ssids */
+			WL_SCAN(">>> Active scan req for ssid (%s)\n",
+				request->ssids[i].ssid);
+
+			/*
+			 * match_set ssids is a supert set of n_ssid list,
+			 * so we need not add these set seperately.
+			 */
+		}
+	}
+
+	if (request->n_match_sets > 0) {
+		/* clean up everything */
+		ret = brcmf_dev_pno_clean(ndev);
+		if  (ret < 0) {
+			WL_ERR("failed error=%d\n", ret);
+			return ret;
+		}
+
+		/* configure pno */
+		ret = brcmf_dev_pno_config(ndev);
+		if (ret < 0) {
+			WL_ERR("PNO setup failed!! ret=%d\n", ret);
+			return -EINVAL;
+		}
+
+		/* configure each match set */
+		for (i = 0; i < request->n_match_sets; i++) {
+			struct cfg80211_ssid *ssid;
+			u32 ssid_len;
+
+			ssid = &request->match_sets[i].ssid;
+			ssid_len = ssid->ssid_len;
+
+			if (!ssid_len) {
+				WL_ERR("skip broadcast ssid\n");
+				continue;
+			}
+			pfn.auth = cpu_to_le32(WLAN_AUTH_OPEN);
+			pfn.wpa_auth = cpu_to_le32(BRCMF_PNO_WPA_AUTH_ANY);
+			pfn.wsec = cpu_to_le32(0);
+			pfn.infra = cpu_to_le32(1);
+			pfn.flags = cpu_to_le32(1 << BRCMF_PNO_HIDDEN_BIT);
+			pfn.ssid.SSID_len = cpu_to_le32(ssid_len);
+			memcpy(pfn.ssid.SSID, ssid->ssid, ssid_len);
+			ret = brcmf_dev_iovar_setbuf(ndev, "pfn_add",
+						     &pfn, sizeof(pfn),
+						     iovbuf, sizeof(iovbuf));
+			WL_SCAN(">>> PNO filter %s for ssid (%s)\n",
+				ret == 0 ? "set" : "failed",
+				ssid->ssid);
+		}
+		/* Enable the PNO */
+		if (brcmf_dev_intvar_set(ndev, "pfn", 1) < 0) {
+			WL_ERR("PNO enable failed!! ret=%d\n", ret);
+			return -EINVAL;
+		}
+	} else {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy,
+					  struct net_device *ndev)
+{
+	struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy);
+
+	WL_SCAN("enter\n");
+	brcmf_dev_pno_clean(ndev);
+	if (cfg_priv->sched_escan)
+		brcmf_notify_escan_complete(cfg_priv, ndev, true, true);
+	return 0;
+}
+#endif /* CONFIG_BRCMISCAN */
+
 #ifdef CONFIG_NL80211_TESTMODE
 static int brcmf_cfg80211_testmode(struct wiphy *wiphy, void *data, int len)
 {
@@ -3269,6 +3556,11 @@
 	.set_pmksa = brcmf_cfg80211_set_pmksa,
 	.del_pmksa = brcmf_cfg80211_del_pmksa,
 	.flush_pmksa = brcmf_cfg80211_flush_pmksa,
+#ifndef CONFIG_BRCMISCAN
+	/* scheduled scan need e-scan, which is mutual exclusive with i-scan */
+	.sched_scan_start = brcmf_cfg80211_sched_scan_start,
+	.sched_scan_stop = brcmf_cfg80211_sched_scan_stop,
+#endif
 #ifdef CONFIG_NL80211_TESTMODE
 	.testmode_cmd = brcmf_cfg80211_testmode
 #endif
@@ -3290,6 +3582,17 @@
 	return err;
 }
 
+static void brcmf_wiphy_pno_params(struct wiphy *wiphy)
+{
+#ifndef CONFIG_BRCMFISCAN
+	/* scheduled scan settings */
+	wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT;
+	wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT;
+	wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
+	wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
+#endif
+}
+
 static struct wireless_dev *brcmf_alloc_wdev(s32 sizeof_iface,
 					  struct device *ndev)
 {
@@ -3328,6 +3631,7 @@
 								 * save mode
 								 * by default
 								 */
+	brcmf_wiphy_pno_params(wdev->wiphy);
 	err = wiphy_register(wdev->wiphy);
 	if (err < 0) {
 		WL_ERR("Could not register wiphy device (%d)\n", err);
@@ -3730,6 +4034,7 @@
 	el->handler[BRCMF_E_ROAM] = brcmf_notify_roaming_status;
 	el->handler[BRCMF_E_MIC_ERROR] = brcmf_notify_mic_status;
 	el->handler[BRCMF_E_SET_SSID] = brcmf_notify_connect_status;
+	el->handler[BRCMF_E_PFN_NET_FOUND] = brcmf_notify_sched_scan_results;
 }
 
 static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_priv *cfg_priv)
@@ -3955,13 +4260,13 @@
 	cfg_priv->dongle_up = false;	/* dongle down */
 	brcmf_flush_eq(cfg_priv);
 	brcmf_link_down(cfg_priv);
-	brcmf_term_iscan(cfg_priv);
+	brcmf_abort_scanning(cfg_priv);
 	brcmf_deinit_priv_mem(cfg_priv);
 }
 
 struct brcmf_cfg80211_dev *brcmf_cfg80211_attach(struct net_device *ndev,
 						 struct device *busdev,
-						 void *data)
+						 struct brcmf_pub *drvr)
 {
 	struct wireless_dev *wdev;
 	struct brcmf_cfg80211_priv *cfg_priv;
@@ -3986,7 +4291,7 @@
 	wdev->iftype = brcmf_mode_to_nl80211_iftype(WL_MODE_BSS);
 	cfg_priv = wdev_to_cfg(wdev);
 	cfg_priv->wdev = wdev;
-	cfg_priv->pub = data;
+	cfg_priv->pub = drvr;
 	ci = (struct brcmf_cfg80211_iface *)&cfg_priv->ci;
 	ci->cfg_priv = cfg_priv;
 	ndev->ieee80211_ptr = wdev;
@@ -4101,6 +4406,7 @@
 	setbit(eventmask, BRCMF_E_JOIN_START);
 	setbit(eventmask, BRCMF_E_SCAN_COMPLETE);
 	setbit(eventmask, BRCMF_E_ESCAN_RESULT);
+	setbit(eventmask, BRCMF_E_PFN_NET_FOUND);
 
 	brcmf_c_mkiovar("event_msgs", eventmask, BRCMF_EVENTING_MASK_LEN,
 			iovbuf, sizeof(iovbuf));
@@ -4233,7 +4539,7 @@
 		return err;
 	}
 
-	phy = ((char *)&phy_list)[1];
+	phy = ((char *)&phy_list)[0];
 	WL_INFO("%c phy\n", phy);
 	if (phy == 'n' || phy == 'a') {
 		wiphy = cfg_to_wiphy(cfg_priv);
@@ -4366,17 +4672,8 @@
 		brcmf_delay(500);
 	}
 
-	set_bit(WL_STATUS_SCAN_ABORTING, &cfg_priv->status);
-	brcmf_term_iscan(cfg_priv);
-	if (cfg_priv->scan_request) {
-		cfg80211_scan_done(cfg_priv->scan_request, true);
-		/* May need to perform this to cover rmmod */
-		/* wl_set_mpc(cfg_to_ndev(wl), 1); */
-		cfg_priv->scan_request = NULL;
-	}
+	brcmf_abort_scanning(cfg_priv);
 	clear_bit(WL_STATUS_READY, &cfg_priv->status);
-	clear_bit(WL_STATUS_SCANNING, &cfg_priv->status);
-	clear_bit(WL_STATUS_SCAN_ABORTING, &cfg_priv->status);
 
 	brcmf_debugfs_remove_netdev(cfg_priv);
 
@@ -4409,20 +4706,3 @@
 	return err;
 }
 
-static __used s32 brcmf_add_ie(struct brcmf_cfg80211_priv *cfg_priv,
-			       u8 t, u8 l, u8 *v)
-{
-	struct brcmf_cfg80211_ie *ie = &cfg_priv->ie;
-	s32 err = 0;
-
-	if (ie->offset + l + 2 > WL_TLV_INFO_MAX) {
-		WL_ERR("ei crosses buffer boundary\n");
-		return -ENOSPC;
-	}
-	ie->buf[ie->offset] = t;
-	ie->buf[ie->offset + 1] = l;
-	memcpy(&ie->buf[ie->offset + 2], v, l);
-	ie->offset += l + 2;
-
-	return err;
-}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h
index 3b21297..52e408e 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h
@@ -295,50 +295,168 @@
 	struct net_device *ndev;
 };
 
-/* dongle private data of cfg80211 interface */
+/**
+ * struct brcmf_pno_param_le - PNO scan configuration parameters
+ *
+ * @version: PNO parameters version.
+ * @scan_freq: scan frequency.
+ * @lost_network_timeout: #sec. to declare discovered network as lost.
+ * @flags: Bit field to control features of PFN such as sort criteria auto
+ *	enable switch and background scan.
+ * @rssi_margin: Margin to avoid jitter for choosing a PFN based on RSSI sort
+ *	criteria.
+ * @bestn: number of best networks in each scan.
+ * @mscan: number of scans recorded.
+ * @repeat: minimum number of scan intervals before scan frequency changes
+ *	in adaptive scan.
+ * @exp: exponent of 2 for maximum scan interval.
+ * @slow_freq: slow scan period.
+ */
+struct brcmf_pno_param_le {
+	__le32 version;
+	__le32 scan_freq;
+	__le32 lost_network_timeout;
+	__le16 flags;
+	__le16 rssi_margin;
+	u8 bestn;
+	u8 mscan;
+	u8 repeat;
+	u8 exp;
+	__le32 slow_freq;
+};
+
+/**
+ * struct brcmf_pno_net_param_le - scan parameters per preferred network.
+ *
+ * @ssid: ssid name and its length.
+ * @flags: bit2: hidden.
+ * @infra: BSS vs IBSS.
+ * @auth: Open vs Closed.
+ * @wpa_auth: WPA type.
+ * @wsec: wsec value.
+ */
+struct brcmf_pno_net_param_le {
+	struct brcmf_ssid_le ssid;
+	__le32 flags;
+	__le32 infra;
+	__le32 auth;
+	__le32 wpa_auth;
+	__le32 wsec;
+};
+
+/**
+ * struct brcmf_pno_net_info_le - information per found network.
+ *
+ * @bssid: BSS network identifier.
+ * @channel: channel number only.
+ * @SSID_len: length of ssid.
+ * @SSID: ssid characters.
+ * @RSSI: receive signal strength (in dBm).
+ * @timestamp: age in seconds.
+ */
+struct brcmf_pno_net_info_le {
+	u8 bssid[ETH_ALEN];
+	u8 channel;
+	u8 SSID_len;
+	u8 SSID[32];
+	__le16	RSSI;
+	__le16	timestamp;
+};
+
+/**
+ * struct brcmf_pno_scanresults_le - result returned in PNO NET FOUND event.
+ *
+ * @version: PNO version identifier.
+ * @status: indicates completion status of PNO scan.
+ * @count: amount of brcmf_pno_net_info_le entries appended.
+ */
+struct brcmf_pno_scanresults_le {
+	__le32 version;
+	__le32 status;
+	__le32 count;
+};
+
+/**
+ * struct brcmf_cfg80211_priv - dongle private data of cfg80211 interface
+ *
+ * @wdev: representing wl cfg80211 device.
+ * @conf: dongle configuration.
+ * @scan_request: cfg80211 scan request object.
+ * @el: main event loop.
+ * @evt_q_list: used for event queue.
+ * @evt_q_lock: for event queue synchronization.
+ * @usr_sync: mainly for dongle up/down synchronization.
+ * @bss_list: bss_list holding scanned ap information.
+ * @scan_results: results of the last scan.
+ * @scan_req_int: internal scan request object.
+ * @bss_info: bss information for cfg80211 layer.
+ * @ie: information element object for internal purpose.
+ * @profile: holding dongle profile.
+ * @iscan: iscan controller information.
+ * @conn_info: association info.
+ * @pmk_list: wpa2 pmk list.
+ * @event_work: event handler work struct.
+ * @status: current dongle status.
+ * @pub: common driver information.
+ * @channel: current channel.
+ * @iscan_on: iscan on/off switch.
+ * @iscan_kickstart: indicate iscan already started.
+ * @active_scan: current scan mode.
+ * @sched_escan: e-scan for scheduled scan support running.
+ * @ibss_starter: indicates this sta is ibss starter.
+ * @link_up: link/connection up flag.
+ * @pwr_save: indicate whether dongle to support power save mode.
+ * @dongle_up: indicate whether dongle up or not.
+ * @roam_on: on/off switch for dongle self-roaming.
+ * @scan_tried: indicates if first scan attempted.
+ * @dcmd_buf: dcmd buffer.
+ * @extra_buf: mainly to grab assoc information.
+ * @debugfsdir: debugfs folder for this device.
+ * @escan_on: escan on/off switch.
+ * @escan_info: escan information.
+ * @escan_timeout: Timer for catch scan timeout.
+ * @escan_timeout_work: scan timeout worker.
+ * @escan_ioctl_buf: dongle command buffer for escan commands.
+ * @ci: used to link this structure to netdev private data.
+ */
 struct brcmf_cfg80211_priv {
-	struct wireless_dev *wdev;	/* representing wl cfg80211 device */
-	struct brcmf_cfg80211_conf *conf;	/* dongle configuration */
-	struct cfg80211_scan_request *scan_request;	/* scan request
-							 object */
-	struct brcmf_cfg80211_event_loop el;	/* main event loop */
-	struct list_head evt_q_list;	/* used for event queue */
-	spinlock_t	 evt_q_lock;	/* for event queue synchronization */
-	struct mutex usr_sync;	/* maily for dongle up/down synchronization */
-	struct brcmf_scan_results *bss_list;	/* bss_list holding scanned
-						 ap information */
+	struct wireless_dev *wdev;
+	struct brcmf_cfg80211_conf *conf;
+	struct cfg80211_scan_request *scan_request;
+	struct brcmf_cfg80211_event_loop el;
+	struct list_head evt_q_list;
+	spinlock_t	 evt_q_lock;
+	struct mutex usr_sync;
+	struct brcmf_scan_results *bss_list;
 	struct brcmf_scan_results *scan_results;
-	struct brcmf_cfg80211_scan_req *scan_req_int;	/* scan request object
-						 for internal purpose */
-	struct wl_cfg80211_bss_info *bss_info;	/* bss information for
-						 cfg80211 layer */
-	struct brcmf_cfg80211_ie ie;	/* information element object for
-					 internal purpose */
-	struct brcmf_cfg80211_profile *profile;	/* holding dongle profile */
-	struct brcmf_cfg80211_iscan_ctrl *iscan;	/* iscan controller */
-	struct brcmf_cfg80211_connect_info conn_info; /* association info */
-	struct brcmf_cfg80211_pmk_list *pmk_list;	/* wpa2 pmk list */
-	struct work_struct event_work;	/* event handler work struct */
-	unsigned long status;		/* current dongle status */
-	void *pub;
-	u32 channel;		/* current channel */
-	bool iscan_on;		/* iscan on/off switch */
-	bool iscan_kickstart;	/* indicate iscan already started */
-	bool active_scan;	/* current scan mode */
-	bool ibss_starter;	/* indicates this sta is ibss starter */
-	bool link_up;		/* link/connection up flag */
-	bool pwr_save;		/* indicate whether dongle to support
-					 power save mode */
-	bool dongle_up;		/* indicate whether dongle up or not */
-	bool roam_on;		/* on/off switch for dongle self-roaming */
-	bool scan_tried;	/* indicates if first scan attempted */
-	u8 *dcmd_buf;		/* dcmd buffer */
-	u8 *extra_buf;		/* maily to grab assoc information */
+	struct brcmf_cfg80211_scan_req *scan_req_int;
+	struct wl_cfg80211_bss_info *bss_info;
+	struct brcmf_cfg80211_ie ie;
+	struct brcmf_cfg80211_profile *profile;
+	struct brcmf_cfg80211_iscan_ctrl *iscan;
+	struct brcmf_cfg80211_connect_info conn_info;
+	struct brcmf_cfg80211_pmk_list *pmk_list;
+	struct work_struct event_work;
+	unsigned long status;
+	struct brcmf_pub *pub;
+	u32 channel;
+	bool iscan_on;
+	bool iscan_kickstart;
+	bool active_scan;
+	bool sched_escan;
+	bool ibss_starter;
+	bool link_up;
+	bool pwr_save;
+	bool dongle_up;
+	bool roam_on;
+	bool scan_tried;
+	u8 *dcmd_buf;
+	u8 *extra_buf;
 	struct dentry *debugfsdir;
-	bool escan_on;		/* escan on/off switch */
-	struct escan_info escan_info;   /* escan information */
-	struct timer_list escan_timeout;   /* Timer for catch scan timeout */
-	struct work_struct escan_timeout_work;	/* scan timeout worker */
+	bool escan_on;
+	struct escan_info escan_info;
+	struct timer_list escan_timeout;
+	struct work_struct escan_timeout_work;
 	u8 *escan_ioctl_buf;
 	u8 ci[0] __aligned(NETDEV_ALIGN);
 };
@@ -379,7 +497,7 @@
 
 extern struct brcmf_cfg80211_dev *brcmf_cfg80211_attach(struct net_device *ndev,
 							struct device *busdev,
-							void *data);
+							struct brcmf_pub *drvr);
 extern void brcmf_cfg80211_detach(struct brcmf_cfg80211_dev *cfg);
 
 /* event handler from dongle */
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
index 718da8d..a744ea5 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
@@ -304,7 +304,10 @@
 	wl->mute_tx = true;
 
 	if (!wl->pub->up)
-		err = brcms_up(wl);
+		if (!blocked)
+			err = brcms_up(wl);
+		else
+			err = -ERFKILL;
 	else
 		err = -ENODEV;
 	spin_unlock_bh(&wl->lock);
diff --git a/drivers/net/wireless/ipw2x00/libipw_wx.c b/drivers/net/wireless/ipw2x00/libipw_wx.c
index 1571505..54aba47 100644
--- a/drivers/net/wireless/ipw2x00/libipw_wx.c
+++ b/drivers/net/wireless/ipw2x00/libipw_wx.c
@@ -675,7 +675,7 @@
 	}
       done:
 	if (ieee->set_security)
-		ieee->set_security(ieee->dev, &sec);
+		ieee->set_security(dev, &sec);
 
 	return ret;
 }
diff --git a/drivers/net/wireless/iwlegacy/common.c b/drivers/net/wireless/iwlegacy/common.c
index eb99875..318ed3c 100644
--- a/drivers/net/wireless/iwlegacy/common.c
+++ b/drivers/net/wireless/iwlegacy/common.c
@@ -1586,9 +1586,9 @@
 		return 0;
 
 	frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ);
-	memcpy(frame->da, il_bcast_addr, ETH_ALEN);
+	eth_broadcast_addr(frame->da);
 	memcpy(frame->sa, ta, ETH_ALEN);
-	memcpy(frame->bssid, il_bcast_addr, ETH_ALEN);
+	eth_broadcast_addr(frame->bssid);
 	frame->seq_ctrl = 0;
 
 	len += 24;
diff --git a/drivers/net/wireless/iwlwifi/dvm/scan.c b/drivers/net/wireless/iwlwifi/dvm/scan.c
index e3467fa..bb9f625 100644
--- a/drivers/net/wireless/iwlwifi/dvm/scan.c
+++ b/drivers/net/wireless/iwlwifi/dvm/scan.c
@@ -612,9 +612,9 @@
 		return 0;
 
 	frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ);
-	memcpy(frame->da, iwl_bcast_addr, ETH_ALEN);
+	eth_broadcast_addr(frame->da);
 	memcpy(frame->sa, ta, ETH_ALEN);
-	memcpy(frame->bssid, iwl_bcast_addr, ETH_ALEN);
+	eth_broadcast_addr(frame->bssid);
 	frame->seq_ctrl = 0;
 
 	len += 24;
diff --git a/drivers/net/wireless/iwlwifi/dvm/sta.c b/drivers/net/wireless/iwlwifi/dvm/sta.c
index fe36a38..cd9b6de 100644
--- a/drivers/net/wireless/iwlwifi/dvm/sta.c
+++ b/drivers/net/wireless/iwlwifi/dvm/sta.c
@@ -128,10 +128,11 @@
 			       struct iwl_device_cmd *cmd)
 {
 	struct iwl_rx_packet *pkt = rxb_addr(rxb);
-	struct iwl_addsta_cmd *addsta =
-		(struct iwl_addsta_cmd *) cmd->payload;
 
-	return iwl_process_add_sta_resp(priv, addsta, pkt);
+	if (!cmd)
+		return 0;
+
+	return iwl_process_add_sta_resp(priv, (void *)cmd->payload, pkt);
 }
 
 int iwl_send_add_sta(struct iwl_priv *priv,
diff --git a/drivers/net/wireless/iwlwifi/dvm/ucode.c b/drivers/net/wireless/iwlwifi/dvm/ucode.c
index 6d8d6dd..2cb1efb 100644
--- a/drivers/net/wireless/iwlwifi/dvm/ucode.c
+++ b/drivers/net/wireless/iwlwifi/dvm/ucode.c
@@ -295,7 +295,7 @@
 static int iwl_verify_sec_sparse(struct iwl_priv *priv,
 				  const struct fw_desc *fw_desc)
 {
-	__le32 *image = (__le32 *)fw_desc->v_addr;
+	__le32 *image = (__le32 *)fw_desc->data;
 	u32 len = fw_desc->len;
 	u32 val;
 	u32 i;
@@ -319,7 +319,7 @@
 static void iwl_print_mismatch_sec(struct iwl_priv *priv,
 				    const struct fw_desc *fw_desc)
 {
-	__le32 *image = (__le32 *)fw_desc->v_addr;
+	__le32 *image = (__le32 *)fw_desc->data;
 	u32 len = fw_desc->len;
 	u32 val;
 	u32 offs;
diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c
index 48d6d44..198634b 100644
--- a/drivers/net/wireless/iwlwifi/iwl-drv.c
+++ b/drivers/net/wireless/iwlwifi/iwl-drv.c
@@ -64,6 +64,7 @@
 #include <linux/dma-mapping.h>
 #include <linux/firmware.h>
 #include <linux/module.h>
+#include <linux/vmalloc.h>
 
 #include "iwl-drv.h"
 #include "iwl-debug.h"
@@ -164,10 +165,8 @@
 
 static void iwl_free_fw_desc(struct iwl_drv *drv, struct fw_desc *desc)
 {
-	if (desc->v_addr)
-		dma_free_coherent(drv->trans->dev, desc->len,
-				  desc->v_addr, desc->p_addr);
-	desc->v_addr = NULL;
+	vfree(desc->data);
+	desc->data = NULL;
 	desc->len = 0;
 }
 
@@ -186,21 +185,24 @@
 }
 
 static int iwl_alloc_fw_desc(struct iwl_drv *drv, struct fw_desc *desc,
-		      struct fw_sec *sec)
+			     struct fw_sec *sec)
 {
-	if (!sec || !sec->size) {
-		desc->v_addr = NULL;
-		return -EINVAL;
-	}
+	void *data;
 
-	desc->v_addr = dma_alloc_coherent(drv->trans->dev, sec->size,
-					  &desc->p_addr, GFP_KERNEL);
-	if (!desc->v_addr)
+	desc->data = NULL;
+
+	if (!sec || !sec->size)
+		return -EINVAL;
+
+	data = vmalloc(sec->size);
+	if (!data)
 		return -ENOMEM;
 
 	desc->len = sec->size;
 	desc->offset = sec->offset;
-	memcpy(desc->v_addr, sec->data, sec->size);
+	memcpy(data, sec->data, desc->len);
+	desc->data = data;
+
 	return 0;
 }
 
diff --git a/drivers/net/wireless/iwlwifi/iwl-fw.h b/drivers/net/wireless/iwlwifi/iwl-fw.h
index 2153e4c..d1a86b6 100644
--- a/drivers/net/wireless/iwlwifi/iwl-fw.h
+++ b/drivers/net/wireless/iwlwifi/iwl-fw.h
@@ -124,8 +124,7 @@
 
 /* one for each uCode image (inst/data, init/runtime/wowlan) */
 struct fw_desc {
-	dma_addr_t p_addr;	/* hardware address */
-	void *v_addr;		/* software address */
+	const void *data;	/* vmalloc'ed data */
 	u32 len;		/* size in bytes */
 	u32 offset;		/* offset in the device */
 };
diff --git a/drivers/net/wireless/iwlwifi/pcie/drv.c b/drivers/net/wireless/iwlwifi/pcie/drv.c
index 89bfb43..2a46753 100644
--- a/drivers/net/wireless/iwlwifi/pcie/drv.c
+++ b/drivers/net/wireless/iwlwifi/pcie/drv.c
@@ -263,8 +263,6 @@
 /* PCI registers */
 #define PCI_CFG_RETRY_TIMEOUT	0x041
 
-#ifndef CONFIG_IWLWIFI_IDI
-
 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);
@@ -307,8 +305,6 @@
 	pci_set_drvdata(pdev, NULL);
 }
 
-#endif /* CONFIG_IWLWIFI_IDI */
-
 #ifdef CONFIG_PM_SLEEP
 
 static int iwl_pci_suspend(struct device *device)
@@ -353,15 +349,6 @@
 
 #endif
 
-#ifdef CONFIG_IWLWIFI_IDI
-/*
- * Defined externally in iwl-idi.c
- */
-int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent);
-void __devexit iwl_pci_remove(struct pci_dev *pdev);
-
-#endif /* CONFIG_IWLWIFI_IDI */
-
 static struct pci_driver iwl_pci_driver = {
 	.name = DRV_NAME,
 	.id_table = iwl_hw_card_ids,
diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h
index 71c7994..401178f 100644
--- a/drivers/net/wireless/iwlwifi/pcie/internal.h
+++ b/drivers/net/wireless/iwlwifi/pcie/internal.h
@@ -311,7 +311,7 @@
 ******************************************************/
 void iwl_bg_rx_replenish(struct work_struct *data);
 void iwl_irq_tasklet(struct iwl_trans *trans);
-void iwlagn_rx_replenish(struct iwl_trans *trans);
+void iwl_rx_replenish(struct iwl_trans *trans);
 void iwl_rx_queue_update_write_ptr(struct iwl_trans *trans,
 				   struct iwl_rx_queue *q);
 
diff --git a/drivers/net/wireless/iwlwifi/pcie/rx.c b/drivers/net/wireless/iwlwifi/pcie/rx.c
index 4983720..17c8e5d 100644
--- a/drivers/net/wireless/iwlwifi/pcie/rx.c
+++ b/drivers/net/wireless/iwlwifi/pcie/rx.c
@@ -35,10 +35,6 @@
 #include "internal.h"
 #include "iwl-op-mode.h"
 
-#ifdef CONFIG_IWLWIFI_IDI
-#include "iwl-amfh.h"
-#endif
-
 /******************************************************************************
  *
  * RX path functions
@@ -181,15 +177,15 @@
 }
 
 /**
- * iwlagn_dma_addr2rbd_ptr - convert a DMA address to a uCode read buffer ptr
+ * iwl_dma_addr2rbd_ptr - convert a DMA address to a uCode read buffer ptr
  */
-static inline __le32 iwlagn_dma_addr2rbd_ptr(dma_addr_t dma_addr)
+static inline __le32 iwl_dma_addr2rbd_ptr(dma_addr_t dma_addr)
 {
 	return cpu_to_le32((u32)(dma_addr >> 8));
 }
 
 /**
- * iwlagn_rx_queue_restock - refill RX queue from pre-allocated pool
+ * iwl_rx_queue_restock - refill RX queue from pre-allocated pool
  *
  * If there are slots in the RX queue that need to be restocked,
  * and we have free pre-allocated buffers, fill the ranks as much
@@ -199,7 +195,7 @@
  * also updates the memory address in the firmware to reference the new
  * target buffer.
  */
-static void iwlagn_rx_queue_restock(struct iwl_trans *trans)
+static void iwl_rx_queue_restock(struct iwl_trans *trans)
 {
 	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
 	struct iwl_rx_queue *rxq = &trans_pcie->rxq;
@@ -207,6 +203,17 @@
 	struct iwl_rx_mem_buffer *rxb;
 	unsigned long flags;
 
+	/*
+	 * If the device isn't enabled - not need to try to add buffers...
+	 * This can happen when we stop the device and still have an interrupt
+	 * pending. We stop the APM before we sync the interrupts / tasklets
+	 * because we have to (see comment there). On the other hand, since
+	 * the APM is stopped, we cannot access the HW (in particular not prph).
+	 * So don't try to restock if the APM has been already stopped.
+	 */
+	if (!test_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status))
+		return;
+
 	spin_lock_irqsave(&rxq->lock, flags);
 	while ((iwl_rx_queue_space(rxq) > 0) && (rxq->free_count)) {
 		/* The overwritten rxb must be a used one */
@@ -219,7 +226,7 @@
 		list_del(element);
 
 		/* Point to Rx buffer via next RBD in circular buffer */
-		rxq->bd[rxq->write] = iwlagn_dma_addr2rbd_ptr(rxb->page_dma);
+		rxq->bd[rxq->write] = iwl_dma_addr2rbd_ptr(rxb->page_dma);
 		rxq->queue[rxq->write] = rxb;
 		rxq->write = (rxq->write + 1) & RX_QUEUE_MASK;
 		rxq->free_count--;
@@ -230,7 +237,6 @@
 	if (rxq->free_count <= RX_LOW_WATERMARK)
 		schedule_work(&trans_pcie->rx_replenish);
 
-
 	/* If we've added more space for the firmware to place data, tell it.
 	 * Increment device's write pointer in multiples of 8. */
 	if (rxq->write_actual != (rxq->write & ~0x7)) {
@@ -241,15 +247,16 @@
 	}
 }
 
-/**
- * iwlagn_rx_replenish - Move all used packet from rx_used to rx_free
+/*
+ * iwl_rx_allocate - allocate a page for each used RBD
  *
- * When moving to rx_free an SKB is allocated for the slot.
- *
- * Also restock the Rx queue via iwl_rx_queue_restock.
- * This is called as a scheduled work item (except for during initialization)
+ * A used RBD is an Rx buffer that has been given to the stack. To use it again
+ * a page must be allocated and the RBD must point to the page. This function
+ * doesn't change the HW pointer but handles the list of pages that is used by
+ * iwl_rx_queue_restock. The latter function will update the HW to use the newly
+ * allocated buffers.
  */
-static void iwlagn_rx_allocate(struct iwl_trans *trans, gfp_t priority)
+static void iwl_rx_allocate(struct iwl_trans *trans, gfp_t priority)
 {
 	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
 	struct iwl_rx_queue *rxq = &trans_pcie->rxq;
@@ -328,23 +335,31 @@
 	}
 }
 
-void iwlagn_rx_replenish(struct iwl_trans *trans)
+/*
+ * iwl_rx_replenish - Move all used buffers from rx_used to rx_free
+ *
+ * When moving to rx_free an page is allocated for the slot.
+ *
+ * Also restock the Rx queue via iwl_rx_queue_restock.
+ * This is called as a scheduled work item (except for during initialization)
+ */
+void iwl_rx_replenish(struct iwl_trans *trans)
 {
 	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
 	unsigned long flags;
 
-	iwlagn_rx_allocate(trans, GFP_KERNEL);
+	iwl_rx_allocate(trans, GFP_KERNEL);
 
 	spin_lock_irqsave(&trans_pcie->irq_lock, flags);
-	iwlagn_rx_queue_restock(trans);
+	iwl_rx_queue_restock(trans);
 	spin_unlock_irqrestore(&trans_pcie->irq_lock, flags);
 }
 
-static void iwlagn_rx_replenish_now(struct iwl_trans *trans)
+static void iwl_rx_replenish_now(struct iwl_trans *trans)
 {
-	iwlagn_rx_allocate(trans, GFP_ATOMIC);
+	iwl_rx_allocate(trans, GFP_ATOMIC);
 
-	iwlagn_rx_queue_restock(trans);
+	iwl_rx_queue_restock(trans);
 }
 
 void iwl_bg_rx_replenish(struct work_struct *data)
@@ -352,7 +367,7 @@
 	struct iwl_trans_pcie *trans_pcie =
 	    container_of(data, struct iwl_trans_pcie, rx_replenish);
 
-	iwlagn_rx_replenish(trans_pcie->trans);
+	iwl_rx_replenish(trans_pcie->trans);
 }
 
 static void iwl_rx_handle_rxbuf(struct iwl_trans *trans,
@@ -530,7 +545,7 @@
 			count++;
 			if (count >= 8) {
 				rxq->read = i;
-				iwlagn_rx_replenish_now(trans);
+				iwl_rx_replenish_now(trans);
 				count = 0;
 			}
 		}
@@ -539,9 +554,9 @@
 	/* Backtrack one entry */
 	rxq->read = i;
 	if (fill_rx)
-		iwlagn_rx_replenish_now(trans);
+		iwl_rx_replenish_now(trans);
 	else
-		iwlagn_rx_queue_restock(trans);
+		iwl_rx_queue_restock(trans);
 }
 
 /**
@@ -723,11 +738,9 @@
 		/* Disable periodic interrupt; we use it as just a one-shot. */
 		iwl_write8(trans, CSR_INT_PERIODIC_REG,
 			    CSR_INT_PERIODIC_DIS);
-#ifdef CONFIG_IWLWIFI_IDI
-		iwl_amfh_rx_handler();
-#else
+
 		iwl_rx_handle(trans);
-#endif
+
 		/*
 		 * Enable periodic interrupt in 8 msec only if we received
 		 * real RX interrupt (instead of just periodic int), to catch
diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c
index 8488511..d0c0fe7 100644
--- a/drivers/net/wireless/iwlwifi/pcie/trans.c
+++ b/drivers/net/wireless/iwlwifi/pcie/trans.c
@@ -216,7 +216,7 @@
 	rxq->free_count = 0;
 	spin_unlock_irqrestore(&rxq->lock, flags);
 
-	iwlagn_rx_replenish(trans);
+	iwl_rx_replenish(trans);
 
 	iwl_trans_rx_hw_init(trans, rxq);
 
@@ -855,10 +855,8 @@
 
 	iwl_op_mode_nic_config(trans->op_mode);
 
-#ifndef CONFIG_IWLWIFI_IDI
 	/* Allocate the RX queue, or reset if it is already allocated */
 	iwl_rx_init(trans);
-#endif
 
 	/* Allocate or reset and init all Tx and Command queues */
 	if (iwl_tx_init(trans))
@@ -925,13 +923,10 @@
 /*
  * ucode
  */
-static int iwl_load_section(struct iwl_trans *trans, u8 section_num,
-			    const struct fw_desc *section)
+static int iwl_load_firmware_chunk(struct iwl_trans *trans, u32 dst_addr,
+				   dma_addr_t phy_addr, u32 byte_cnt)
 {
 	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-	dma_addr_t phy_addr = section->p_addr;
-	u32 byte_cnt = section->len;
-	u32 dst_addr = section->offset;
 	int ret;
 
 	trans_pcie->ucode_write_complete = false;
@@ -945,8 +940,8 @@
 			   dst_addr);
 
 	iwl_write_direct32(trans,
-		FH_TFDIB_CTRL0_REG(FH_SRVC_CHNL),
-		phy_addr & FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK);
+			   FH_TFDIB_CTRL0_REG(FH_SRVC_CHNL),
+			   phy_addr & FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK);
 
 	iwl_write_direct32(trans,
 			   FH_TFDIB_CTRL1_REG(FH_SRVC_CHNL),
@@ -965,33 +960,64 @@
 			   FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE	|
 			   FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD);
 
-	IWL_DEBUG_FW(trans, "[%d] uCode section being loaded...\n",
-		     section_num);
 	ret = wait_event_timeout(trans_pcie->ucode_write_waitq,
 				 trans_pcie->ucode_write_complete, 5 * HZ);
 	if (!ret) {
-		IWL_ERR(trans, "Could not load the [%d] uCode section\n",
-			section_num);
+		IWL_ERR(trans, "Failed to load firmware chunk!\n");
 		return -ETIMEDOUT;
 	}
 
 	return 0;
 }
 
+static int iwl_load_section(struct iwl_trans *trans, u8 section_num,
+			    const struct fw_desc *section)
+{
+	u8 *v_addr;
+	dma_addr_t p_addr;
+	u32 offset;
+	int ret = 0;
+
+	IWL_DEBUG_FW(trans, "[%d] uCode section being loaded...\n",
+		     section_num);
+
+	v_addr = dma_alloc_coherent(trans->dev, PAGE_SIZE, &p_addr, GFP_KERNEL);
+	if (!v_addr)
+		return -ENOMEM;
+
+	for (offset = 0; offset < section->len; offset += PAGE_SIZE) {
+		u32 copy_size;
+
+		copy_size = min_t(u32, PAGE_SIZE, section->len - offset);
+
+		memcpy(v_addr, (u8 *)section->data + offset, copy_size);
+		ret = iwl_load_firmware_chunk(trans, section->offset + offset,
+					      p_addr, copy_size);
+		if (ret) {
+			IWL_ERR(trans,
+				"Could not load the [%d] uCode section\n",
+				section_num);
+			break;
+		}
+	}
+
+	dma_free_coherent(trans->dev, PAGE_SIZE, v_addr, p_addr);
+	return ret;
+}
+
 static int iwl_load_given_ucode(struct iwl_trans *trans,
 				const struct fw_img *image)
 {
-	int ret = 0;
-		int i;
+	int i, ret = 0;
 
-		for (i = 0; i < IWL_UCODE_SECTION_MAX; i++) {
-			if (!image->sec[i].p_addr)
-				break;
+	for (i = 0; i < IWL_UCODE_SECTION_MAX; i++) {
+		if (!image->sec[i].data)
+			break;
 
-			ret = iwl_load_section(trans, i, &image->sec[i]);
-			if (ret)
-				return ret;
-		}
+		ret = iwl_load_section(trans, i, &image->sec[i]);
+		if (ret)
+			return ret;
+	}
 
 	/* Remove all resets to allow NIC to operate */
 	iwl_write32(trans, CSR_RESET, 0);
@@ -1184,9 +1210,8 @@
 	 */
 	if (test_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status)) {
 		iwl_trans_tx_stop(trans);
-#ifndef CONFIG_IWLWIFI_IDI
 		iwl_trans_rx_stop(trans);
-#endif
+
 		/* Power-down device's busmaster DMA clocks */
 		iwl_write_prph(trans, APMG_CLK_DIS_REG,
 			       APMG_CLK_VAL_DMA_CLK_RQT);
@@ -1456,14 +1481,16 @@
 	bool hw_rfkill;
 	unsigned long flags;
 
+	spin_lock_irqsave(&trans_pcie->irq_lock, flags);
+	iwl_disable_interrupts(trans);
+	spin_unlock_irqrestore(&trans_pcie->irq_lock, flags);
+
 	iwl_apm_stop(trans);
 
 	spin_lock_irqsave(&trans_pcie->irq_lock, flags);
 	iwl_disable_interrupts(trans);
 	spin_unlock_irqrestore(&trans_pcie->irq_lock, flags);
 
-	iwl_write32(trans, CSR_INT, 0xFFFFFFFF);
-
 	if (!op_mode_leaving) {
 		/*
 		 * Even if we stop the HW, we still want the RF kill
@@ -1551,9 +1578,8 @@
 	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
 
 	iwl_trans_pcie_tx_free(trans);
-#ifndef CONFIG_IWLWIFI_IDI
 	iwl_trans_pcie_rx_free(trans);
-#endif
+
 	if (trans_pcie->irq_requested == true) {
 		free_irq(trans_pcie->irq, trans);
 		iwl_free_isr_ict(trans);
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index 9d45b3b..429ca32 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -2056,7 +2056,7 @@
 	mac80211_hwsim_free();
 	return err;
 }
-
+module_init(init_mac80211_hwsim);
 
 static void __exit exit_mac80211_hwsim(void)
 {
@@ -2067,7 +2067,4 @@
 	mac80211_hwsim_free();
 	unregister_netdev(hwsim_mon);
 }
-
-
-module_init(init_mac80211_hwsim);
 module_exit(exit_mac80211_hwsim);
diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c
index b9f7b3e..c24824f 100644
--- a/drivers/net/wireless/mwifiex/cfg80211.c
+++ b/drivers/net/wireless/mwifiex/cfg80211.c
@@ -1502,6 +1502,12 @@
 
 	wiphy_dbg(wiphy, "info: received scan request on %s\n", dev->name);
 
+	if (atomic_read(&priv->wmm.tx_pkts_queued) >=
+	    MWIFIEX_MIN_TX_PENDING_TO_CANCEL_SCAN) {
+		dev_dbg(priv->adapter->dev, "scan rejected due to traffic\n");
+		return -EBUSY;
+	}
+
 	priv->scan_request = request;
 
 	priv->user_scan_cfg = kzalloc(sizeof(struct mwifiex_user_scan_cfg),
@@ -1630,7 +1636,7 @@
  *  create a new virtual interface with the given name
  */
 struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy,
-					      char *name,
+					      const char *name,
 					      enum nl80211_iftype type,
 					      u32 *flags,
 					      struct vif_params *params)
diff --git a/drivers/net/wireless/mwifiex/ie.c b/drivers/net/wireless/mwifiex/ie.c
index 1d8dd00..fa3a80f 100644
--- a/drivers/net/wireless/mwifiex/ie.c
+++ b/drivers/net/wireless/mwifiex/ie.c
@@ -160,7 +160,7 @@
 	u16 len;
 	int ret;
 
-	ap_custom_ie = kzalloc(sizeof(struct mwifiex_ie), GFP_KERNEL);
+	ap_custom_ie = kzalloc(sizeof(*ap_custom_ie), GFP_KERNEL);
 	if (!ap_custom_ie)
 		return -ENOMEM;
 
diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c
index 9c1549e..b2ba262 100644
--- a/drivers/net/wireless/mwifiex/init.c
+++ b/drivers/net/wireless/mwifiex/init.c
@@ -118,6 +118,7 @@
 
 			mwifiex_insert_cmd_to_pending_q(adapter, cmd_node,
 							true);
+			queue_work(adapter->workqueue, &adapter->main_work);
 			goto done;
 		}
 	} else {
diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h
index 12ceea4..90b64b01 100644
--- a/drivers/net/wireless/mwifiex/main.h
+++ b/drivers/net/wireless/mwifiex/main.h
@@ -91,6 +91,8 @@
 #define MWIFIEX_MAX_EMPTY_TX_Q_CNT			10
 #define MWIFIEX_SCAN_DELAY_MSEC				20
 
+#define MWIFIEX_MIN_TX_PENDING_TO_CANCEL_SCAN		2
+
 #define RSN_GTK_OUI_OFFSET				2
 
 #define MWIFIEX_OUI_NOT_PRESENT			0
@@ -1031,7 +1033,7 @@
 					struct mwifiex_bssdescriptor *bss_desc);
 
 struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy,
-					      char *name,
+					      const char *name,
 					      enum nl80211_iftype type,
 					      u32 *flags,
 					      struct vif_params *params);
diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c
index d7ad2d4..731562f 100644
--- a/drivers/net/wireless/mwifiex/sta_ioctl.c
+++ b/drivers/net/wireless/mwifiex/sta_ioctl.c
@@ -26,6 +26,9 @@
 #include "11n.h"
 #include "cfg80211.h"
 
+static int disconnect_on_suspend = 1;
+module_param(disconnect_on_suspend, int, 0644);
+
 /*
  * Copies the multicast address list from device to driver.
  *
@@ -448,6 +451,16 @@
 int mwifiex_enable_hs(struct mwifiex_adapter *adapter)
 {
 	struct mwifiex_ds_hs_cfg hscfg;
+	struct mwifiex_private *priv;
+	int i;
+
+	if (disconnect_on_suspend) {
+		for (i = 0; i < adapter->priv_num; i++) {
+			priv = adapter->priv[i];
+			if (priv)
+				mwifiex_deauthenticate(priv, NULL);
+		}
+	}
 
 	if (adapter->hs_activated) {
 		dev_dbg(adapter->dev, "cmd: HS Already actived\n");
diff --git a/drivers/net/wireless/p54/main.c b/drivers/net/wireless/p54/main.c
index 2969d53..aadda99 100644
--- a/drivers/net/wireless/p54/main.c
+++ b/drivers/net/wireless/p54/main.c
@@ -515,6 +515,17 @@
 	if (modparam_nohwcrypt)
 		return -EOPNOTSUPP;
 
+	if (key->flags & IEEE80211_KEY_FLAG_RX_MGMT) {
+		/*
+		 * Unfortunately most/all firmwares are trying to decrypt
+		 * incoming management frames if a suitable key can be found.
+		 * However, in doing so the data in these frames gets
+		 * corrupted. So, we can't have firmware supported crypto
+		 * offload in this case.
+		 */
+		return -EOPNOTSUPP;
+	}
+
 	mutex_lock(&priv->conf_mutex);
 	if (cmd == SET_KEY) {
 		switch (key->cipher) {
@@ -738,6 +749,7 @@
 		     IEEE80211_HW_SIGNAL_DBM |
 		     IEEE80211_HW_SUPPORTS_PS |
 		     IEEE80211_HW_PS_NULLFUNC_STACK |
+		     IEEE80211_HW_MFP_CAPABLE |
 		     IEEE80211_HW_REPORTS_TX_ACK_STATUS;
 
 	dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
diff --git a/drivers/net/wireless/rt2x00/rt2400pci.c b/drivers/net/wireless/rt2x00/rt2400pci.c
index 6458ab8..e3a2d90 100644
--- a/drivers/net/wireless/rt2x00/rt2400pci.c
+++ b/drivers/net/wireless/rt2x00/rt2400pci.c
@@ -1789,7 +1789,6 @@
 
 static const struct rt2x00_ops rt2400pci_ops = {
 	.name			= KBUILD_MODNAME,
-	.max_sta_intf		= 1,
 	.max_ap_intf		= 1,
 	.eeprom_size		= EEPROM_SIZE,
 	.rf_size		= RF_SIZE,
diff --git a/drivers/net/wireless/rt2x00/rt2500pci.c b/drivers/net/wireless/rt2x00/rt2500pci.c
index 68bca14..479d756 100644
--- a/drivers/net/wireless/rt2x00/rt2500pci.c
+++ b/drivers/net/wireless/rt2x00/rt2500pci.c
@@ -2081,7 +2081,6 @@
 
 static const struct rt2x00_ops rt2500pci_ops = {
 	.name			= KBUILD_MODNAME,
-	.max_sta_intf		= 1,
 	.max_ap_intf		= 1,
 	.eeprom_size		= EEPROM_SIZE,
 	.rf_size		= RF_SIZE,
diff --git a/drivers/net/wireless/rt2x00/rt2500usb.c b/drivers/net/wireless/rt2x00/rt2500usb.c
index f95b551..a12e84f 100644
--- a/drivers/net/wireless/rt2x00/rt2500usb.c
+++ b/drivers/net/wireless/rt2x00/rt2500usb.c
@@ -1896,7 +1896,6 @@
 
 static const struct rt2x00_ops rt2500usb_ops = {
 	.name			= KBUILD_MODNAME,
-	.max_sta_intf		= 1,
 	.max_ap_intf		= 1,
 	.eeprom_size		= EEPROM_SIZE,
 	.rf_size		= RF_SIZE,
diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c
index 9e09367..540c94f 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/rt2x00/rt2800lib.c
@@ -1763,36 +1763,15 @@
 
 	rt2800_rfcsr_read(rt2x00dev, 1, &rfcsr);
 	rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 0);
+	rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD,
+			  rt2x00dev->default_ant.rx_chain_num <= 1);
+	rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD,
+			  rt2x00dev->default_ant.rx_chain_num <= 2);
 	rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 0);
-	if (rt2x00_rt(rt2x00dev, RT3390)) {
-		rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD,
-				  rt2x00dev->default_ant.rx_chain_num == 1);
-		rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD,
-				  rt2x00dev->default_ant.tx_chain_num == 1);
-	} else {
-		rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 0);
-		rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 0);
-		rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 0);
-		rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 0);
-
-		switch (rt2x00dev->default_ant.tx_chain_num) {
-		case 1:
-			rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 1);
-			/* fall through */
-		case 2:
-			rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 1);
-			break;
-		}
-
-		switch (rt2x00dev->default_ant.rx_chain_num) {
-		case 1:
-			rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 1);
-			/* fall through */
-		case 2:
-			rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 1);
-			break;
-		}
-	}
+	rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD,
+			  rt2x00dev->default_ant.tx_chain_num <= 1);
+	rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD,
+			  rt2x00dev->default_ant.tx_chain_num <= 2);
 	rt2800_rfcsr_write(rt2x00dev, 1, rfcsr);
 
 	rt2800_rfcsr_read(rt2x00dev, 30, &rfcsr);
@@ -2896,23 +2875,32 @@
 
 static u8 rt2800_get_default_vgc(struct rt2x00_dev *rt2x00dev)
 {
+	u8 vgc;
+
 	if (rt2x00dev->curr_band == IEEE80211_BAND_2GHZ) {
 		if (rt2x00_rt(rt2x00dev, RT3070) ||
 		    rt2x00_rt(rt2x00dev, RT3071) ||
 		    rt2x00_rt(rt2x00dev, RT3090) ||
 		    rt2x00_rt(rt2x00dev, RT3290) ||
 		    rt2x00_rt(rt2x00dev, RT3390) ||
+		    rt2x00_rt(rt2x00dev, RT3572) ||
 		    rt2x00_rt(rt2x00dev, RT5390) ||
 		    rt2x00_rt(rt2x00dev, RT5392))
-			return 0x1c + (2 * rt2x00dev->lna_gain);
+			vgc = 0x1c + (2 * rt2x00dev->lna_gain);
 		else
-			return 0x2e + rt2x00dev->lna_gain;
+			vgc = 0x2e + rt2x00dev->lna_gain;
+	} else { /* 5GHZ band */
+		if (rt2x00_rt(rt2x00dev, RT3572))
+			vgc = 0x22 + (rt2x00dev->lna_gain * 5) / 3;
+		else {
+			if (!test_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags))
+				vgc = 0x32 + (rt2x00dev->lna_gain * 5) / 3;
+			else
+				vgc = 0x3a + (rt2x00dev->lna_gain * 5) / 3;
+		}
 	}
 
-	if (!test_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags))
-		return 0x32 + (rt2x00dev->lna_gain * 5) / 3;
-	else
-		return 0x3a + (rt2x00dev->lna_gain * 5) / 3;
+	return vgc;
 }
 
 static inline void rt2800_set_vgc(struct rt2x00_dev *rt2x00dev,
@@ -3081,7 +3069,7 @@
 		rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000400);
 		rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00080606);
 	} else if (rt2x00_rt(rt2x00dev, RT5390) ||
-			   rt2x00_rt(rt2x00dev, RT5392)) {
+		   rt2x00_rt(rt2x00dev, RT5392)) {
 		rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000404);
 		rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00080606);
 		rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x00000000);
@@ -3526,6 +3514,11 @@
 	} else if (rt2800_is_305x_soc(rt2x00dev)) {
 		rt2800_bbp_write(rt2x00dev, 78, 0x0e);
 		rt2800_bbp_write(rt2x00dev, 80, 0x08);
+	} else if (rt2x00_rt(rt2x00dev, RT3290)) {
+		rt2800_bbp_write(rt2x00dev, 74, 0x0b);
+		rt2800_bbp_write(rt2x00dev, 79, 0x18);
+		rt2800_bbp_write(rt2x00dev, 80, 0x09);
+		rt2800_bbp_write(rt2x00dev, 81, 0x33);
 	} else if (rt2x00_rt(rt2x00dev, RT3352)) {
 		rt2800_bbp_write(rt2x00dev, 78, 0x0e);
 		rt2800_bbp_write(rt2x00dev, 80, 0x08);
@@ -3534,13 +3527,6 @@
 		rt2800_bbp_write(rt2x00dev, 81, 0x37);
 	}
 
-	if (rt2x00_rt(rt2x00dev, RT3290)) {
-		rt2800_bbp_write(rt2x00dev, 74, 0x0b);
-		rt2800_bbp_write(rt2x00dev, 79, 0x18);
-		rt2800_bbp_write(rt2x00dev, 80, 0x09);
-		rt2800_bbp_write(rt2x00dev, 81, 0x33);
-	}
-
 	rt2800_bbp_write(rt2x00dev, 82, 0x62);
 	if (rt2x00_rt(rt2x00dev, RT3290) ||
 	    rt2x00_rt(rt2x00dev, RT5390) ||
diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/rt2x00/rt2800pci.c
index 391e08f..27829e1 100644
--- a/drivers/net/wireless/rt2x00/rt2800pci.c
+++ b/drivers/net/wireless/rt2x00/rt2800pci.c
@@ -1088,7 +1088,6 @@
 static const struct rt2x00_ops rt2800pci_ops = {
 	.name			= KBUILD_MODNAME,
 	.drv_data_size		= sizeof(struct rt2800_drv_data),
-	.max_sta_intf		= 1,
 	.max_ap_intf		= 8,
 	.eeprom_size		= EEPROM_SIZE,
 	.rf_size		= RF_SIZE,
diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c
index 603b65d..c9e9370 100644
--- a/drivers/net/wireless/rt2x00/rt2800usb.c
+++ b/drivers/net/wireless/rt2x00/rt2800usb.c
@@ -870,7 +870,6 @@
 static const struct rt2x00_ops rt2800usb_ops = {
 	.name			= KBUILD_MODNAME,
 	.drv_data_size		= sizeof(struct rt2800_drv_data),
-	.max_sta_intf		= 1,
 	.max_ap_intf		= 8,
 	.eeprom_size		= EEPROM_SIZE,
 	.rf_size		= RF_SIZE,
diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h
index 49375c8..0751b35 100644
--- a/drivers/net/wireless/rt2x00/rt2x00.h
+++ b/drivers/net/wireless/rt2x00/rt2x00.h
@@ -656,7 +656,6 @@
 struct rt2x00_ops {
 	const char *name;
 	const unsigned int drv_data_size;
-	const unsigned int max_sta_intf;
 	const unsigned int max_ap_intf;
 	const unsigned int eeprom_size;
 	const unsigned int rf_size;
@@ -742,6 +741,14 @@
 };
 
 /*
+ * Interface combinations
+ */
+enum {
+	IF_COMB_AP = 0,
+	NUM_IF_COMB,
+};
+
+/*
  * rt2x00 device structure.
  */
 struct rt2x00_dev {
@@ -868,6 +875,12 @@
 	unsigned int intf_beaconing;
 
 	/*
+	 * Interface combinations
+	 */
+	struct ieee80211_iface_limit if_limits_ap;
+	struct ieee80211_iface_combination if_combinations[NUM_IF_COMB];
+
+	/*
 	 * Link quality
 	 */
 	struct link link;
diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c
index 10cf672..69097d1 100644
--- a/drivers/net/wireless/rt2x00/rt2x00dev.c
+++ b/drivers/net/wireless/rt2x00/rt2x00dev.c
@@ -1118,6 +1118,34 @@
 	rt2x00dev->intf_associated = 0;
 }
 
+static inline void rt2x00lib_set_if_combinations(struct rt2x00_dev *rt2x00dev)
+{
+	struct ieee80211_iface_limit *if_limit;
+	struct ieee80211_iface_combination *if_combination;
+
+	/*
+	 * Build up AP interface limits structure.
+	 */
+	if_limit = &rt2x00dev->if_limits_ap;
+	if_limit->max = rt2x00dev->ops->max_ap_intf;
+	if_limit->types = BIT(NL80211_IFTYPE_AP);
+
+	/*
+	 * Build up AP interface combinations structure.
+	 */
+	if_combination = &rt2x00dev->if_combinations[IF_COMB_AP];
+	if_combination->limits = if_limit;
+	if_combination->n_limits = 1;
+	if_combination->max_interfaces = if_limit->max;
+	if_combination->num_different_channels = 1;
+
+	/*
+	 * Finally, specify the possible combinations to mac80211.
+	 */
+	rt2x00dev->hw->wiphy->iface_combinations = rt2x00dev->if_combinations;
+	rt2x00dev->hw->wiphy->n_iface_combinations = 1;
+}
+
 /*
  * driver allocation handlers.
  */
@@ -1126,6 +1154,11 @@
 	int retval = -ENOMEM;
 
 	/*
+	 * Set possible interface combinations.
+	 */
+	rt2x00lib_set_if_combinations(rt2x00dev);
+
+	/*
 	 * Allocate the driver data memory, if necessary.
 	 */
 	if (rt2x00dev->ops->drv_data_size > 0) {
diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c
index c3d0f2f..98a9e48 100644
--- a/drivers/net/wireless/rt2x00/rt2x00mac.c
+++ b/drivers/net/wireless/rt2x00/rt2x00mac.c
@@ -214,46 +214,6 @@
 	    !test_bit(DEVICE_STATE_STARTED, &rt2x00dev->flags))
 		return -ENODEV;
 
-	switch (vif->type) {
-	case NL80211_IFTYPE_AP:
-		/*
-		 * We don't support mixed combinations of
-		 * sta and ap interfaces.
-		 */
-		if (rt2x00dev->intf_sta_count)
-			return -ENOBUFS;
-
-		/*
-		 * Check if we exceeded the maximum amount
-		 * of supported interfaces.
-		 */
-		if (rt2x00dev->intf_ap_count >= rt2x00dev->ops->max_ap_intf)
-			return -ENOBUFS;
-
-		break;
-	case NL80211_IFTYPE_STATION:
-	case NL80211_IFTYPE_ADHOC:
-	case NL80211_IFTYPE_MESH_POINT:
-	case NL80211_IFTYPE_WDS:
-		/*
-		 * We don't support mixed combinations of
-		 * sta and ap interfaces.
-		 */
-		if (rt2x00dev->intf_ap_count)
-			return -ENOBUFS;
-
-		/*
-		 * Check if we exceeded the maximum amount
-		 * of supported interfaces.
-		 */
-		if (rt2x00dev->intf_sta_count >= rt2x00dev->ops->max_sta_intf)
-			return -ENOBUFS;
-
-		break;
-	default:
-		return -EINVAL;
-	}
-
 	/*
 	 * Loop through all beacon queues to find a free
 	 * entry. Since there are as much beacon entries
diff --git a/drivers/net/wireless/rt2x00/rt61pci.c b/drivers/net/wireless/rt2x00/rt61pci.c
index 2673e05..d6582a2 100644
--- a/drivers/net/wireless/rt2x00/rt61pci.c
+++ b/drivers/net/wireless/rt2x00/rt61pci.c
@@ -3045,7 +3045,6 @@
 
 static const struct rt2x00_ops rt61pci_ops = {
 	.name			= KBUILD_MODNAME,
-	.max_sta_intf		= 1,
 	.max_ap_intf		= 4,
 	.eeprom_size		= EEPROM_SIZE,
 	.rf_size		= RF_SIZE,
diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c
index cfa9f37..e5eb43b 100644
--- a/drivers/net/wireless/rt2x00/rt73usb.c
+++ b/drivers/net/wireless/rt2x00/rt73usb.c
@@ -2382,7 +2382,6 @@
 
 static const struct rt2x00_ops rt73usb_ops = {
 	.name			= KBUILD_MODNAME,
-	.max_sta_intf		= 1,
 	.max_ap_intf		= 4,
 	.eeprom_size		= EEPROM_SIZE,
 	.rf_size		= RF_SIZE,
diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig
index 3b20b73..ec85767 100644
--- a/drivers/nfc/Kconfig
+++ b/drivers/nfc/Kconfig
@@ -5,21 +5,9 @@
 menu "Near Field Communication (NFC) devices"
 	depends on NFC
 
-config PN544_NFC
-	tristate "PN544 NFC driver"
-	depends on I2C
-	select CRC_CCITT
-	default n
-	---help---
-	  Say yes if you want PN544 Near Field Communication driver.
-	  This is for i2c connected version. If unsure, say N here.
-
-	  To compile this driver as a module, choose m here. The module will
-	  be called pn544.
-
 config PN544_HCI_NFC
 	tristate "HCI PN544 NFC driver"
-	depends on I2C && NFC_SHDLC
+	depends on I2C && NFC_HCI && NFC_SHDLC
 	select CRC_CCITT
 	default n
 	---help---
diff --git a/drivers/nfc/Makefile b/drivers/nfc/Makefile
index 473e44ce..bf05831 100644
--- a/drivers/nfc/Makefile
+++ b/drivers/nfc/Makefile
@@ -2,7 +2,6 @@
 # Makefile for nfc devices
 #
 
-obj-$(CONFIG_PN544_NFC)		+= pn544.o
 obj-$(CONFIG_PN544_HCI_NFC)	+= pn544_hci.o
 obj-$(CONFIG_NFC_PN533)		+= pn533.o
 obj-$(CONFIG_NFC_WILINK)	+= nfcwilink.o
diff --git a/drivers/nfc/nfcwilink.c b/drivers/nfc/nfcwilink.c
index e7fd493..50b1ee4 100644
--- a/drivers/nfc/nfcwilink.c
+++ b/drivers/nfc/nfcwilink.c
@@ -352,8 +352,6 @@
 	struct nfcwilink *drv = priv_data;
 	int rc;
 
-	nfc_dev_dbg(&drv->pdev->dev, "receive entry, len %d", skb->len);
-
 	if (!skb)
 		return -EFAULT;
 
@@ -362,6 +360,8 @@
 		return -EFAULT;
 	}
 
+	nfc_dev_dbg(&drv->pdev->dev, "receive entry, len %d", skb->len);
+
 	/* strip the ST header
 	(apart for the chnl byte, which is not received in the hdr) */
 	skb_pull(skb, (NFCWILINK_HDR_LEN-1));
@@ -604,21 +604,7 @@
 	},
 };
 
-/* ------- Module Init/Exit interfaces ------ */
-static int __init nfcwilink_init(void)
-{
-	printk(KERN_INFO "NFC Driver for TI WiLink");
-
-	return platform_driver_register(&nfcwilink_driver);
-}
-
-static void __exit nfcwilink_exit(void)
-{
-	platform_driver_unregister(&nfcwilink_driver);
-}
-
-module_init(nfcwilink_init);
-module_exit(nfcwilink_exit);
+module_platform_driver(nfcwilink_driver);
 
 /* ------ Module Info ------ */
 
diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c
index d606f52..97c440a 100644
--- a/drivers/nfc/pn533.c
+++ b/drivers/nfc/pn533.c
@@ -356,6 +356,7 @@
 
 	struct workqueue_struct	*wq;
 	struct work_struct cmd_work;
+	struct work_struct cmd_complete_work;
 	struct work_struct poll_work;
 	struct work_struct mi_work;
 	struct work_struct tg_work;
@@ -383,6 +384,19 @@
 	u8 tgt_mode;
 
 	u32 device_type;
+
+	struct list_head cmd_queue;
+	u8 cmd_pending;
+};
+
+struct pn533_cmd {
+	struct list_head queue;
+	struct pn533_frame *out_frame;
+	struct pn533_frame *in_frame;
+	int in_frame_len;
+	pn533_cmd_complete_t cmd_complete;
+	void *arg;
+	gfp_t flags;
 };
 
 struct pn533_frame {
@@ -487,7 +501,7 @@
 
 static void pn533_wq_cmd_complete(struct work_struct *work)
 {
-	struct pn533 *dev = container_of(work, struct pn533, cmd_work);
+	struct pn533 *dev = container_of(work, struct pn533, cmd_complete_work);
 	struct pn533_frame *in_frame;
 	int rc;
 
@@ -502,7 +516,7 @@
 					PN533_FRAME_CMD_PARAMS_LEN(in_frame));
 
 	if (rc != -EINPROGRESS)
-		mutex_unlock(&dev->cmd_lock);
+		queue_work(dev->wq, &dev->cmd_work);
 }
 
 static void pn533_recv_response(struct urb *urb)
@@ -550,7 +564,7 @@
 	dev->wq_in_frame = in_frame;
 
 sched_wq:
-	queue_work(dev->wq, &dev->cmd_work);
+	queue_work(dev->wq, &dev->cmd_complete_work);
 }
 
 static int pn533_submit_urb_for_response(struct pn533 *dev, gfp_t flags)
@@ -606,7 +620,7 @@
 
 sched_wq:
 	dev->wq_in_frame = NULL;
-	queue_work(dev->wq, &dev->cmd_work);
+	queue_work(dev->wq, &dev->cmd_complete_work);
 }
 
 static int pn533_submit_urb_for_ack(struct pn533 *dev, gfp_t flags)
@@ -669,6 +683,31 @@
 	return rc;
 }
 
+static void pn533_wq_cmd(struct work_struct *work)
+{
+	struct pn533 *dev = container_of(work, struct pn533, cmd_work);
+	struct pn533_cmd *cmd;
+
+	mutex_lock(&dev->cmd_lock);
+
+	if (list_empty(&dev->cmd_queue)) {
+		dev->cmd_pending = 0;
+		mutex_unlock(&dev->cmd_lock);
+		return;
+	}
+
+	cmd = list_first_entry(&dev->cmd_queue, struct pn533_cmd, queue);
+
+	mutex_unlock(&dev->cmd_lock);
+
+	__pn533_send_cmd_frame_async(dev, cmd->out_frame, cmd->in_frame,
+				     cmd->in_frame_len, cmd->cmd_complete,
+				     cmd->arg, cmd->flags);
+
+	list_del(&cmd->queue);
+	kfree(cmd);
+}
+
 static int pn533_send_cmd_frame_async(struct pn533 *dev,
 					struct pn533_frame *out_frame,
 					struct pn533_frame *in_frame,
@@ -676,21 +715,44 @@
 					pn533_cmd_complete_t cmd_complete,
 					void *arg, gfp_t flags)
 {
-	int rc;
+	struct pn533_cmd *cmd;
+	int rc = 0;
 
 	nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
 
-	if (!mutex_trylock(&dev->cmd_lock))
-		return -EBUSY;
+	mutex_lock(&dev->cmd_lock);
 
-	rc = __pn533_send_cmd_frame_async(dev, out_frame, in_frame,
-					in_frame_len, cmd_complete, arg, flags);
-	if (rc)
-		goto error;
+	if (!dev->cmd_pending) {
+		rc = __pn533_send_cmd_frame_async(dev, out_frame, in_frame,
+						  in_frame_len, cmd_complete,
+						  arg, flags);
+		if (!rc)
+			dev->cmd_pending = 1;
 
-	return 0;
-error:
+		goto unlock;
+	}
+
+	nfc_dev_dbg(&dev->interface->dev, "%s Queueing command", __func__);
+
+	cmd = kzalloc(sizeof(struct pn533_cmd), flags);
+	if (!cmd) {
+		rc = -ENOMEM;
+		goto unlock;
+	}
+
+	INIT_LIST_HEAD(&cmd->queue);
+	cmd->out_frame = out_frame;
+	cmd->in_frame = in_frame;
+	cmd->in_frame_len = in_frame_len;
+	cmd->cmd_complete = cmd_complete;
+	cmd->arg = arg;
+	cmd->flags = flags;
+
+	list_add_tail(&cmd->queue, &dev->cmd_queue);
+
+unlock:
 	mutex_unlock(&dev->cmd_lock);
+
 	return rc;
 }
 
@@ -1305,8 +1367,6 @@
 
 	dev->cancel_listen = 1;
 
-	mutex_unlock(&dev->cmd_lock);
-
 	pn533_poll_next_mod(dev);
 
 	queue_work(dev->wq, &dev->poll_work);
@@ -2131,7 +2191,7 @@
 
 	kfree(arg);
 
-	mutex_unlock(&dev->cmd_lock);
+	queue_work(dev->wq, &dev->cmd_work);
 }
 
 static int pn533_set_configuration(struct pn533 *dev, u8 cfgitem, u8 *cfgdata,
@@ -2330,13 +2390,12 @@
 			NULL, 0,
 			pn533_send_complete, dev);
 
-	INIT_WORK(&dev->cmd_work, pn533_wq_cmd_complete);
+	INIT_WORK(&dev->cmd_work, pn533_wq_cmd);
+	INIT_WORK(&dev->cmd_complete_work, pn533_wq_cmd_complete);
 	INIT_WORK(&dev->mi_work, pn533_wq_mi_recv);
 	INIT_WORK(&dev->tg_work, pn533_wq_tg_get_data);
 	INIT_WORK(&dev->poll_work, pn533_wq_poll);
-	dev->wq = alloc_workqueue("pn533",
-				  WQ_NON_REENTRANT | WQ_UNBOUND | WQ_MEM_RECLAIM,
-				  1);
+	dev->wq = alloc_ordered_workqueue("pn533", 0);
 	if (dev->wq == NULL)
 		goto error;
 
@@ -2346,6 +2405,8 @@
 
 	skb_queue_head_init(&dev->resp_q);
 
+	INIT_LIST_HEAD(&dev->cmd_queue);
+
 	usb_set_intfdata(interface, dev);
 
 	pn533_tx_frame_init(dev->out_frame, PN533_CMD_GET_FIRMWARE_VERSION);
@@ -2417,6 +2478,7 @@
 static void pn533_disconnect(struct usb_interface *interface)
 {
 	struct pn533 *dev;
+	struct pn533_cmd *cmd, *n;
 
 	dev = usb_get_intfdata(interface);
 	usb_set_intfdata(interface, NULL);
@@ -2433,6 +2495,11 @@
 
 	del_timer(&dev->listen_timer);
 
+	list_for_each_entry_safe(cmd, n, &dev->cmd_queue, queue) {
+		list_del(&cmd->queue);
+		kfree(cmd);
+	}
+
 	kfree(dev->in_frame);
 	usb_free_urb(dev->in_urb);
 	kfree(dev->out_frame);
diff --git a/drivers/nfc/pn544.c b/drivers/nfc/pn544.c
deleted file mode 100644
index 724f65d..0000000
--- a/drivers/nfc/pn544.c
+++ /dev/null
@@ -1,893 +0,0 @@
-/*
- * Driver for the PN544 NFC chip.
- *
- * Copyright (C) Nokia Corporation
- *
- * Author: Jari Vanhala <ext-jari.vanhala@nokia.com>
- * Contact: Matti Aaltonen <matti.j.aaltonen@nokia.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#include <linux/completion.h>
-#include <linux/crc-ccitt.h>
-#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/miscdevice.h>
-#include <linux/module.h>
-#include <linux/mutex.h>
-#include <linux/nfc/pn544.h>
-#include <linux/poll.h>
-#include <linux/regulator/consumer.h>
-#include <linux/serial_core.h> /* for TCGETS */
-#include <linux/slab.h>
-
-#define DRIVER_CARD	"PN544 NFC"
-#define DRIVER_DESC	"NFC driver for PN544"
-
-static struct i2c_device_id pn544_id_table[] = {
-	{ PN544_DRIVER_NAME, 0 },
-	{ }
-};
-MODULE_DEVICE_TABLE(i2c, pn544_id_table);
-
-#define HCI_MODE	0
-#define FW_MODE		1
-
-enum pn544_state {
-	PN544_ST_COLD,
-	PN544_ST_FW_READY,
-	PN544_ST_READY,
-};
-
-enum pn544_irq {
-	PN544_NONE,
-	PN544_INT,
-};
-
-struct pn544_info {
-	struct miscdevice miscdev;
-	struct i2c_client *i2c_dev;
-	struct regulator_bulk_data regs[3];
-
-	enum pn544_state state;
-	wait_queue_head_t read_wait;
-	loff_t read_offset;
-	enum pn544_irq read_irq;
-	struct mutex read_mutex; /* Serialize read_irq access */
-	struct mutex mutex; /* Serialize info struct access */
-	u8 *buf;
-	size_t buflen;
-};
-
-static const char reg_vdd_io[]	= "Vdd_IO";
-static const char reg_vbat[]	= "VBat";
-static const char reg_vsim[]	= "VSim";
-
-/* sysfs interface */
-static ssize_t pn544_test(struct device *dev,
-			  struct device_attribute *attr, char *buf)
-{
-	struct pn544_info *info = dev_get_drvdata(dev);
-	struct i2c_client *client = info->i2c_dev;
-	struct pn544_nfc_platform_data *pdata = client->dev.platform_data;
-
-	return snprintf(buf, PAGE_SIZE, "%d\n", pdata->test());
-}
-
-static int pn544_enable(struct pn544_info *info, int mode)
-{
-	struct pn544_nfc_platform_data *pdata;
-	struct i2c_client *client = info->i2c_dev;
-
-	int r;
-
-	r = regulator_bulk_enable(ARRAY_SIZE(info->regs), info->regs);
-	if (r < 0)
-		return r;
-
-	pdata = client->dev.platform_data;
-	info->read_irq = PN544_NONE;
-	if (pdata->enable)
-		pdata->enable(mode);
-
-	if (mode) {
-		info->state = PN544_ST_FW_READY;
-		dev_dbg(&client->dev, "now in FW-mode\n");
-	} else {
-		info->state = PN544_ST_READY;
-		dev_dbg(&client->dev, "now in HCI-mode\n");
-	}
-
-	usleep_range(10000, 15000);
-
-	return 0;
-}
-
-static void pn544_disable(struct pn544_info *info)
-{
-	struct pn544_nfc_platform_data *pdata;
-	struct i2c_client *client = info->i2c_dev;
-
-	pdata = client->dev.platform_data;
-	if (pdata->disable)
-		pdata->disable();
-
-	info->state = PN544_ST_COLD;
-
-	dev_dbg(&client->dev, "Now in OFF-mode\n");
-
-	msleep(PN544_RESETVEN_TIME);
-
-	info->read_irq = PN544_NONE;
-	regulator_bulk_disable(ARRAY_SIZE(info->regs), info->regs);
-}
-
-static int check_crc(u8 *buf, int buflen)
-{
-	u8 len;
-	u16 crc;
-
-	len = buf[0] + 1;
-	if (len < 4 || len != buflen || len > PN544_MSG_MAX_SIZE) {
-		pr_err(PN544_DRIVER_NAME
-		       ": CRC; corrupt packet len %u (%d)\n", len, buflen);
-		print_hex_dump(KERN_DEBUG, "crc: ", DUMP_PREFIX_NONE,
-			       16, 2, buf, buflen, false);
-		return -EPERM;
-	}
-	crc = crc_ccitt(0xffff, buf, len - 2);
-	crc = ~crc;
-
-	if (buf[len-2] != (crc & 0xff) || buf[len-1] != (crc >> 8)) {
-		pr_err(PN544_DRIVER_NAME ": CRC error 0x%x != 0x%x 0x%x\n",
-		       crc, buf[len-1], buf[len-2]);
-
-		print_hex_dump(KERN_DEBUG, "crc: ", DUMP_PREFIX_NONE,
-			       16, 2, buf, buflen, false);
-		return -EPERM;
-	}
-	return 0;
-}
-
-static int pn544_i2c_write(struct i2c_client *client, u8 *buf, int len)
-{
-	int r;
-
-	if (len < 4 || len != (buf[0] + 1)) {
-		dev_err(&client->dev, "%s: Illegal message length: %d\n",
-			__func__, len);
-		return -EINVAL;
-	}
-
-	if (check_crc(buf, len))
-		return -EINVAL;
-
-	usleep_range(3000, 6000);
-
-	r = i2c_master_send(client, buf, len);
-	dev_dbg(&client->dev, "send: %d\n", r);
-
-	if (r == -EREMOTEIO) { /* Retry, chip was in standby */
-		usleep_range(6000, 10000);
-		r = i2c_master_send(client, buf, len);
-		dev_dbg(&client->dev, "send2: %d\n", r);
-	}
-
-	if (r != len)
-		return -EREMOTEIO;
-
-	return r;
-}
-
-static int pn544_i2c_read(struct i2c_client *client, u8 *buf, int buflen)
-{
-	int r;
-	u8 len;
-
-	/*
-	 * You could read a packet in one go, but then you'd need to read
-	 * max size and rest would be 0xff fill, so we do split reads.
-	 */
-	r = i2c_master_recv(client, &len, 1);
-	dev_dbg(&client->dev, "recv1: %d\n", r);
-
-	if (r != 1)
-		return -EREMOTEIO;
-
-	if (len < PN544_LLC_HCI_OVERHEAD)
-		len = PN544_LLC_HCI_OVERHEAD;
-	else if (len > (PN544_MSG_MAX_SIZE - 1))
-		len = PN544_MSG_MAX_SIZE - 1;
-
-	if (1 + len > buflen) /* len+(data+crc16) */
-		return -EMSGSIZE;
-
-	buf[0] = len;
-
-	r = i2c_master_recv(client, buf + 1, len);
-	dev_dbg(&client->dev, "recv2: %d\n", r);
-
-	if (r != len)
-		return -EREMOTEIO;
-
-	usleep_range(3000, 6000);
-
-	return r + 1;
-}
-
-static int pn544_fw_write(struct i2c_client *client, u8 *buf, int len)
-{
-	int r;
-
-	dev_dbg(&client->dev, "%s\n", __func__);
-
-	if (len < PN544_FW_HEADER_SIZE ||
-	    (PN544_FW_HEADER_SIZE + (buf[1] << 8) + buf[2]) != len)
-		return -EINVAL;
-
-	r = i2c_master_send(client, buf, len);
-	dev_dbg(&client->dev, "fw send: %d\n", r);
-
-	if (r == -EREMOTEIO) { /* Retry, chip was in standby */
-		usleep_range(6000, 10000);
-		r = i2c_master_send(client, buf, len);
-		dev_dbg(&client->dev, "fw send2: %d\n", r);
-	}
-
-	if (r != len)
-		return -EREMOTEIO;
-
-	return r;
-}
-
-static int pn544_fw_read(struct i2c_client *client, u8 *buf, int buflen)
-{
-	int r, len;
-
-	if (buflen < PN544_FW_HEADER_SIZE)
-		return -EINVAL;
-
-	r = i2c_master_recv(client, buf, PN544_FW_HEADER_SIZE);
-	dev_dbg(&client->dev, "FW recv1: %d\n", r);
-
-	if (r < 0)
-		return r;
-
-	if (r < PN544_FW_HEADER_SIZE)
-		return -EINVAL;
-
-	len = (buf[1] << 8) + buf[2];
-	if (len == 0) /* just header, no additional data */
-		return r;
-
-	if (len > buflen - PN544_FW_HEADER_SIZE)
-		return -EMSGSIZE;
-
-	r = i2c_master_recv(client, buf + PN544_FW_HEADER_SIZE, len);
-	dev_dbg(&client->dev, "fw recv2: %d\n", r);
-
-	if (r != len)
-		return -EINVAL;
-
-	return r + PN544_FW_HEADER_SIZE;
-}
-
-static irqreturn_t pn544_irq_thread_fn(int irq, void *dev_id)
-{
-	struct pn544_info *info = dev_id;
-	struct i2c_client *client = info->i2c_dev;
-
-	BUG_ON(!info);
-	BUG_ON(irq != info->i2c_dev->irq);
-
-	dev_dbg(&client->dev, "IRQ\n");
-
-	mutex_lock(&info->read_mutex);
-	info->read_irq = PN544_INT;
-	mutex_unlock(&info->read_mutex);
-
-	wake_up_interruptible(&info->read_wait);
-
-	return IRQ_HANDLED;
-}
-
-static enum pn544_irq pn544_irq_state(struct pn544_info *info)
-{
-	enum pn544_irq irq;
-
-	mutex_lock(&info->read_mutex);
-	irq = info->read_irq;
-	mutex_unlock(&info->read_mutex);
-	/*
-	 * XXX: should we check GPIO-line status directly?
-	 * return pdata->irq_status() ? PN544_INT : PN544_NONE;
-	 */
-
-	return irq;
-}
-
-static ssize_t pn544_read(struct file *file, char __user *buf,
-			  size_t count, loff_t *offset)
-{
-	struct pn544_info *info = container_of(file->private_data,
-					       struct pn544_info, miscdev);
-	struct i2c_client *client = info->i2c_dev;
-	enum pn544_irq irq;
-	size_t len;
-	int r = 0;
-
-	dev_dbg(&client->dev, "%s: info: %p, count: %zu\n", __func__,
-		info, count);
-
-	mutex_lock(&info->mutex);
-
-	if (info->state == PN544_ST_COLD) {
-		r = -ENODEV;
-		goto out;
-	}
-
-	irq = pn544_irq_state(info);
-	if (irq == PN544_NONE) {
-		if (file->f_flags & O_NONBLOCK) {
-			r = -EAGAIN;
-			goto out;
-		}
-
-		if (wait_event_interruptible(info->read_wait,
-					     (info->read_irq == PN544_INT))) {
-			r = -ERESTARTSYS;
-			goto out;
-		}
-	}
-
-	if (info->state == PN544_ST_FW_READY) {
-		len = min(count, info->buflen);
-
-		mutex_lock(&info->read_mutex);
-		r = pn544_fw_read(info->i2c_dev, info->buf, len);
-		info->read_irq = PN544_NONE;
-		mutex_unlock(&info->read_mutex);
-
-		if (r < 0) {
-			dev_err(&info->i2c_dev->dev, "FW read failed: %d\n", r);
-			goto out;
-		}
-
-		print_hex_dump(KERN_DEBUG, "FW read: ", DUMP_PREFIX_NONE,
-			       16, 2, info->buf, r, false);
-
-		*offset += r;
-		if (copy_to_user(buf, info->buf, r)) {
-			r = -EFAULT;
-			goto out;
-		}
-	} else {
-		len = min(count, info->buflen);
-
-		mutex_lock(&info->read_mutex);
-		r = pn544_i2c_read(info->i2c_dev, info->buf, len);
-		info->read_irq = PN544_NONE;
-		mutex_unlock(&info->read_mutex);
-
-		if (r < 0) {
-			dev_err(&info->i2c_dev->dev, "read failed (%d)\n", r);
-			goto out;
-		}
-		print_hex_dump(KERN_DEBUG, "read: ", DUMP_PREFIX_NONE,
-			       16, 2, info->buf, r, false);
-
-		*offset += r;
-		if (copy_to_user(buf, info->buf, r)) {
-			r = -EFAULT;
-			goto out;
-		}
-	}
-
-out:
-	mutex_unlock(&info->mutex);
-
-	return r;
-}
-
-static unsigned int pn544_poll(struct file *file, poll_table *wait)
-{
-	struct pn544_info *info = container_of(file->private_data,
-					       struct pn544_info, miscdev);
-	struct i2c_client *client = info->i2c_dev;
-	int r = 0;
-
-	dev_dbg(&client->dev, "%s: info: %p\n", __func__, info);
-
-	mutex_lock(&info->mutex);
-
-	if (info->state == PN544_ST_COLD) {
-		r = -ENODEV;
-		goto out;
-	}
-
-	poll_wait(file, &info->read_wait, wait);
-
-	if (pn544_irq_state(info) == PN544_INT) {
-		r = POLLIN | POLLRDNORM;
-		goto out;
-	}
-out:
-	mutex_unlock(&info->mutex);
-
-	return r;
-}
-
-static ssize_t pn544_write(struct file *file, const char __user *buf,
-			   size_t count, loff_t *ppos)
-{
-	struct pn544_info *info = container_of(file->private_data,
-					       struct pn544_info, miscdev);
-	struct i2c_client *client = info->i2c_dev;
-	ssize_t	len;
-	int r;
-
-	dev_dbg(&client->dev, "%s: info: %p, count %zu\n", __func__,
-		info, count);
-
-	mutex_lock(&info->mutex);
-
-	if (info->state == PN544_ST_COLD) {
-		r = -ENODEV;
-		goto out;
-	}
-
-	/*
-	 * XXX: should we detect rset-writes and clean possible
-	 * read_irq state
-	 */
-	if (info->state == PN544_ST_FW_READY) {
-		size_t fw_len;
-
-		if (count < PN544_FW_HEADER_SIZE) {
-			r = -EINVAL;
-			goto out;
-		}
-
-		len = min(count, info->buflen);
-		if (copy_from_user(info->buf, buf, len)) {
-			r = -EFAULT;
-			goto out;
-		}
-
-		print_hex_dump(KERN_DEBUG, "FW write: ", DUMP_PREFIX_NONE,
-			       16, 2, info->buf, len, false);
-
-		fw_len = PN544_FW_HEADER_SIZE + (info->buf[1] << 8) +
-			info->buf[2];
-
-		if (len > fw_len) /* 1 msg at a time */
-			len = fw_len;
-
-		r = pn544_fw_write(info->i2c_dev, info->buf, len);
-	} else {
-		if (count < PN544_LLC_MIN_SIZE) {
-			r = -EINVAL;
-			goto out;
-		}
-
-		len = min(count, info->buflen);
-		if (copy_from_user(info->buf, buf, len)) {
-			r = -EFAULT;
-			goto out;
-		}
-
-		print_hex_dump(KERN_DEBUG, "write: ", DUMP_PREFIX_NONE,
-			       16, 2, info->buf, len, false);
-
-		if (len > (info->buf[0] + 1)) /* 1 msg at a time */
-			len  = info->buf[0] + 1;
-
-		r = pn544_i2c_write(info->i2c_dev, info->buf, len);
-	}
-out:
-	mutex_unlock(&info->mutex);
-
-	return r;
-
-}
-
-static long pn544_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
-	struct pn544_info *info = container_of(file->private_data,
-					       struct pn544_info, miscdev);
-	struct i2c_client *client = info->i2c_dev;
-	struct pn544_nfc_platform_data *pdata;
-	unsigned int val;
-	int r = 0;
-
-	dev_dbg(&client->dev, "%s: info: %p, cmd: 0x%x\n", __func__, info, cmd);
-
-	mutex_lock(&info->mutex);
-
-	if (info->state == PN544_ST_COLD) {
-		r = -ENODEV;
-		goto out;
-	}
-
-	pdata = info->i2c_dev->dev.platform_data;
-	switch (cmd) {
-	case PN544_GET_FW_MODE:
-		dev_dbg(&client->dev, "%s:  PN544_GET_FW_MODE\n", __func__);
-
-		val = (info->state == PN544_ST_FW_READY);
-		if (copy_to_user((void __user *)arg, &val, sizeof(val))) {
-			r = -EFAULT;
-			goto out;
-		}
-
-		break;
-
-	case PN544_SET_FW_MODE:
-		dev_dbg(&client->dev, "%s:  PN544_SET_FW_MODE\n", __func__);
-
-		if (copy_from_user(&val, (void __user *)arg, sizeof(val))) {
-			r = -EFAULT;
-			goto out;
-		}
-
-		if (val) {
-			if (info->state == PN544_ST_FW_READY)
-				break;
-
-			pn544_disable(info);
-			r = pn544_enable(info, FW_MODE);
-			if (r < 0)
-				goto out;
-		} else {
-			if (info->state == PN544_ST_READY)
-				break;
-			pn544_disable(info);
-			r = pn544_enable(info, HCI_MODE);
-			if (r < 0)
-				goto out;
-		}
-		file->f_pos = info->read_offset;
-		break;
-
-	case TCGETS:
-		dev_dbg(&client->dev, "%s:  TCGETS\n", __func__);
-
-		r = -ENOIOCTLCMD;
-		break;
-
-	default:
-		dev_err(&client->dev, "Unknown ioctl 0x%x\n", cmd);
-		r = -ENOIOCTLCMD;
-		break;
-	}
-
-out:
-	mutex_unlock(&info->mutex);
-
-	return r;
-}
-
-static int pn544_open(struct inode *inode, struct file *file)
-{
-	struct pn544_info *info = container_of(file->private_data,
-					       struct pn544_info, miscdev);
-	struct i2c_client *client = info->i2c_dev;
-	int r = 0;
-
-	dev_dbg(&client->dev, "%s: info: %p, client %p\n", __func__,
-		info, info->i2c_dev);
-
-	mutex_lock(&info->mutex);
-
-	/*
-	 * Only 1 at a time.
-	 * XXX: maybe user (counter) would work better
-	 */
-	if (info->state != PN544_ST_COLD) {
-		r = -EBUSY;
-		goto out;
-	}
-
-	file->f_pos = info->read_offset;
-	r = pn544_enable(info, HCI_MODE);
-
-out:
-	mutex_unlock(&info->mutex);
-	return r;
-}
-
-static int pn544_close(struct inode *inode, struct file *file)
-{
-	struct pn544_info *info = container_of(file->private_data,
-					       struct pn544_info, miscdev);
-	struct i2c_client *client = info->i2c_dev;
-
-	dev_dbg(&client->dev, "%s: info: %p, client %p\n",
-		__func__, info, info->i2c_dev);
-
-	mutex_lock(&info->mutex);
-	pn544_disable(info);
-	mutex_unlock(&info->mutex);
-
-	return 0;
-}
-
-static const struct file_operations pn544_fops = {
-	.owner		= THIS_MODULE,
-	.llseek		= no_llseek,
-	.read		= pn544_read,
-	.write		= pn544_write,
-	.poll		= pn544_poll,
-	.open		= pn544_open,
-	.release	= pn544_close,
-	.unlocked_ioctl	= pn544_ioctl,
-};
-
-#ifdef CONFIG_PM
-static int pn544_suspend(struct device *dev)
-{
-	struct i2c_client *client = to_i2c_client(dev);
-	struct pn544_info *info;
-	int r = 0;
-
-	dev_info(&client->dev, "***\n%s: client %p\n***\n", __func__, client);
-
-	info = i2c_get_clientdata(client);
-	dev_info(&client->dev, "%s: info: %p, client %p\n", __func__,
-		 info, client);
-
-	mutex_lock(&info->mutex);
-
-	switch (info->state) {
-	case PN544_ST_FW_READY:
-		/* Do not suspend while upgrading FW, please! */
-		r = -EPERM;
-		break;
-
-	case PN544_ST_READY:
-		/*
-		 * CHECK: Device should be in standby-mode. No way to check?
-		 * Allowing low power mode for the regulator is potentially
-		 * dangerous if pn544 does not go to suspension.
-		 */
-		break;
-
-	case PN544_ST_COLD:
-		break;
-	};
-
-	mutex_unlock(&info->mutex);
-	return r;
-}
-
-static int pn544_resume(struct device *dev)
-{
-	struct i2c_client *client = to_i2c_client(dev);
-	struct pn544_info *info = i2c_get_clientdata(client);
-	int r = 0;
-
-	dev_dbg(&client->dev, "%s: info: %p, client %p\n", __func__,
-		info, client);
-
-	mutex_lock(&info->mutex);
-
-	switch (info->state) {
-	case PN544_ST_READY:
-		/*
-		 * CHECK: If regulator low power mode is allowed in
-		 * pn544_suspend, we should go back to normal mode
-		 * here.
-		 */
-		break;
-
-	case PN544_ST_COLD:
-		break;
-
-	case PN544_ST_FW_READY:
-		break;
-	};
-
-	mutex_unlock(&info->mutex);
-
-	return r;
-}
-
-static SIMPLE_DEV_PM_OPS(pn544_pm_ops, pn544_suspend, pn544_resume);
-#endif
-
-static struct device_attribute pn544_attr =
-	__ATTR(nfc_test, S_IRUGO, pn544_test, NULL);
-
-static int __devinit pn544_probe(struct i2c_client *client,
-				 const struct i2c_device_id *id)
-{
-	struct pn544_info *info;
-	struct pn544_nfc_platform_data *pdata;
-	int r = 0;
-
-	dev_dbg(&client->dev, "%s\n", __func__);
-	dev_dbg(&client->dev, "IRQ: %d\n", client->irq);
-
-	/* private data allocation */
-	info = kzalloc(sizeof(struct pn544_info), GFP_KERNEL);
-	if (!info) {
-		dev_err(&client->dev,
-			"Cannot allocate memory for pn544_info.\n");
-		r = -ENOMEM;
-		goto err_info_alloc;
-	}
-
-	info->buflen = max(PN544_MSG_MAX_SIZE, PN544_MAX_I2C_TRANSFER);
-	info->buf = kzalloc(info->buflen, GFP_KERNEL);
-	if (!info->buf) {
-		dev_err(&client->dev,
-			"Cannot allocate memory for pn544_info->buf.\n");
-		r = -ENOMEM;
-		goto err_buf_alloc;
-	}
-
-	info->regs[0].supply = reg_vdd_io;
-	info->regs[1].supply = reg_vbat;
-	info->regs[2].supply = reg_vsim;
-	r = regulator_bulk_get(&client->dev, ARRAY_SIZE(info->regs),
-				 info->regs);
-	if (r < 0)
-		goto err_kmalloc;
-
-	info->i2c_dev = client;
-	info->state = PN544_ST_COLD;
-	info->read_irq = PN544_NONE;
-	mutex_init(&info->read_mutex);
-	mutex_init(&info->mutex);
-	init_waitqueue_head(&info->read_wait);
-	i2c_set_clientdata(client, info);
-	pdata = client->dev.platform_data;
-	if (!pdata) {
-		dev_err(&client->dev, "No platform data\n");
-		r = -EINVAL;
-		goto err_reg;
-	}
-
-	if (!pdata->request_resources) {
-		dev_err(&client->dev, "request_resources() missing\n");
-		r = -EINVAL;
-		goto err_reg;
-	}
-
-	r = pdata->request_resources(client);
-	if (r) {
-		dev_err(&client->dev, "Cannot get platform resources\n");
-		goto err_reg;
-	}
-
-	r = request_threaded_irq(client->irq, NULL, pn544_irq_thread_fn,
-				 IRQF_TRIGGER_RISING, PN544_DRIVER_NAME,
-				 info);
-	if (r < 0) {
-		dev_err(&client->dev, "Unable to register IRQ handler\n");
-		goto err_res;
-	}
-
-	/* If we don't have the test we don't need the sysfs file */
-	if (pdata->test) {
-		r = device_create_file(&client->dev, &pn544_attr);
-		if (r) {
-			dev_err(&client->dev,
-				"sysfs registration failed, error %d\n", r);
-			goto err_irq;
-		}
-	}
-
-	info->miscdev.minor = MISC_DYNAMIC_MINOR;
-	info->miscdev.name = PN544_DRIVER_NAME;
-	info->miscdev.fops = &pn544_fops;
-	info->miscdev.parent = &client->dev;
-	r = misc_register(&info->miscdev);
-	if (r < 0) {
-		dev_err(&client->dev, "Device registration failed\n");
-		goto err_sysfs;
-	}
-
-	dev_dbg(&client->dev, "%s: info: %p, pdata %p, client %p\n",
-		__func__, info, pdata, client);
-
-	return 0;
-
-err_sysfs:
-	if (pdata->test)
-		device_remove_file(&client->dev, &pn544_attr);
-err_irq:
-	free_irq(client->irq, info);
-err_res:
-	if (pdata->free_resources)
-		pdata->free_resources();
-err_reg:
-	regulator_bulk_free(ARRAY_SIZE(info->regs), info->regs);
-err_kmalloc:
-	kfree(info->buf);
-err_buf_alloc:
-	kfree(info);
-err_info_alloc:
-	return r;
-}
-
-static __devexit int pn544_remove(struct i2c_client *client)
-{
-	struct pn544_info *info = i2c_get_clientdata(client);
-	struct pn544_nfc_platform_data *pdata = client->dev.platform_data;
-
-	dev_dbg(&client->dev, "%s\n", __func__);
-
-	misc_deregister(&info->miscdev);
-	if (pdata->test)
-		device_remove_file(&client->dev, &pn544_attr);
-
-	if (info->state != PN544_ST_COLD) {
-		if (pdata->disable)
-			pdata->disable();
-
-		info->read_irq = PN544_NONE;
-	}
-
-	free_irq(client->irq, info);
-	if (pdata->free_resources)
-		pdata->free_resources();
-
-	regulator_bulk_free(ARRAY_SIZE(info->regs), info->regs);
-	kfree(info->buf);
-	kfree(info);
-
-	return 0;
-}
-
-static struct i2c_driver pn544_driver = {
-	.driver = {
-		.name = PN544_DRIVER_NAME,
-#ifdef CONFIG_PM
-		.pm = &pn544_pm_ops,
-#endif
-	},
-	.probe = pn544_probe,
-	.id_table = pn544_id_table,
-	.remove = __devexit_p(pn544_remove),
-};
-
-static int __init pn544_init(void)
-{
-	int r;
-
-	pr_debug(DRIVER_DESC ": %s\n", __func__);
-
-	r = i2c_add_driver(&pn544_driver);
-	if (r) {
-		pr_err(PN544_DRIVER_NAME ": driver registration failed\n");
-		return r;
-	}
-
-	return 0;
-}
-
-static void __exit pn544_exit(void)
-{
-	i2c_del_driver(&pn544_driver);
-	pr_info(DRIVER_DESC ", Exiting.\n");
-}
-
-module_init(pn544_init);
-module_exit(pn544_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/nfc/pn544_hci.c b/drivers/nfc/pn544_hci.c
index aa71807..c9c8570 100644
--- a/drivers/nfc/pn544_hci.c
+++ b/drivers/nfc/pn544_hci.c
@@ -29,7 +29,7 @@
 
 #include <linux/nfc.h>
 #include <net/nfc/hci.h>
-#include <net/nfc/shdlc.h>
+#include <net/nfc/llc.h>
 
 #include <linux/nfc/pn544.h>
 
@@ -128,10 +128,12 @@
 
 /* Largest headroom needed for outgoing custom commands */
 #define PN544_CMDS_HEADROOM	2
+#define PN544_FRAME_HEADROOM 1
+#define PN544_FRAME_TAILROOM 2
 
 struct pn544_hci_info {
 	struct i2c_client *i2c_dev;
-	struct nfc_shdlc *shdlc;
+	struct nfc_hci_dev *hdev;
 
 	enum pn544_state state;
 
@@ -146,6 +148,9 @@
 				 * < 0 if hardware error occured (e.g. i2c err)
 				 * and prevents normal operation.
 				 */
+	int async_cb_type;
+	data_exchange_cb_t async_cb;
+	void *async_cb_context;
 };
 
 static void pn544_hci_platform_init(struct pn544_hci_info *info)
@@ -230,8 +235,12 @@
 		r = i2c_master_send(client, buf, len);
 	}
 
-	if (r >= 0 && r != len)
-		r = -EREMOTEIO;
+	if (r >= 0) {
+		if (r != len)
+			return -EREMOTEIO;
+		else
+			return 0;
+	}
 
 	return r;
 }
@@ -341,13 +350,16 @@
 static irqreturn_t pn544_hci_irq_thread_fn(int irq, void *dev_id)
 {
 	struct pn544_hci_info *info = dev_id;
-	struct i2c_client *client = info->i2c_dev;
+	struct i2c_client *client;
 	struct sk_buff *skb = NULL;
 	int r;
 
-	BUG_ON(!info);
-	BUG_ON(irq != info->i2c_dev->irq);
+	if (!info || irq != info->i2c_dev->irq) {
+		WARN_ON_ONCE(1);
+		return IRQ_NONE;
+	}
 
+	client = info->i2c_dev;
 	dev_dbg(&client->dev, "IRQ\n");
 
 	if (info->hard_fault != 0)
@@ -357,21 +369,21 @@
 	if (r == -EREMOTEIO) {
 		info->hard_fault = r;
 
-		nfc_shdlc_recv_frame(info->shdlc, NULL);
+		nfc_hci_recv_frame(info->hdev, NULL);
 
 		return IRQ_HANDLED;
 	} else if ((r == -ENOMEM) || (r == -EBADMSG)) {
 		return IRQ_HANDLED;
 	}
 
-	nfc_shdlc_recv_frame(info->shdlc, skb);
+	nfc_hci_recv_frame(info->hdev, skb);
 
 	return IRQ_HANDLED;
 }
 
-static int pn544_hci_open(struct nfc_shdlc *shdlc)
+static int pn544_hci_open(struct nfc_hci_dev *hdev)
 {
-	struct pn544_hci_info *info = nfc_shdlc_get_clientdata(shdlc);
+	struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev);
 	int r = 0;
 
 	mutex_lock(&info->info_lock);
@@ -391,9 +403,9 @@
 	return r;
 }
 
-static void pn544_hci_close(struct nfc_shdlc *shdlc)
+static void pn544_hci_close(struct nfc_hci_dev *hdev)
 {
-	struct pn544_hci_info *info = nfc_shdlc_get_clientdata(shdlc);
+	struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev);
 
 	mutex_lock(&info->info_lock);
 
@@ -408,9 +420,8 @@
 	mutex_unlock(&info->info_lock);
 }
 
-static int pn544_hci_ready(struct nfc_shdlc *shdlc)
+static int pn544_hci_ready(struct nfc_hci_dev *hdev)
 {
-	struct nfc_hci_dev *hdev = nfc_shdlc_get_hci_dev(shdlc);
 	struct sk_buff *skb;
 	static struct hw_config {
 		u8 adr[2];
@@ -576,21 +587,45 @@
 	return 0;
 }
 
-static int pn544_hci_xmit(struct nfc_shdlc *shdlc, struct sk_buff *skb)
+static void pn544_hci_add_len_crc(struct sk_buff *skb)
 {
-	struct pn544_hci_info *info = nfc_shdlc_get_clientdata(shdlc);
+	u16 crc;
+	int len;
+
+	len = skb->len + 2;
+	*skb_push(skb, 1) = len;
+
+	crc = crc_ccitt(0xffff, skb->data, skb->len);
+	crc = ~crc;
+	*skb_put(skb, 1) = crc & 0xff;
+	*skb_put(skb, 1) = crc >> 8;
+}
+
+static void pn544_hci_remove_len_crc(struct sk_buff *skb)
+{
+	skb_pull(skb, PN544_FRAME_HEADROOM);
+	skb_trim(skb, PN544_FRAME_TAILROOM);
+}
+
+static int pn544_hci_xmit(struct nfc_hci_dev *hdev, struct sk_buff *skb)
+{
+	struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev);
 	struct i2c_client *client = info->i2c_dev;
+	int r;
 
 	if (info->hard_fault != 0)
 		return info->hard_fault;
 
-	return pn544_hci_i2c_write(client, skb->data, skb->len);
+	pn544_hci_add_len_crc(skb);
+	r = pn544_hci_i2c_write(client, skb->data, skb->len);
+	pn544_hci_remove_len_crc(skb);
+
+	return r;
 }
 
-static int pn544_hci_start_poll(struct nfc_shdlc *shdlc,
+static int pn544_hci_start_poll(struct nfc_hci_dev *hdev,
 				u32 im_protocols, u32 tm_protocols)
 {
-	struct nfc_hci_dev *hdev = nfc_shdlc_get_hci_dev(shdlc);
 	u8 phases = 0;
 	int r;
 	u8 duration[2];
@@ -641,7 +676,7 @@
 	return r;
 }
 
-static int pn544_hci_target_from_gate(struct nfc_shdlc *shdlc, u8 gate,
+static int pn544_hci_target_from_gate(struct nfc_hci_dev *hdev, u8 gate,
 				      struct nfc_target *target)
 {
 	switch (gate) {
@@ -659,11 +694,10 @@
 	return 0;
 }
 
-static int pn544_hci_complete_target_discovered(struct nfc_shdlc *shdlc,
+static int pn544_hci_complete_target_discovered(struct nfc_hci_dev *hdev,
 						u8 gate,
 						struct nfc_target *target)
 {
-	struct nfc_hci_dev *hdev = nfc_shdlc_get_hci_dev(shdlc);
 	struct sk_buff *uid_skb;
 	int r = 0;
 
@@ -704,6 +738,26 @@
 	return r;
 }
 
+#define PN544_CB_TYPE_READER_F 1
+
+static void pn544_hci_data_exchange_cb(void *context, struct sk_buff *skb,
+				       int err)
+{
+	struct pn544_hci_info *info = context;
+
+	switch (info->async_cb_type) {
+	case PN544_CB_TYPE_READER_F:
+		if (err == 0)
+			skb_pull(skb, 1);
+		info->async_cb(info->async_cb_context, skb, err);
+		break;
+	default:
+		if (err == 0)
+			kfree_skb(skb);
+		break;
+	}
+}
+
 #define MIFARE_CMD_AUTH_KEY_A	0x60
 #define MIFARE_CMD_AUTH_KEY_B	0x61
 #define MIFARE_CMD_HEADER	2
@@ -715,13 +769,12 @@
  * <= 0: driver handled the data exchange
  *    1: driver doesn't especially handle, please do standard processing
  */
-static int pn544_hci_data_exchange(struct nfc_shdlc *shdlc,
+static int pn544_hci_data_exchange(struct nfc_hci_dev *hdev,
 				   struct nfc_target *target,
-				   struct sk_buff *skb,
-				   struct sk_buff **res_skb)
+				   struct sk_buff *skb, data_exchange_cb_t cb,
+				   void *cb_context)
 {
-	struct nfc_hci_dev *hdev = nfc_shdlc_get_hci_dev(shdlc);
-	int r;
+	struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev);
 
 	pr_info(DRIVER_DESC ": %s for gate=%d\n", __func__,
 		target->hci_reader_gate);
@@ -746,41 +799,43 @@
 				memcpy(data, uid, MIFARE_UID_LEN);
 			}
 
-			return nfc_hci_send_cmd(hdev, target->hci_reader_gate,
-						PN544_MIFARE_CMD,
-						skb->data, skb->len, res_skb);
+			return nfc_hci_send_cmd_async(hdev,
+						      target->hci_reader_gate,
+						      PN544_MIFARE_CMD,
+						      skb->data, skb->len,
+						      cb, cb_context);
 		} else
 			return 1;
 	case PN544_RF_READER_F_GATE:
 		*skb_push(skb, 1) = 0;
 		*skb_push(skb, 1) = 0;
 
-		r = nfc_hci_send_cmd(hdev, target->hci_reader_gate,
-				     PN544_FELICA_RAW,
-				     skb->data, skb->len, res_skb);
-		if (r == 0)
-			skb_pull(*res_skb, 1);
-		return r;
+		info->async_cb_type = PN544_CB_TYPE_READER_F;
+		info->async_cb = cb;
+		info->async_cb_context = cb_context;
+
+		return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate,
+					      PN544_FELICA_RAW, skb->data,
+					      skb->len,
+					      pn544_hci_data_exchange_cb, info);
 	case PN544_RF_READER_JEWEL_GATE:
-		return nfc_hci_send_cmd(hdev, target->hci_reader_gate,
-					PN544_JEWEL_RAW_CMD,
-					skb->data, skb->len, res_skb);
+		return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate,
+					      PN544_JEWEL_RAW_CMD, skb->data,
+					      skb->len, cb, cb_context);
 	default:
 		return 1;
 	}
 }
 
-static int pn544_hci_check_presence(struct nfc_shdlc *shdlc,
+static int pn544_hci_check_presence(struct nfc_hci_dev *hdev,
 				   struct nfc_target *target)
 {
-	struct nfc_hci_dev *hdev = nfc_shdlc_get_hci_dev(shdlc);
-
 	return nfc_hci_send_cmd(hdev, target->hci_reader_gate,
 				PN544_RF_READER_CMD_PRESENCE_CHECK,
 				NULL, 0, NULL);
 }
 
-static struct nfc_shdlc_ops pn544_shdlc_ops = {
+static struct nfc_hci_ops pn544_hci_ops = {
 	.open = pn544_hci_open,
 	.close = pn544_hci_close,
 	.hci_ready = pn544_hci_ready,
@@ -848,8 +903,8 @@
 	pn544_hci_platform_init(info);
 
 	r = request_threaded_irq(client->irq, NULL, pn544_hci_irq_thread_fn,
-				 IRQF_TRIGGER_RISING, PN544_HCI_DRIVER_NAME,
-				 info);
+				 IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+				 PN544_HCI_DRIVER_NAME, info);
 	if (r < 0) {
 		dev_err(&client->dev, "Unable to register IRQ handler\n");
 		goto err_rti;
@@ -872,22 +927,30 @@
 		    NFC_PROTO_ISO14443_B_MASK |
 		    NFC_PROTO_NFC_DEP_MASK;
 
-	info->shdlc = nfc_shdlc_allocate(&pn544_shdlc_ops,
-					 &init_data, protocols,
-					 PN544_CMDS_HEADROOM, 0,
-					 PN544_HCI_LLC_MAX_PAYLOAD,
-					 dev_name(&client->dev));
-	if (!info->shdlc) {
-		dev_err(&client->dev, "Cannot allocate nfc shdlc.\n");
+	info->hdev = nfc_hci_allocate_device(&pn544_hci_ops, &init_data,
+					     protocols, LLC_SHDLC_NAME,
+					     PN544_FRAME_HEADROOM +
+					     PN544_CMDS_HEADROOM,
+					     PN544_FRAME_TAILROOM,
+					     PN544_HCI_LLC_MAX_PAYLOAD);
+	if (!info->hdev) {
+		dev_err(&client->dev, "Cannot allocate nfc hdev.\n");
 		r = -ENOMEM;
-		goto err_allocshdlc;
+		goto err_alloc_hdev;
 	}
 
-	nfc_shdlc_set_clientdata(info->shdlc, info);
+	nfc_hci_set_clientdata(info->hdev, info);
+
+	r = nfc_hci_register_device(info->hdev);
+	if (r)
+		goto err_regdev;
 
 	return 0;
 
-err_allocshdlc:
+err_regdev:
+	nfc_hci_free_device(info->hdev);
+
+err_alloc_hdev:
 	free_irq(client->irq, info);
 
 err_rti:
@@ -908,7 +971,7 @@
 
 	dev_dbg(&client->dev, "%s\n", __func__);
 
-	nfc_shdlc_free(info->shdlc);
+	nfc_hci_free_device(info->hdev);
 
 	if (info->state != PN544_ST_COLD) {
 		if (pdata->disable)
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index e02fc68..2385119 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -1935,36 +1935,6 @@
 }
 
 /**
- * ieee80211_fhss_chan_to_freq - get channel frequency
- * @channel: the FHSS channel
- *
- * Convert IEEE802.11 FHSS channel to frequency (MHz)
- * Ref IEEE 802.11-2007 section 14.6
- */
-static inline int ieee80211_fhss_chan_to_freq(int channel)
-{
-	if ((channel > 1) && (channel < 96))
-		return channel + 2400;
-	else
-		return -1;
-}
-
-/**
- * ieee80211_freq_to_fhss_chan - get channel
- * @freq: the channels frequency
- *
- * Convert frequency (MHz) to IEEE802.11 FHSS channel
- * Ref IEEE 802.11-2007 section 14.6
- */
-static inline int ieee80211_freq_to_fhss_chan(int freq)
-{
-	if ((freq > 2401) && (freq < 2496))
-		return freq - 2400;
-	else
-		return -1;
-}
-
-/**
  * ieee80211_dsss_chan_to_freq - get channel center frequency
  * @channel: the DSSS channel
  *
@@ -2000,56 +1970,6 @@
 		return -1;
 }
 
-/* Convert IEEE802.11 HR DSSS channel to frequency (MHz) and back
- * Ref IEEE 802.11-2007 section 18.4.6.2
- *
- * The channels and frequencies are the same as those defined for DSSS
- */
-#define ieee80211_hr_chan_to_freq(chan) ieee80211_dsss_chan_to_freq(chan)
-#define ieee80211_freq_to_hr_chan(freq) ieee80211_freq_to_dsss_chan(freq)
-
-/* Convert IEEE802.11 ERP channel to frequency (MHz) and back
- * Ref IEEE 802.11-2007 section 19.4.2
- */
-#define ieee80211_erp_chan_to_freq(chan) ieee80211_hr_chan_to_freq(chan)
-#define ieee80211_freq_to_erp_chan(freq) ieee80211_freq_to_hr_chan(freq)
-
-/**
- * ieee80211_ofdm_chan_to_freq - get channel center frequency
- * @s_freq: starting frequency == (dotChannelStartingFactor/2) MHz
- * @channel: the OFDM channel
- *
- * Convert IEEE802.11 OFDM channel to center frequency (MHz)
- * Ref IEEE 802.11-2007 section 17.3.8.3.2
- */
-static inline int ieee80211_ofdm_chan_to_freq(int s_freq, int channel)
-{
-	if ((channel > 0) && (channel <= 200) &&
-	    (s_freq >= 4000))
-		return s_freq + (channel * 5);
-	else
-		return -1;
-}
-
-/**
- * ieee80211_freq_to_ofdm_channel - get channel
- * @s_freq: starting frequency == (dotChannelStartingFactor/2) MHz
- * @freq: the frequency
- *
- * Convert frequency (MHz) to IEEE802.11 OFDM channel
- * Ref IEEE 802.11-2007 section 17.3.8.3.2
- *
- * This routine selects the channel with the closest center frequency.
- */
-static inline int ieee80211_freq_to_ofdm_chan(int s_freq, int freq)
-{
-	if ((freq > (s_freq + 2)) && (freq <= (s_freq + 1202)) &&
-	    (s_freq >= 4000))
-		return (freq + 2 - s_freq) / 5;
-	else
-		return -1;
-}
-
 /**
  * ieee80211_tu_to_usec - convert time units (TU) to microseconds
  * @tu: the TUs
diff --git a/include/linux/nfc.h b/include/linux/nfc.h
index 6189f27..d908d17 100644
--- a/include/linux/nfc.h
+++ b/include/linux/nfc.h
@@ -183,4 +183,15 @@
 
 #define NFC_HEADER_SIZE 1
 
+/**
+ * Pseudo-header info for raw socket packets
+ * First byte is the adapter index
+ * Second byte contains flags
+ *  - 0x01 - Direction (0=RX, 1=TX)
+ *  - 0x02-0x80 - Reserved
+ **/
+#define NFC_LLCP_RAW_HEADER_SIZE	2
+#define NFC_LLCP_DIRECTION_RX		0x00
+#define NFC_LLCP_DIRECTION_TX		0x01
+
 #endif /*__LINUX_NFC_H */
diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h
index 4584162..7df9b500 100644
--- a/include/linux/nl80211.h
+++ b/include/linux/nl80211.h
@@ -573,6 +573,11 @@
  * @NL80211_CMD_STOP_P2P_DEVICE: Stop the given P2P Device, identified by
  *	its %NL80211_ATTR_WDEV identifier.
  *
+ * @NL80211_CMD_CONN_FAILED: connection request to an AP failed; used to
+ *	notify userspace that AP has rejected the connection request from a
+ *	station, due to particular reason. %NL80211_ATTR_CONN_FAILED_REASON
+ *	is used for this.
+ *
  * @NL80211_CMD_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
  */
@@ -719,6 +724,8 @@
 	NL80211_CMD_START_P2P_DEVICE,
 	NL80211_CMD_STOP_P2P_DEVICE,
 
+	NL80211_CMD_CONN_FAILED,
+
 	/* add new commands above here */
 
 	/* used to define NL80211_CMD_MAX below */
@@ -1262,6 +1269,10 @@
  *	was used to provide the hint. For the different types of
  *	allowed user regulatory hints see nl80211_user_reg_hint_type.
  *
+ * @NL80211_ATTR_CONN_FAILED_REASON: The reason for which AP has rejected
+ *	the connection request from a station. nl80211_connect_failed_reason
+ *	enum has different reasons of connection failure.
+ *
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
@@ -1517,6 +1528,8 @@
 
 	NL80211_ATTR_USER_REG_HINT_TYPE,
 
+	NL80211_ATTR_CONN_FAILED_REASON,
+
 	/* add attributes here, update the policy in nl80211.c */
 
 	__NL80211_ATTR_AFTER_LAST,
@@ -3045,4 +3058,15 @@
 	NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U =	1<<3,
 };
 
+/**
+ * enum nl80211_connect_failed_reason - connection request failed reasons
+ * @NL80211_CONN_FAIL_MAX_CLIENTS: Maximum number of clients that can be
+ *	handled by the AP is reached.
+ * @NL80211_CONN_FAIL_BLOCKED_CLIENT: Client's MAC is in the AP's blocklist.
+ */
+enum nl80211_connect_failed_reason {
+	NL80211_CONN_FAIL_MAX_CLIENTS,
+	NL80211_CONN_FAIL_BLOCKED_CLIENT,
+};
+
 #endif /* __LINUX_NL80211_H */
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 23cf413..76b2b6b 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -302,8 +302,11 @@
 
 /* ---- HCI Error Codes ---- */
 #define HCI_ERROR_AUTH_FAILURE		0x05
+#define HCI_ERROR_CONNECTION_TIMEOUT	0x08
 #define HCI_ERROR_REJ_BAD_ADDR		0x0f
 #define HCI_ERROR_REMOTE_USER_TERM	0x13
+#define HCI_ERROR_REMOTE_LOW_RESOURCES	0x14
+#define HCI_ERROR_REMOTE_POWER_OFF	0x15
 #define HCI_ERROR_LOCAL_HOST_TERM	0x16
 #define HCI_ERROR_PAIRING_NOT_ALLOWED	0x18
 
@@ -1246,6 +1249,24 @@
 	bdaddr_t bdaddr;
 } __packed;
 
+#define HCI_EV_USER_PASSKEY_NOTIFY	0x3b
+struct hci_ev_user_passkey_notify {
+	bdaddr_t	bdaddr;
+	__le32		passkey;
+} __packed;
+
+#define HCI_KEYPRESS_STARTED		0
+#define HCI_KEYPRESS_ENTERED		1
+#define HCI_KEYPRESS_ERASED		2
+#define HCI_KEYPRESS_CLEARED		3
+#define HCI_KEYPRESS_COMPLETED		4
+
+#define HCI_EV_KEYPRESS_NOTIFY		0x3c
+struct hci_ev_keypress_notify {
+	bdaddr_t	bdaddr;
+	__u8		type;
+} __packed;
+
 #define HCI_EV_REMOTE_HOST_FEATURES	0x3d
 struct hci_ev_remote_host_features {
 	bdaddr_t bdaddr;
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 41d9439..e7d45460 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -303,6 +303,8 @@
 	__u8		pin_length;
 	__u8		enc_key_size;
 	__u8		io_capability;
+	__u32		passkey_notify;
+	__u8		passkey_entered;
 	__u16		disc_timeout;
 	unsigned long	flags;
 
@@ -428,15 +430,6 @@
 	       test_bit(HCI_CONN_SSP_ENABLED, &conn->flags);
 }
 
-static inline void hci_conn_hash_init(struct hci_dev *hdev)
-{
-	struct hci_conn_hash *h = &hdev->conn_hash;
-	INIT_LIST_HEAD(&h->list);
-	h->acl_num = 0;
-	h->sco_num = 0;
-	h->le_num = 0;
-}
-
 static inline void hci_conn_hash_add(struct hci_dev *hdev, struct hci_conn *c)
 {
 	struct hci_conn_hash *h = &hdev->conn_hash;
@@ -551,9 +544,7 @@
 	return NULL;
 }
 
-void hci_acl_connect(struct hci_conn *conn);
 void hci_acl_disconn(struct hci_conn *conn, __u8 reason);
-void hci_add_sco(struct hci_conn *conn, __u16 handle);
 void hci_setup_sync(struct hci_conn *conn, __u16 handle);
 void hci_sco_setup(struct hci_conn *conn, __u8 status);
 
@@ -563,7 +554,7 @@
 void hci_conn_check_pending(struct hci_dev *hdev);
 
 struct hci_chan *hci_chan_create(struct hci_conn *conn);
-int hci_chan_del(struct hci_chan *chan);
+void hci_chan_del(struct hci_chan *chan);
 void hci_chan_list_flush(struct hci_conn *conn);
 
 struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst,
@@ -614,11 +605,17 @@
 /* ----- HCI Devices ----- */
 static inline void hci_dev_put(struct hci_dev *d)
 {
+	BT_DBG("%s orig refcnt %d", d->name,
+	       atomic_read(&d->dev.kobj.kref.refcount));
+
 	put_device(&d->dev);
 }
 
 static inline struct hci_dev *hci_dev_hold(struct hci_dev *d)
 {
+	BT_DBG("%s orig refcnt %d", d->name,
+	       atomic_read(&d->dev.kobj.kref.refcount));
+
 	get_device(&d->dev);
 	return d;
 }
@@ -1004,7 +1001,7 @@
 			  u8 addr_type, u32 flags, u8 *name, u8 name_len,
 			  u8 *dev_class);
 int mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
-			     u8 link_type, u8 addr_type);
+			     u8 link_type, u8 addr_type, u8 reason);
 int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr,
 			   u8 link_type, u8 addr_type, u8 status);
 int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
@@ -1027,6 +1024,9 @@
 				     u8 link_type, u8 addr_type, u8 status);
 int mgmt_user_passkey_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
 					 u8 link_type, u8 addr_type, u8 status);
+int mgmt_user_passkey_notify(struct hci_dev *hdev, bdaddr_t *bdaddr,
+			     u8 link_type, u8 addr_type, u32 passkey,
+			     u8 entered);
 int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
 		     u8 addr_type, u8 status);
 int mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status);
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index d206296..7ed8e35 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -433,11 +433,10 @@
 	struct sock *sk;
 
 	struct l2cap_conn	*conn;
+	struct kref	kref;
 
 	__u8		state;
 
-	atomic_t	refcnt;
-
 	__le16		psm;
 	__u16		dcid;
 	__u16		scid;
diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
index 4348ee8..22980a7 100644
--- a/include/net/bluetooth/mgmt.h
+++ b/include/net/bluetooth/mgmt.h
@@ -405,7 +405,16 @@
 	__u8	eir[0];
 } __packed;
 
+#define MGMT_DEV_DISCONN_UNKNOWN	0x00
+#define MGMT_DEV_DISCONN_TIMEOUT	0x01
+#define MGMT_DEV_DISCONN_LOCAL_HOST	0x02
+#define MGMT_DEV_DISCONN_REMOTE		0x03
+
 #define MGMT_EV_DEVICE_DISCONNECTED	0x000C
+struct mgmt_ev_device_disconnected {
+	struct mgmt_addr_info addr;
+	__u8	reason;
+} __packed;
 
 #define MGMT_EV_CONNECT_FAILED		0x000D
 struct mgmt_ev_connect_failed {
@@ -469,3 +478,10 @@
 struct mgmt_ev_device_unpaired {
 	struct mgmt_addr_info addr;
 } __packed;
+
+#define MGMT_EV_PASSKEY_NOTIFY		0x0017
+struct mgmt_ev_passkey_notify {
+	struct mgmt_addr_info addr;
+	__le32	passkey;
+	__u8	entered;
+} __packed;
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index a9a2be7..1b49890 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -1580,9 +1580,7 @@
  * @set_cqm_txe_config: Configure connection quality monitor TX error
  *	thresholds.
  * @sched_scan_start: Tell the driver to start a scheduled scan.
- * @sched_scan_stop: Tell the driver to stop an ongoing scheduled
- *	scan.  The driver_initiated flag specifies whether the driver
- *	itself has informed that the scan has stopped.
+ * @sched_scan_stop: Tell the driver to stop an ongoing scheduled scan.
  *
  * @mgmt_frame_register: Notify driver that a management frame type was
  *	registered. Note that this callback may not sleep, and cannot run
@@ -1630,7 +1628,7 @@
 	void	(*set_wakeup)(struct wiphy *wiphy, bool enabled);
 
 	struct wireless_dev * (*add_virtual_intf)(struct wiphy *wiphy,
-						  char *name,
+						  const char *name,
 						  enum nl80211_iftype type,
 						  u32 *flags,
 						  struct vif_params *params);
@@ -3363,6 +3361,25 @@
 void cfg80211_del_sta(struct net_device *dev, const u8 *mac_addr, gfp_t gfp);
 
 /**
+ * cfg80211_conn_failed - connection request failed notification
+ *
+ * @dev: the netdev
+ * @mac_addr: the station's address
+ * @reason: the reason for connection failure
+ * @gfp: allocation flags
+ *
+ * Whenever a station tries to connect to an AP and if the station
+ * could not connect to the AP as the AP has rejected the connection
+ * for some reasons, this function is called.
+ *
+ * The reason for connection failure can be any of the value from
+ * nl80211_connect_failed_reason enum
+ */
+void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr,
+			  enum nl80211_connect_failed_reason reason,
+			  gfp_t gfp);
+
+/**
  * cfg80211_rx_mgmt - notification of received, unprocessed management frame
  * @wdev: wireless device receiving the frame
  * @freq: Frequency on which the frame was received in MHz
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 71f8262..82558c8 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -973,21 +973,29 @@
  *	generation in software.
  * @IEEE80211_KEY_FLAG_PAIRWISE: Set by mac80211, this flag indicates
  *	that the key is pairwise rather then a shared key.
- * @IEEE80211_KEY_FLAG_SW_MGMT: This flag should be set by the driver for a
+ * @IEEE80211_KEY_FLAG_SW_MGMT_TX: This flag should be set by the driver for a
  *	CCMP key if it requires CCMP encryption of management frames (MFP) to
  *	be done in software.
  * @IEEE80211_KEY_FLAG_PUT_IV_SPACE: This flag should be set by the driver
  *	if space should be prepared for the IV, but the IV
  *	itself should not be generated. Do not set together with
  *	@IEEE80211_KEY_FLAG_GENERATE_IV on the same key.
+ * @IEEE80211_KEY_FLAG_RX_MGMT: This key will be used to decrypt received
+ *	management frames. The flag can help drivers that have a hardware
+ *	crypto implementation that doesn't deal with management frames
+ *	properly by allowing them to not upload the keys to hardware and
+ *	fall back to software crypto. Note that this flag deals only with
+ *	RX, if your crypto engine can't deal with TX you can also set the
+ *	%IEEE80211_KEY_FLAG_SW_MGMT_TX flag to encrypt such frames in SW.
  */
 enum ieee80211_key_flags {
 	IEEE80211_KEY_FLAG_WMM_STA	= 1<<0,
 	IEEE80211_KEY_FLAG_GENERATE_IV	= 1<<1,
 	IEEE80211_KEY_FLAG_GENERATE_MMIC= 1<<2,
 	IEEE80211_KEY_FLAG_PAIRWISE	= 1<<3,
-	IEEE80211_KEY_FLAG_SW_MGMT	= 1<<4,
+	IEEE80211_KEY_FLAG_SW_MGMT_TX	= 1<<4,
 	IEEE80211_KEY_FLAG_PUT_IV_SPACE = 1<<5,
+	IEEE80211_KEY_FLAG_RX_MGMT	= 1<<6,
 };
 
 /**
diff --git a/include/net/nfc/hci.h b/include/net/nfc/hci.h
index f5169b0..e900072 100644
--- a/include/net/nfc/hci.h
+++ b/include/net/nfc/hci.h
@@ -30,6 +30,11 @@
 	int (*open) (struct nfc_hci_dev *hdev);
 	void (*close) (struct nfc_hci_dev *hdev);
 	int (*hci_ready) (struct nfc_hci_dev *hdev);
+	/*
+	 * xmit must always send the complete buffer before
+	 * returning. Returned result must be 0 for success
+	 * or negative for failure.
+	 */
 	int (*xmit) (struct nfc_hci_dev *hdev, struct sk_buff *skb);
 	int (*start_poll) (struct nfc_hci_dev *hdev,
 			   u32 im_protocols, u32 tm_protocols);
@@ -38,8 +43,8 @@
 	int (*complete_target_discovered) (struct nfc_hci_dev *hdev, u8 gate,
 					   struct nfc_target *target);
 	int (*data_exchange) (struct nfc_hci_dev *hdev,
-			      struct nfc_target *target,
-			      struct sk_buff *skb, struct sk_buff **res_skb);
+			      struct nfc_target *target, struct sk_buff *skb,
+			      data_exchange_cb_t cb, void *cb_context);
 	int (*check_presence)(struct nfc_hci_dev *hdev,
 			      struct nfc_target *target);
 };
@@ -74,7 +79,6 @@
 
 	struct list_head msg_tx_queue;
 
-	struct workqueue_struct *msg_tx_wq;
 	struct work_struct msg_tx_work;
 
 	struct timer_list cmd_timer;
@@ -82,13 +86,14 @@
 
 	struct sk_buff_head rx_hcp_frags;
 
-	struct workqueue_struct *msg_rx_wq;
 	struct work_struct msg_rx_work;
 
 	struct sk_buff_head msg_rx_queue;
 
 	struct nfc_hci_ops *ops;
 
+	struct nfc_llc *llc;
+
 	struct nfc_hci_init_data init_data;
 
 	void *clientdata;
@@ -105,12 +110,17 @@
 	u8 hw_mpw;
 	u8 hw_software;
 	u8 hw_bsid;
+
+	int async_cb_type;
+	data_exchange_cb_t async_cb;
+	void *async_cb_context;
 };
 
 /* hci device allocation */
 struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops,
 					    struct nfc_hci_init_data *init_data,
 					    u32 protocols,
+					    const char *llc_name,
 					    int tx_headroom,
 					    int tx_tailroom,
 					    int max_link_payload);
@@ -202,6 +212,9 @@
 		      const u8 *param, size_t param_len);
 int nfc_hci_send_cmd(struct nfc_hci_dev *hdev, u8 gate, u8 cmd,
 		     const u8 *param, size_t param_len, struct sk_buff **skb);
+int nfc_hci_send_cmd_async(struct nfc_hci_dev *hdev, u8 gate, u8 cmd,
+			   const u8 *param, size_t param_len,
+			   data_exchange_cb_t cb, void *cb_context);
 int nfc_hci_send_response(struct nfc_hci_dev *hdev, u8 gate, u8 response,
 			  const u8 *param, size_t param_len);
 int nfc_hci_send_event(struct nfc_hci_dev *hdev, u8 gate, u8 event,
diff --git a/include/net/nfc/llc.h b/include/net/nfc/llc.h
new file mode 100644
index 0000000..400ab7a
--- /dev/null
+++ b/include/net/nfc/llc.h
@@ -0,0 +1,54 @@
+/*
+ * Link Layer Control manager public interface
+ *
+ * Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __NFC_LLC_H_
+#define __NFC_LLC_H_
+
+#include <net/nfc/hci.h>
+#include <linux/skbuff.h>
+
+#define LLC_NOP_NAME "nop"
+#define LLC_SHDLC_NAME "shdlc"
+
+typedef void (*rcv_to_hci_t) (struct nfc_hci_dev *hdev, struct sk_buff *skb);
+typedef int (*xmit_to_drv_t) (struct nfc_hci_dev *hdev, struct sk_buff *skb);
+typedef void (*llc_failure_t) (struct nfc_hci_dev *hdev, int err);
+
+struct nfc_llc;
+
+struct nfc_llc *nfc_llc_allocate(const char *name, struct nfc_hci_dev *hdev,
+				 xmit_to_drv_t xmit_to_drv,
+				 rcv_to_hci_t rcv_to_hci, int tx_headroom,
+				 int tx_tailroom, llc_failure_t llc_failure);
+void nfc_llc_free(struct nfc_llc *llc);
+
+void nfc_llc_get_rx_head_tail_room(struct nfc_llc *llc, int *rx_headroom,
+				   int *rx_tailroom);
+
+
+int nfc_llc_start(struct nfc_llc *llc);
+int nfc_llc_stop(struct nfc_llc *llc);
+void nfc_llc_rcv_from_drv(struct nfc_llc *llc, struct sk_buff *skb);
+int nfc_llc_xmit_from_hci(struct nfc_llc *llc, struct sk_buff *skb);
+
+int nfc_llc_init(void);
+void nfc_llc_exit(void);
+
+#endif /* __NFC_LLC_H_ */
diff --git a/include/net/nfc/nci.h b/include/net/nfc/nci.h
index 276094b..88785e5 100644
--- a/include/net/nfc/nci.h
+++ b/include/net/nfc/nci.h
@@ -32,6 +32,7 @@
 #define NCI_MAX_NUM_MAPPING_CONFIGS				10
 #define NCI_MAX_NUM_RF_CONFIGS					10
 #define NCI_MAX_NUM_CONN					10
+#define NCI_MAX_PARAM_LEN					251
 
 /* NCI Status Codes */
 #define NCI_STATUS_OK						0x00
@@ -102,6 +103,9 @@
 #define NCI_RF_INTERFACE_ISO_DEP				0x02
 #define NCI_RF_INTERFACE_NFC_DEP				0x03
 
+/* NCI Configuration Parameter Tags */
+#define NCI_PN_ATR_REQ_GEN_BYTES				0x29
+
 /* NCI Reset types */
 #define NCI_RESET_TYPE_KEEP_CONFIG				0x00
 #define NCI_RESET_TYPE_RESET_CONFIG				0x01
@@ -188,6 +192,18 @@
 
 #define NCI_OP_CORE_INIT_CMD		nci_opcode_pack(NCI_GID_CORE, 0x01)
 
+#define NCI_OP_CORE_SET_CONFIG_CMD	nci_opcode_pack(NCI_GID_CORE, 0x02)
+struct set_config_param {
+	__u8	id;
+	__u8	len;
+	__u8	val[NCI_MAX_PARAM_LEN];
+} __packed;
+
+struct nci_core_set_config_cmd {
+	__u8	num_params;
+	struct	set_config_param param; /* support 1 param per cmd is enough */
+} __packed;
+
 #define NCI_OP_RF_DISCOVER_MAP_CMD	nci_opcode_pack(NCI_GID_RF_MGMT, 0x00)
 struct disc_map_config {
 	__u8	rf_protocol;
@@ -252,6 +268,13 @@
 	__le32	manufact_specific_info;
 } __packed;
 
+#define NCI_OP_CORE_SET_CONFIG_RSP	nci_opcode_pack(NCI_GID_CORE, 0x02)
+struct nci_core_set_config_rsp {
+	__u8	status;
+	__u8	num_params;
+	__u8	params_id[0];	/* variable size array */
+} __packed;
+
 #define NCI_OP_RF_DISCOVER_MAP_RSP	nci_opcode_pack(NCI_GID_RF_MGMT, 0x00)
 
 #define NCI_OP_RF_DISCOVER_RSP		nci_opcode_pack(NCI_GID_RF_MGMT, 0x03)
@@ -328,6 +351,11 @@
 	__u8	attrib_res[50];
 };
 
+struct activation_params_poll_nfc_dep {
+	__u8	atr_res_len;
+	__u8	atr_res[63];
+};
+
 struct nci_rf_intf_activated_ntf {
 	__u8	rf_discovery_id;
 	__u8	rf_interface;
@@ -351,6 +379,7 @@
 	union {
 		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;
 	} activation_params;
 
 } __packed;
diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h
index feba740..d705d86 100644
--- a/include/net/nfc/nci_core.h
+++ b/include/net/nfc/nci_core.h
@@ -54,6 +54,7 @@
 /* NCI timeouts */
 #define NCI_RESET_TIMEOUT			5000
 #define NCI_INIT_TIMEOUT			5000
+#define NCI_SET_CONFIG_TIMEOUT			5000
 #define NCI_RF_DISC_TIMEOUT			5000
 #define NCI_RF_DISC_SELECT_TIMEOUT		5000
 #define NCI_RF_DEACTIVATE_TIMEOUT		30000
@@ -137,6 +138,10 @@
 	data_exchange_cb_t	data_exchange_cb;
 	void			*data_exchange_cb_context;
 	struct sk_buff		*rx_data_reassembly;
+
+	/* stored during intf_activated_ntf */
+	__u8 remote_gb[NFC_MAX_GT_LEN];
+	__u8 remote_gb_len;
 };
 
 /* ----- NCI Devices ----- */
diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h
index 6735909..f05b106 100644
--- a/include/net/nfc/nfc.h
+++ b/include/net/nfc/nfc.h
@@ -72,6 +72,7 @@
 
 #define NFC_TARGET_IDX_ANY -1
 #define NFC_MAX_GT_LEN 48
+#define NFC_ATR_RES_GT_OFFSET 15
 
 struct nfc_target {
 	u32 idx;
@@ -112,7 +113,6 @@
 	int tx_tailroom;
 
 	struct timer_list check_pres_timer;
-	struct workqueue_struct *check_pres_wq;
 	struct work_struct check_pres_work;
 
 	struct nfc_ops *ops;
diff --git a/include/net/nfc/shdlc.h b/include/net/nfc/shdlc.h
deleted file mode 100644
index 35e930d..0000000
--- a/include/net/nfc/shdlc.h
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2012  Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License 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, write to the
- * Free Software Foundation, Inc.,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#ifndef __NFC_SHDLC_H
-#define __NFC_SHDLC_H
-
-struct nfc_shdlc;
-
-struct nfc_shdlc_ops {
-	int (*open) (struct nfc_shdlc *shdlc);
-	void (*close) (struct nfc_shdlc *shdlc);
-	int (*hci_ready) (struct nfc_shdlc *shdlc);
-	int (*xmit) (struct nfc_shdlc *shdlc, struct sk_buff *skb);
-	int (*start_poll) (struct nfc_shdlc *shdlc,
-			   u32 im_protocols, u32 tm_protocols);
-	int (*target_from_gate) (struct nfc_shdlc *shdlc, u8 gate,
-				 struct nfc_target *target);
-	int (*complete_target_discovered) (struct nfc_shdlc *shdlc, u8 gate,
-					   struct nfc_target *target);
-	int (*data_exchange) (struct nfc_shdlc *shdlc,
-			      struct nfc_target *target,
-			      struct sk_buff *skb, struct sk_buff **res_skb);
-	int (*check_presence)(struct nfc_shdlc *shdlc,
-			      struct nfc_target *target);
-};
-
-enum shdlc_state {
-	SHDLC_DISCONNECTED = 0,
-	SHDLC_CONNECTING = 1,
-	SHDLC_NEGOCIATING = 2,
-	SHDLC_CONNECTED = 3
-};
-
-struct nfc_shdlc {
-	struct mutex state_mutex;
-	enum shdlc_state state;
-	int hard_fault;
-
-	struct nfc_hci_dev *hdev;
-
-	wait_queue_head_t *connect_wq;
-	int connect_tries;
-	int connect_result;
-	struct timer_list connect_timer;/* aka T3 in spec 10.6.1 */
-
-	u8 w;				/* window size */
-	bool srej_support;
-
-	struct timer_list t1_timer;	/* send ack timeout */
-	bool t1_active;
-
-	struct timer_list t2_timer;	/* guard/retransmit timeout */
-	bool t2_active;
-
-	int ns;				/* next seq num for send */
-	int nr;				/* next expected seq num for receive */
-	int dnr;			/* oldest sent unacked seq num */
-
-	struct sk_buff_head rcv_q;
-
-	struct sk_buff_head send_q;
-	bool rnr;			/* other side is not ready to receive */
-
-	struct sk_buff_head ack_pending_q;
-
-	struct workqueue_struct *sm_wq;
-	struct work_struct sm_work;
-
-	struct nfc_shdlc_ops *ops;
-
-	int client_headroom;
-	int client_tailroom;
-
-	void *clientdata;
-};
-
-void nfc_shdlc_recv_frame(struct nfc_shdlc *shdlc, struct sk_buff *skb);
-
-struct nfc_shdlc *nfc_shdlc_allocate(struct nfc_shdlc_ops *ops,
-				     struct nfc_hci_init_data *init_data,
-				     u32 protocols,
-				     int tx_headroom, int tx_tailroom,
-				     int max_link_payload, const char *devname);
-
-void nfc_shdlc_free(struct nfc_shdlc *shdlc);
-
-void nfc_shdlc_set_clientdata(struct nfc_shdlc *shdlc, void *clientdata);
-void *nfc_shdlc_get_clientdata(struct nfc_shdlc *shdlc);
-struct nfc_hci_dev *nfc_shdlc_get_hci_dev(struct nfc_shdlc *shdlc);
-
-#endif /* __NFC_SHDLC_H */
diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c
index 58f9762..9d49ee6 100644
--- a/net/bluetooth/af_bluetooth.c
+++ b/net/bluetooth/af_bluetooth.c
@@ -567,8 +567,6 @@
 
 static int bt_seq_show(struct seq_file *seq, void *v)
 {
-	struct sock *sk;
-	struct bt_sock *bt;
 	struct bt_seq_state *s = seq->private;
 	struct bt_sock_list *l = s->l;
 	bdaddr_t src_baswapped, dst_baswapped;
@@ -583,8 +581,8 @@
 
 		seq_putc(seq, '\n');
 	} else {
-		sk = sk_entry(v);
-		bt = bt_sk(sk);
+		struct sock *sk = sk_entry(v);
+		struct bt_sock *bt = bt_sk(sk);
 		baswap(&src_baswapped, &bt->src);
 		baswap(&dst_baswapped, &bt->dst);
 
@@ -624,7 +622,7 @@
 	sk_list = PDE(inode)->data;
 	s = __seq_open_private(file, &bt_seq_ops,
 			       sizeof(struct bt_seq_state));
-	if (s == NULL)
+	if (!s)
 		return -ENOMEM;
 
 	s->l = sk_list;
@@ -646,7 +644,7 @@
 	sk_list->fops.release   = seq_release_private;
 
 	pde = proc_net_fops_create(net, name, 0, &sk_list->fops);
-	if (pde == NULL)
+	if (!pde)
 		return -ENOMEM;
 
 	pde->data = sk_list;
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 3c094e7..b9196a4 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -31,7 +31,7 @@
 #include <net/bluetooth/a2mp.h>
 #include <net/bluetooth/smp.h>
 
-static void hci_le_connect(struct hci_conn *conn)
+static void hci_le_create_connection(struct hci_conn *conn)
 {
 	struct hci_dev *hdev = conn->hdev;
 	struct hci_cp_le_create_conn cp;
@@ -55,12 +55,12 @@
 	hci_send_cmd(hdev, HCI_OP_LE_CREATE_CONN, sizeof(cp), &cp);
 }
 
-static void hci_le_connect_cancel(struct hci_conn *conn)
+static void hci_le_create_connection_cancel(struct hci_conn *conn)
 {
 	hci_send_cmd(conn->hdev, HCI_OP_LE_CREATE_CONN_CANCEL, 0, NULL);
 }
 
-void hci_acl_connect(struct hci_conn *conn)
+static void hci_acl_create_connection(struct hci_conn *conn)
 {
 	struct hci_dev *hdev = conn->hdev;
 	struct inquiry_entry *ie;
@@ -104,7 +104,7 @@
 	hci_send_cmd(hdev, HCI_OP_CREATE_CONN, sizeof(cp), &cp);
 }
 
-static void hci_acl_connect_cancel(struct hci_conn *conn)
+static void hci_acl_create_connection_cancel(struct hci_conn *conn)
 {
 	struct hci_cp_create_conn_cancel cp;
 
@@ -130,7 +130,7 @@
 	hci_send_cmd(conn->hdev, HCI_OP_DISCONNECT, sizeof(cp), &cp);
 }
 
-void hci_add_sco(struct hci_conn *conn, __u16 handle)
+static void hci_add_sco(struct hci_conn *conn, __u16 handle)
 {
 	struct hci_dev *hdev = conn->hdev;
 	struct hci_cp_add_sco cp;
@@ -246,9 +246,9 @@
 	case BT_CONNECT2:
 		if (conn->out) {
 			if (conn->type == ACL_LINK)
-				hci_acl_connect_cancel(conn);
+				hci_acl_create_connection_cancel(conn);
 			else if (conn->type == LE_LINK)
-				hci_le_connect_cancel(conn);
+				hci_le_create_connection_cancel(conn);
 		}
 		break;
 	case BT_CONFIG:
@@ -471,41 +471,38 @@
 }
 EXPORT_SYMBOL(hci_get_route);
 
-/* Create SCO, ACL or LE connection.
- * Device _must_ be locked */
-struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst,
-			     __u8 dst_type, __u8 sec_level, __u8 auth_type)
+static struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
+				    u8 dst_type, u8 sec_level, u8 auth_type)
 {
-	struct hci_conn *acl;
-	struct hci_conn *sco;
 	struct hci_conn *le;
 
-	BT_DBG("%s dst %s", hdev->name, batostr(dst));
+	le = hci_conn_hash_lookup_ba(hdev, LE_LINK, dst);
+	if (!le) {
+		le = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT);
+		if (le)
+			return ERR_PTR(-EBUSY);
 
-	if (type == LE_LINK) {
-		le = hci_conn_hash_lookup_ba(hdev, LE_LINK, dst);
-		if (!le) {
-			le = hci_conn_hash_lookup_state(hdev, LE_LINK,
-							BT_CONNECT);
-			if (le)
-				return ERR_PTR(-EBUSY);
+		le = hci_conn_add(hdev, LE_LINK, dst);
+		if (!le)
+			return ERR_PTR(-ENOMEM);
 
-			le = hci_conn_add(hdev, LE_LINK, dst);
-			if (!le)
-				return ERR_PTR(-ENOMEM);
-
-			le->dst_type = bdaddr_to_le(dst_type);
-			hci_le_connect(le);
-		}
-
-		le->pending_sec_level = sec_level;
-		le->auth_type = auth_type;
-
-		hci_conn_hold(le);
-
-		return le;
+		le->dst_type = bdaddr_to_le(dst_type);
+		hci_le_create_connection(le);
 	}
 
+	le->pending_sec_level = sec_level;
+	le->auth_type = auth_type;
+
+	hci_conn_hold(le);
+
+	return le;
+}
+
+static struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst,
+						u8 sec_level, u8 auth_type)
+{
+	struct hci_conn *acl;
+
 	acl = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
 	if (!acl) {
 		acl = hci_conn_add(hdev, ACL_LINK, dst);
@@ -519,10 +516,20 @@
 		acl->sec_level = BT_SECURITY_LOW;
 		acl->pending_sec_level = sec_level;
 		acl->auth_type = auth_type;
-		hci_acl_connect(acl);
+		hci_acl_create_connection(acl);
 	}
 
-	if (type == ACL_LINK)
+	return acl;
+}
+
+static struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type,
+				bdaddr_t *dst, u8 sec_level, u8 auth_type)
+{
+	struct hci_conn *acl;
+	struct hci_conn *sco;
+
+	acl = hci_connect_acl(hdev, dst, sec_level, auth_type);
+	if (IS_ERR(acl))
 		return acl;
 
 	sco = hci_conn_hash_lookup_ba(hdev, type, dst);
@@ -556,6 +563,25 @@
 	return sco;
 }
 
+/* Create SCO, ACL or LE connection. */
+struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst,
+			     __u8 dst_type, __u8 sec_level, __u8 auth_type)
+{
+	BT_DBG("%s dst %s type 0x%x", hdev->name, batostr(dst), type);
+
+	switch (type) {
+	case LE_LINK:
+		return hci_connect_le(hdev, dst, dst_type, sec_level, auth_type);
+	case ACL_LINK:
+		return hci_connect_acl(hdev, dst, sec_level, auth_type);
+	case SCO_LINK:
+	case ESCO_LINK:
+		return hci_connect_sco(hdev, type, dst, sec_level, auth_type);
+	}
+
+	return ERR_PTR(-EINVAL);
+}
+
 /* Check link security requirement */
 int hci_conn_check_link_mode(struct hci_conn *conn)
 {
@@ -775,7 +801,7 @@
 
 	conn = hci_conn_hash_lookup_state(hdev, ACL_LINK, BT_CONNECT2);
 	if (conn)
-		hci_acl_connect(conn);
+		hci_acl_create_connection(conn);
 
 	hci_dev_unlock(hdev);
 }
@@ -913,7 +939,7 @@
 	return chan;
 }
 
-int hci_chan_del(struct hci_chan *chan)
+void hci_chan_del(struct hci_chan *chan)
 {
 	struct hci_conn *conn = chan->conn;
 	struct hci_dev *hdev = conn->hdev;
@@ -926,8 +952,6 @@
 
 	skb_queue_purge(&chan->data_q);
 	kfree(chan);
-
-	return 0;
 }
 
 void hci_chan_list_flush(struct hci_conn *conn)
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index fa974a1..e407051 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -231,6 +231,9 @@
 
 	/* Read Local AMP Info */
 	hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_INFO, 0, NULL);
+
+	/* Read Data Blk size */
+	hci_send_cmd(hdev, HCI_OP_READ_DATA_BLOCK_SIZE, 0, NULL);
 }
 
 static void hci_init_req(struct hci_dev *hdev, unsigned long opt)
@@ -268,7 +271,6 @@
 		BT_ERR("Unknown device type %d", hdev->dev_type);
 		break;
 	}
-
 }
 
 static void hci_le_init_req(struct hci_dev *hdev, unsigned long opt)
@@ -1652,6 +1654,7 @@
 	INIT_LIST_HEAD(&hdev->link_keys);
 	INIT_LIST_HEAD(&hdev->long_term_keys);
 	INIT_LIST_HEAD(&hdev->remote_oob_data);
+	INIT_LIST_HEAD(&hdev->conn_hash.list);
 
 	INIT_WORK(&hdev->rx_work, hci_rx_work);
 	INIT_WORK(&hdev->cmd_work, hci_cmd_work);
@@ -1674,7 +1677,6 @@
 
 	hci_init_sysfs(hdev);
 	discovery_init(hdev);
-	hci_conn_hash_init(hdev);
 
 	return hdev;
 }
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 4fd2cf3..2022b43 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -29,6 +29,7 @@
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/mgmt.h>
 
 /* Handle HCI Event packets */
 
@@ -303,7 +304,7 @@
 
 	hci_dev_lock(hdev);
 
-	if (status != 0) {
+	if (status) {
 		mgmt_write_scan_failed(hdev, param, status);
 		hdev->discov_timeout = 0;
 		goto done;
@@ -925,7 +926,7 @@
 	if (test_bit(HCI_MGMT, &hdev->dev_flags))
 		mgmt_pin_code_reply_complete(hdev, &rp->bdaddr, rp->status);
 
-	if (rp->status != 0)
+	if (rp->status)
 		goto unlock;
 
 	cp = hci_sent_cmd_data(hdev, HCI_OP_PIN_CODE_REPLY);
@@ -1891,6 +1892,22 @@
 	}
 }
 
+static u8 hci_to_mgmt_reason(u8 err)
+{
+	switch (err) {
+	case HCI_ERROR_CONNECTION_TIMEOUT:
+		return MGMT_DEV_DISCONN_TIMEOUT;
+	case HCI_ERROR_REMOTE_USER_TERM:
+	case HCI_ERROR_REMOTE_LOW_RESOURCES:
+	case HCI_ERROR_REMOTE_POWER_OFF:
+		return MGMT_DEV_DISCONN_REMOTE;
+	case HCI_ERROR_LOCAL_HOST_TERM:
+		return MGMT_DEV_DISCONN_LOCAL_HOST;
+	default:
+		return MGMT_DEV_DISCONN_UNKNOWN;
+	}
+}
+
 static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
 {
 	struct hci_ev_disconn_complete *ev = (void *) skb->data;
@@ -1909,12 +1926,15 @@
 
 	if (test_and_clear_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags) &&
 	    (conn->type == ACL_LINK || conn->type == LE_LINK)) {
-		if (ev->status != 0)
+		if (ev->status) {
 			mgmt_disconnect_failed(hdev, &conn->dst, conn->type,
 					       conn->dst_type, ev->status);
-		else
+		} else {
+			u8 reason = hci_to_mgmt_reason(ev->reason);
+
 			mgmt_device_disconnected(hdev, &conn->dst, conn->type,
-						 conn->dst_type);
+						 conn->dst_type, reason);
+		}
 	}
 
 	if (ev->status == 0) {
@@ -3259,6 +3279,65 @@
 		mgmt_user_passkey_request(hdev, &ev->bdaddr, ACL_LINK, 0);
 }
 
+static void hci_user_passkey_notify_evt(struct hci_dev *hdev,
+					struct sk_buff *skb)
+{
+	struct hci_ev_user_passkey_notify *ev = (void *) skb->data;
+	struct hci_conn *conn;
+
+	BT_DBG("%s", hdev->name);
+
+	conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
+	if (!conn)
+		return;
+
+	conn->passkey_notify = __le32_to_cpu(ev->passkey);
+	conn->passkey_entered = 0;
+
+	if (test_bit(HCI_MGMT, &hdev->dev_flags))
+		mgmt_user_passkey_notify(hdev, &conn->dst, conn->type,
+					 conn->dst_type, conn->passkey_notify,
+					 conn->passkey_entered);
+}
+
+static void hci_keypress_notify_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	struct hci_ev_keypress_notify *ev = (void *) skb->data;
+	struct hci_conn *conn;
+
+	BT_DBG("%s", hdev->name);
+
+	conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
+	if (!conn)
+		return;
+
+	switch (ev->type) {
+	case HCI_KEYPRESS_STARTED:
+		conn->passkey_entered = 0;
+		return;
+
+	case HCI_KEYPRESS_ENTERED:
+		conn->passkey_entered++;
+		break;
+
+	case HCI_KEYPRESS_ERASED:
+		conn->passkey_entered--;
+		break;
+
+	case HCI_KEYPRESS_CLEARED:
+		conn->passkey_entered = 0;
+		break;
+
+	case HCI_KEYPRESS_COMPLETED:
+		return;
+	}
+
+	if (test_bit(HCI_MGMT, &hdev->dev_flags))
+		mgmt_user_passkey_notify(hdev, &conn->dst, conn->type,
+					 conn->dst_type, conn->passkey_notify,
+					 conn->passkey_entered);
+}
+
 static void hci_simple_pair_complete_evt(struct hci_dev *hdev,
 					 struct sk_buff *skb)
 {
@@ -3278,7 +3357,7 @@
 	 * initiated the authentication. A traditional auth_complete
 	 * event gets always produced as initiator and is also mapped to
 	 * the mgmt_auth_failed event */
-	if (!test_bit(HCI_CONN_AUTH_PEND, &conn->flags) && ev->status != 0)
+	if (!test_bit(HCI_CONN_AUTH_PEND, &conn->flags) && ev->status)
 		mgmt_auth_failed(hdev, &conn->dst, conn->type, conn->dst_type,
 				 ev->status);
 
@@ -3623,6 +3702,14 @@
 		hci_user_passkey_request_evt(hdev, skb);
 		break;
 
+	case HCI_EV_USER_PASSKEY_NOTIFY:
+		hci_user_passkey_notify_evt(hdev, skb);
+		break;
+
+	case HCI_EV_KEYPRESS_NOTIFY:
+		hci_keypress_notify_evt(hdev, skb);
+		break;
+
 	case HCI_EV_SIMPLE_PAIR_COMPLETE:
 		hci_simple_pair_complete_evt(hdev, skb);
 		break;
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index e0abaf3..7a59e92 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -406,7 +406,7 @@
 
 	chan->state = BT_OPEN;
 
-	atomic_set(&chan->refcnt, 1);
+	kref_init(&chan->kref);
 
 	/* This flag is cleared in l2cap_chan_ready() */
 	set_bit(CONF_NOT_COMPLETE, &chan->conf_state);
@@ -416,8 +416,10 @@
 	return chan;
 }
 
-static void l2cap_chan_destroy(struct l2cap_chan *chan)
+static void l2cap_chan_destroy(struct kref *kref)
 {
+	struct l2cap_chan *chan = container_of(kref, struct l2cap_chan, kref);
+
 	BT_DBG("chan %p", chan);
 
 	write_lock(&chan_list_lock);
@@ -429,17 +431,16 @@
 
 void l2cap_chan_hold(struct l2cap_chan *c)
 {
-	BT_DBG("chan %p orig refcnt %d", c, atomic_read(&c->refcnt));
+	BT_DBG("chan %p orig refcnt %d", c, atomic_read(&c->kref.refcount));
 
-	atomic_inc(&c->refcnt);
+	kref_get(&c->kref);
 }
 
 void l2cap_chan_put(struct l2cap_chan *c)
 {
-	BT_DBG("chan %p orig refcnt %d", c, atomic_read(&c->refcnt));
+	BT_DBG("chan %p orig refcnt %d", c, atomic_read(&c->kref.refcount));
 
-	if (atomic_dec_and_test(&c->refcnt))
-		l2cap_chan_destroy(c);
+	kref_put(&c->kref, l2cap_chan_destroy);
 }
 
 void l2cap_chan_set_defaults(struct l2cap_chan *chan)
@@ -1448,7 +1449,7 @@
 	int err;
 
 	BT_DBG("%s -> %s (type %u) psm 0x%2.2x", batostr(src), batostr(dst),
-	       dst_type, __le16_to_cpu(chan->psm));
+	       dst_type, __le16_to_cpu(psm));
 
 	hdev = hci_get_route(dst, src);
 	if (!hdev)
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index a3329cb..8934343 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -35,7 +35,7 @@
 bool enable_hs;
 
 #define MGMT_VERSION	1
-#define MGMT_REVISION	1
+#define MGMT_REVISION	2
 
 static const u16 mgmt_commands[] = {
 	MGMT_OP_READ_INDEX_LIST,
@@ -99,6 +99,7 @@
 	MGMT_EV_DEVICE_BLOCKED,
 	MGMT_EV_DEVICE_UNBLOCKED,
 	MGMT_EV_DEVICE_UNPAIRED,
+	MGMT_EV_PASSKEY_NOTIFY,
 };
 
 /*
@@ -3077,16 +3078,17 @@
 }
 
 int mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
-			     u8 link_type, u8 addr_type)
+			     u8 link_type, u8 addr_type, u8 reason)
 {
-	struct mgmt_addr_info ev;
+	struct mgmt_ev_device_disconnected ev;
 	struct sock *sk = NULL;
 	int err;
 
 	mgmt_pending_foreach(MGMT_OP_DISCONNECT, hdev, disconnect_rsp, &sk);
 
-	bacpy(&ev.bdaddr, bdaddr);
-	ev.type = link_to_bdaddr(link_type, addr_type);
+	bacpy(&ev.addr.bdaddr, bdaddr);
+	ev.addr.type = link_to_bdaddr(link_type, addr_type);
+	ev.reason = reason;
 
 	err = mgmt_event(MGMT_EV_DEVICE_DISCONNECTED, hdev, &ev, sizeof(ev),
 			 sk);
@@ -3275,6 +3277,22 @@
 					  MGMT_OP_USER_PASSKEY_NEG_REPLY);
 }
 
+int mgmt_user_passkey_notify(struct hci_dev *hdev, bdaddr_t *bdaddr,
+			     u8 link_type, u8 addr_type, u32 passkey,
+			     u8 entered)
+{
+	struct mgmt_ev_passkey_notify ev;
+
+	BT_DBG("%s", hdev->name);
+
+	bacpy(&ev.addr.bdaddr, bdaddr);
+	ev.addr.type = link_to_bdaddr(link_type, addr_type);
+	ev.passkey = __cpu_to_le32(passkey);
+	ev.entered = entered;
+
+	return mgmt_event(MGMT_EV_PASSKEY_NOTIFY, hdev, &ev, sizeof(ev), NULL);
+}
+
 int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
 		     u8 addr_type, u8 status)
 {
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
index d0deb3e..3195a63 100644
--- a/net/mac80211/agg-tx.c
+++ b/net/mac80211/agg-tx.c
@@ -869,7 +869,7 @@
 
 	} else {
 		___ieee80211_stop_tx_ba_session(sta, tid, WLAN_BACK_INITIATOR,
-						true);
+						false);
 	}
 
  out:
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 03fe6d1..05f3a31 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -20,7 +20,8 @@
 #include "rate.h"
 #include "mesh.h"
 
-static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy, char *name,
+static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy,
+						const char *name,
 						enum nl80211_iftype type,
 						u32 *flags,
 						struct vif_params *params)
@@ -170,6 +171,38 @@
 		}
 	}
 
+	switch (sdata->vif.type) {
+	case NL80211_IFTYPE_STATION:
+		if (sdata->u.mgd.mfp != IEEE80211_MFP_DISABLED)
+			key->conf.flags |= IEEE80211_KEY_FLAG_RX_MGMT;
+		break;
+	case NL80211_IFTYPE_AP:
+	case NL80211_IFTYPE_AP_VLAN:
+		/* Keys without a station are used for TX only */
+		if (key->sta && test_sta_flag(key->sta, WLAN_STA_MFP))
+			key->conf.flags |= IEEE80211_KEY_FLAG_RX_MGMT;
+		break;
+	case NL80211_IFTYPE_ADHOC:
+		/* no MFP (yet) */
+		break;
+	case NL80211_IFTYPE_MESH_POINT:
+#ifdef CONFIG_MAC80211_MESH
+		if (sdata->u.mesh.security != IEEE80211_MESH_SEC_NONE)
+			key->conf.flags |= IEEE80211_KEY_FLAG_RX_MGMT;
+		break;
+#endif
+	case NL80211_IFTYPE_WDS:
+	case NL80211_IFTYPE_MONITOR:
+	case NL80211_IFTYPE_P2P_DEVICE:
+	case NL80211_IFTYPE_UNSPECIFIED:
+	case NUM_NL80211_IFTYPES:
+	case NL80211_IFTYPE_P2P_CLIENT:
+	case NL80211_IFTYPE_P2P_GO:
+		/* shouldn't happen */
+		WARN_ON_ONCE(1);
+		break;
+	}
+
 	err = ieee80211_key_link(key, sdata, sta);
 	if (err)
 		ieee80211_key_free(sdata->local, key);
@@ -2038,9 +2071,7 @@
 	 */
 	if (!sdata->u.mgd.associated ||
 	    sdata->vif.bss_conf.channel_type == NL80211_CHAN_NO_HT) {
-		mutex_lock(&sdata->local->iflist_mtx);
 		ieee80211_recalc_smps(sdata->local);
-		mutex_unlock(&sdata->local->iflist_mtx);
 		return 0;
 	}
 
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index f0f87e5..0bfc914 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -68,16 +68,14 @@
 	return mode;
 }
 
-bool ieee80211_set_channel_type(struct ieee80211_local *local,
-				struct ieee80211_sub_if_data *sdata,
-				enum nl80211_channel_type chantype)
+static enum nl80211_channel_type
+ieee80211_get_superchan(struct ieee80211_local *local,
+			struct ieee80211_sub_if_data *sdata)
 {
-	struct ieee80211_sub_if_data *tmp;
 	enum nl80211_channel_type superchan = NL80211_CHAN_NO_HT;
-	bool result;
+	struct ieee80211_sub_if_data *tmp;
 
 	mutex_lock(&local->iflist_mtx);
-
 	list_for_each_entry(tmp, &local->interfaces, list) {
 		if (tmp == sdata)
 			continue;
@@ -103,39 +101,70 @@
 			break;
 		}
 	}
+	mutex_unlock(&local->iflist_mtx);
 
-	switch (superchan) {
+	return superchan;
+}
+
+static bool
+ieee80211_channel_types_are_compatible(enum nl80211_channel_type chantype1,
+				       enum nl80211_channel_type chantype2,
+				       enum nl80211_channel_type *compat)
+{
+	/*
+	 * start out with chantype1 being the result,
+	 * overwriting later if needed
+	 */
+	if (compat)
+		*compat = chantype1;
+
+	switch (chantype1) {
 	case NL80211_CHAN_NO_HT:
+		if (compat)
+			*compat = chantype2;
+		break;
 	case NL80211_CHAN_HT20:
 		/*
 		 * allow any change that doesn't go to no-HT
 		 * (if it already is no-HT no change is needed)
 		 */
-		if (chantype == NL80211_CHAN_NO_HT)
+		if (chantype2 == NL80211_CHAN_NO_HT)
 			break;
-		superchan = chantype;
+		if (compat)
+			*compat = chantype2;
 		break;
 	case NL80211_CHAN_HT40PLUS:
 	case NL80211_CHAN_HT40MINUS:
 		/* allow smaller bandwidth and same */
-		if (chantype == NL80211_CHAN_NO_HT)
+		if (chantype2 == NL80211_CHAN_NO_HT)
 			break;
-		if (chantype == NL80211_CHAN_HT20)
+		if (chantype2 == NL80211_CHAN_HT20)
 			break;
-		if (superchan == chantype)
+		if (chantype2 == chantype1)
 			break;
-		result = false;
-		goto out;
+		return false;
 	}
 
-	local->_oper_channel_type = superchan;
+	return true;
+}
+
+bool ieee80211_set_channel_type(struct ieee80211_local *local,
+				struct ieee80211_sub_if_data *sdata,
+				enum nl80211_channel_type chantype)
+{
+	enum nl80211_channel_type superchan;
+	enum nl80211_channel_type compatchan;
+
+	superchan = ieee80211_get_superchan(local, sdata);
+	if (!ieee80211_channel_types_are_compatible(superchan, chantype,
+						    &compatchan))
+		return false;
+
+	local->_oper_channel_type = compatchan;
 
 	if (sdata)
 		sdata->vif.bss_conf.channel_type = chantype;
 
-	result = true;
- out:
-	mutex_unlock(&local->iflist_mtx);
+	return true;
 
-	return result;
 }
diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
index 97173f8..466f4b4 100644
--- a/net/mac80211/debugfs.c
+++ b/net/mac80211/debugfs.c
@@ -70,6 +70,7 @@
 DEBUGFS_READONLY_FILE(rate_ctrl_alg, "%s",
 	local->rate_ctrl ? local->rate_ctrl->ops->name : "hw/driver");
 
+#ifdef CONFIG_PM
 static ssize_t reset_write(struct file *file, const char __user *user_buf,
 			   size_t count, loff_t *ppos)
 {
@@ -88,6 +89,7 @@
 	.open = simple_open,
 	.llseek = noop_llseek,
 };
+#endif
 
 static ssize_t hwflags_read(struct file *file, char __user *user_buf,
 			    size_t count, loff_t *ppos)
@@ -245,7 +247,9 @@
 	DEBUGFS_ADD(total_ps_buffered);
 	DEBUGFS_ADD(wep_iv);
 	DEBUGFS_ADD(queues);
+#ifdef CONFIG_PM
 	DEBUGFS_ADD_MODE(reset, 0200);
+#endif
 	DEBUGFS_ADD(hwflags);
 	DEBUGFS_ADD(user_power);
 	DEBUGFS_ADD(power);
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index a9d9328..5f3620f 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -278,7 +278,7 @@
 	if (auth && !sdata->u.ibss.auth_frame_registrations) {
 		ibss_dbg(sdata,
 			 "TX Auth SA=%pM DA=%pM BSSID=%pM (auth_transaction=1)\n",
-			 sdata->vif.addr, sdata->u.ibss.bssid, addr);
+			 sdata->vif.addr, addr, sdata->u.ibss.bssid);
 		ieee80211_send_auth(sdata, 1, WLAN_AUTH_OPEN, NULL, 0,
 				    addr, sdata->u.ibss.bssid, NULL, 0, 0);
 	}
@@ -332,11 +332,27 @@
 	return ieee80211_ibss_finish_sta(sta, auth);
 }
 
+static void ieee80211_rx_mgmt_deauth_ibss(struct ieee80211_sub_if_data *sdata,
+					  struct ieee80211_mgmt *mgmt,
+					  size_t len)
+{
+	u16 reason = le16_to_cpu(mgmt->u.deauth.reason_code);
+
+	if (len < IEEE80211_DEAUTH_FRAME_LEN)
+		return;
+
+	ibss_dbg(sdata, "RX DeAuth SA=%pM DA=%pM BSSID=%pM (reason: %d)\n",
+		 mgmt->sa, mgmt->da, mgmt->bssid, reason);
+	sta_info_destroy_addr(sdata, mgmt->sa);
+}
+
 static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata,
 					struct ieee80211_mgmt *mgmt,
 					size_t len)
 {
 	u16 auth_alg, auth_transaction;
+	struct sta_info *sta;
+	u8 deauth_frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
 
 	lockdep_assert_held(&sdata->u.ibss.mtx);
 
@@ -352,10 +368,22 @@
 		 "RX Auth SA=%pM DA=%pM BSSID=%pM (auth_transaction=%d)\n",
 		 mgmt->sa, mgmt->da, mgmt->bssid, auth_transaction);
 	sta_info_destroy_addr(sdata, mgmt->sa);
-	ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, 0, false);
+	sta = ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, 0, false);
 	rcu_read_unlock();
 
 	/*
+	 * if we have any problem in allocating the new station, we reply with a
+	 * DEAUTH frame to tell the other end that we had a problem
+	 */
+	if (!sta) {
+		ieee80211_send_deauth_disassoc(sdata, sdata->u.ibss.bssid,
+					       IEEE80211_STYPE_DEAUTH,
+					       WLAN_REASON_UNSPECIFIED, true,
+					       deauth_frame_buf);
+		return;
+	}
+
+	/*
 	 * IEEE 802.11 standard does not require authentication in IBSS
 	 * networks and most implementations do not seem to use it.
 	 * However, try to reply to authentication attempts if someone
@@ -902,6 +930,9 @@
 	case IEEE80211_STYPE_AUTH:
 		ieee80211_rx_mgmt_auth_ibss(sdata, mgmt, skb->len);
 		break;
+	case IEEE80211_STYPE_DEAUTH:
+		ieee80211_rx_mgmt_deauth_ibss(sdata, mgmt, skb->len);
+		break;
 	}
 
  mgmt_out:
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 204bfed..8c80455 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -68,6 +68,8 @@
 #define IEEE80211_DEFAULT_MAX_SP_LEN		\
 	IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL
 
+#define IEEE80211_DEAUTH_FRAME_LEN	(24 /* hdr */ + 2 /* reason */)
+
 struct ieee80211_fragment_entry {
 	unsigned long first_frag_time;
 	unsigned int seq;
@@ -411,6 +413,7 @@
 	struct work_struct monitor_work;
 	struct work_struct chswitch_work;
 	struct work_struct beacon_connection_loss_work;
+	struct work_struct csa_connection_drop_work;
 
 	unsigned long beacon_timeout;
 	unsigned long probe_timeout;
@@ -970,7 +973,6 @@
 	int scan_channel_idx;
 	int scan_ies_len;
 
-	struct ieee80211_sched_scan_ies sched_scan_ies;
 	struct work_struct sched_scan_stopped_work;
 	struct ieee80211_sub_if_data __rcu *sched_scan_sdata;
 
@@ -1057,7 +1059,7 @@
 	bool disable_dynamic_ps;
 
 	int user_power_level; /* in dBm */
-	int power_constr_level; /* in dBm */
+	int ap_power_level; /* in dBm */
 
 	enum ieee80211_smps_mode smps_mode;
 
@@ -1165,7 +1167,6 @@
 	u8 prep_len;
 	u8 perr_len;
 	u8 country_elem_len;
-	u8 pwr_constr_elem_len;
 	u8 quiet_elem_len;
 	u8 num_of_quiet_elem;	/* can be more the one */
 	u8 timeout_int_len;
@@ -1367,7 +1368,6 @@
 int ieee80211_reconfig(struct ieee80211_local *local);
 void ieee80211_stop_device(struct ieee80211_local *local);
 
-#ifdef CONFIG_PM
 int __ieee80211_suspend(struct ieee80211_hw *hw,
 			struct cfg80211_wowlan *wowlan);
 
@@ -1381,18 +1381,6 @@
 
 	return ieee80211_reconfig(hw_to_local(hw));
 }
-#else
-static inline int __ieee80211_suspend(struct ieee80211_hw *hw,
-				      struct cfg80211_wowlan *wowlan)
-{
-	return 0;
-}
-
-static inline int __ieee80211_resume(struct ieee80211_hw *hw)
-{
-	return 0;
-}
-#endif
 
 /* utility functions/constants */
 extern void *mac80211_wiphy_privid; /* for wiphy privid */
@@ -1459,6 +1447,9 @@
 			 u16 transaction, u16 auth_alg,
 			 u8 *extra, size_t extra_len, const u8 *bssid,
 			 const u8 *da, const u8 *key, u8 key_len, u8 key_idx);
+void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
+				    const u8 *bssid, u16 stype, u16 reason,
+				    bool send_frame, u8 *frame_buf);
 int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
 			     const u8 *ie, size_t ie_len,
 			     enum ieee80211_band band, u32 rate_mask,
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index d747da5..6f8a73c 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -793,11 +793,20 @@
 		flush_work(&sdata->work);
 		/*
 		 * When we get here, the interface is marked down.
-		 * Call synchronize_rcu() to wait for the RX path
+		 * Call rcu_barrier() to wait both for the RX path
 		 * should it be using the interface and enqueuing
-		 * frames at this very time on another CPU.
+		 * frames at this very time on another CPU, and
+		 * for the sta free call_rcu callbacks.
 		 */
-		synchronize_rcu();
+		rcu_barrier();
+
+		/*
+		 * free_sta_rcu() enqueues a work for the actual
+		 * sta cleanup, so we need to flush it while
+		 * sdata is still valid.
+		 */
+		flush_workqueue(local->workqueue);
+
 		skb_queue_purge(&sdata->skb_queue);
 
 		/*
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index 7ae678b..d27e61a 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -402,7 +402,7 @@
 	 * Synchronize so the TX path can no longer be using
 	 * this key before we free/remove it.
 	 */
-	synchronize_rcu();
+	synchronize_net();
 
 	if (key->local)
 		ieee80211_key_disable_hw_accel(key);
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index bd75293..c80c44903 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -150,13 +150,11 @@
 
 	if (test_bit(SCAN_SW_SCANNING, &local->scanning) ||
 	    test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning) ||
-	    test_bit(SCAN_HW_SCANNING, &local->scanning))
+	    test_bit(SCAN_HW_SCANNING, &local->scanning) ||
+	    !local->ap_power_level)
 		power = chan->max_power;
 	else
-		power = local->power_constr_level ?
-			min(chan->max_power,
-				(chan->max_reg_power  - local->power_constr_level)) :
-			chan->max_power;
+		power = min(chan->max_power, local->ap_power_level);
 
 	if (local->user_power_level >= 0)
 		power = min(power, local->user_power_level);
@@ -366,9 +364,7 @@
 	struct ieee80211_local *local =
 		container_of(work, struct ieee80211_local, recalc_smps);
 
-	mutex_lock(&local->iflist_mtx);
 	ieee80211_recalc_smps(local);
-	mutex_unlock(&local->iflist_mtx);
 }
 
 #ifdef CONFIG_INET
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index 9d7ad36..3ab34d8 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -537,7 +537,8 @@
 	spin_lock_bh(&sta->lock);
 	get_random_bytes(&llid, 2);
 	sta->llid = llid;
-	if (sta->plink_state != NL80211_PLINK_LISTEN) {
+	if (sta->plink_state != NL80211_PLINK_LISTEN &&
+	    sta->plink_state != NL80211_PLINK_BLOCKED) {
 		spin_unlock_bh(&sta->lock);
 		return -EBUSY;
 	}
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 5d77650..e714ed8 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -88,8 +88,6 @@
 #define TMR_RUNNING_TIMER	0
 #define TMR_RUNNING_CHANSW	1
 
-#define DEAUTH_DISASSOC_LEN	(24 /* hdr */ + 2 /* reason */)
-
 /*
  * All cfg80211 functions have to be called outside a locked
  * section so that they can acquire a lock themselves... This
@@ -574,46 +572,6 @@
 	ieee80211_tx_skb(sdata, skb);
 }
 
-static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
-					   const u8 *bssid, u16 stype,
-					   u16 reason, bool send_frame,
-					   u8 *frame_buf)
-{
-	struct ieee80211_local *local = sdata->local;
-	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-	struct sk_buff *skb;
-	struct ieee80211_mgmt *mgmt = (void *)frame_buf;
-
-	/* build frame */
-	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | stype);
-	mgmt->duration = 0; /* initialize only */
-	mgmt->seq_ctrl = 0; /* initialize only */
-	memcpy(mgmt->da, bssid, ETH_ALEN);
-	memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
-	memcpy(mgmt->bssid, bssid, ETH_ALEN);
-	/* u.deauth.reason_code == u.disassoc.reason_code */
-	mgmt->u.deauth.reason_code = cpu_to_le16(reason);
-
-	if (send_frame) {
-		skb = dev_alloc_skb(local->hw.extra_tx_headroom +
-				    DEAUTH_DISASSOC_LEN);
-		if (!skb)
-			return;
-
-		skb_reserve(skb, local->hw.extra_tx_headroom);
-
-		/* copy in frame */
-		memcpy(skb_put(skb, DEAUTH_DISASSOC_LEN),
-		       mgmt, DEAUTH_DISASSOC_LEN);
-
-		if (!(ifmgd->flags & IEEE80211_STA_MFP_ENABLED))
-			IEEE80211_SKB_CB(skb)->flags |=
-				IEEE80211_TX_INTFL_DONT_ENCRYPT;
-
-		ieee80211_tx_skb(sdata, skb);
-	}
-}
-
 void ieee80211_send_pspoll(struct ieee80211_local *local,
 			   struct ieee80211_sub_if_data *sdata)
 {
@@ -730,16 +688,13 @@
 
 	trace_api_chswitch_done(sdata, success);
 	if (!success) {
-		/*
-		 * If the channel switch was not successful, stay
-		 * around on the old channel. We currently lack
-		 * good handling of this situation, possibly we
-		 * should just drop the association.
-		 */
-		sdata->local->csa_channel = sdata->local->oper_channel;
+		sdata_info(sdata,
+			   "driver channel switch failed, disconnecting\n");
+		ieee80211_queue_work(&sdata->local->hw,
+				     &ifmgd->csa_connection_drop_work);
+	} else {
+		ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work);
 	}
-
-	ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work);
 }
 EXPORT_SYMBOL(ieee80211_chswitch_done);
 
@@ -784,8 +739,14 @@
 		return;
 
 	new_ch = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq);
-	if (!new_ch || new_ch->flags & IEEE80211_CHAN_DISABLED)
+	if (!new_ch || new_ch->flags & IEEE80211_CHAN_DISABLED) {
+		sdata_info(sdata,
+			   "AP %pM switches to unsupported channel (%d MHz), disconnecting\n",
+			   ifmgd->associated->bssid, new_freq);
+		ieee80211_queue_work(&sdata->local->hw,
+				     &ifmgd->csa_connection_drop_work);
 		return;
+	}
 
 	sdata->local->csa_channel = new_ch;
 
@@ -818,23 +779,71 @@
 }
 
 static void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
-					u16 capab_info, u8 *pwr_constr_elem,
-					u8 pwr_constr_elem_len)
+					struct ieee80211_channel *channel,
+					const u8 *country_ie, u8 country_ie_len,
+					const u8 *pwr_constr_elem)
 {
-	struct ieee80211_conf *conf = &sdata->local->hw.conf;
+	struct ieee80211_country_ie_triplet *triplet;
+	int chan = ieee80211_frequency_to_channel(channel->center_freq);
+	int i, chan_pwr, chan_increment, new_ap_level;
+	bool have_chan_pwr = false;
 
-	if (!(capab_info & WLAN_CAPABILITY_SPECTRUM_MGMT))
+	/* Invalid IE */
+	if (country_ie_len % 2 || country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN)
 		return;
 
-	/* Power constraint IE length should be 1 octet */
-	if (pwr_constr_elem_len != 1)
-		return;
+	triplet = (void *)(country_ie + 3);
+	country_ie_len -= 3;
 
-	if ((*pwr_constr_elem <= conf->channel->max_reg_power) &&
-	    (*pwr_constr_elem != sdata->local->power_constr_level)) {
-		sdata->local->power_constr_level = *pwr_constr_elem;
-		ieee80211_hw_config(sdata->local, 0);
+	switch (channel->band) {
+	default:
+		WARN_ON_ONCE(1);
+		/* fall through */
+	case IEEE80211_BAND_2GHZ:
+	case IEEE80211_BAND_60GHZ:
+		chan_increment = 1;
+		break;
+	case IEEE80211_BAND_5GHZ:
+		chan_increment = 4;
+		break;
 	}
+
+	/* find channel */
+	while (country_ie_len >= 3) {
+		u8 first_channel = triplet->chans.first_channel;
+
+		if (first_channel >= IEEE80211_COUNTRY_EXTENSION_ID)
+			goto next;
+
+		for (i = 0; i < triplet->chans.num_channels; i++) {
+			if (first_channel + i * chan_increment == chan) {
+				have_chan_pwr = true;
+				chan_pwr = triplet->chans.max_power;
+				break;
+			}
+		}
+		if (have_chan_pwr)
+			break;
+
+ next:
+		triplet++;
+		country_ie_len -= 3;
+	}
+
+	if (!have_chan_pwr)
+		return;
+
+	new_ap_level = max_t(int, 0, chan_pwr - *pwr_constr_elem);
+
+	if (sdata->local->ap_power_level == new_ap_level)
+		return;
+
+	sdata_info(sdata,
+		   "Limiting TX power to %d (%d - %d) dBm as advertised by %pM\n",
+		   new_ap_level, chan_pwr, *pwr_constr_elem,
+		   sdata->u.mgd.bssid);
+	sdata->local->ap_power_level = new_ap_level;
+	ieee80211_hw_config(sdata->local, 0);
 }
 
 void ieee80211_enable_dyn_ps(struct ieee80211_vif *vif)
@@ -1339,9 +1348,9 @@
 
 	mutex_lock(&local->iflist_mtx);
 	ieee80211_recalc_ps(local, -1);
-	ieee80211_recalc_smps(local);
 	mutex_unlock(&local->iflist_mtx);
 
+	ieee80211_recalc_smps(local);
 	ieee80211_recalc_ps_vif(sdata);
 
 	netif_tx_start_all_queues(sdata->dev);
@@ -1390,7 +1399,7 @@
 	sta = sta_info_get(sdata, ifmgd->bssid);
 	if (sta) {
 		set_sta_flag(sta, WLAN_STA_BLOCK_BA);
-		ieee80211_sta_tear_down_BA_sessions(sta, tx);
+		ieee80211_sta_tear_down_BA_sessions(sta, false);
 	}
 	mutex_unlock(&local->sta_mtx);
 
@@ -1438,7 +1447,7 @@
 	memset(&ifmgd->ht_capa, 0, sizeof(ifmgd->ht_capa));
 	memset(&ifmgd->ht_capa_mask, 0, sizeof(ifmgd->ht_capa_mask));
 
-	local->power_constr_level = 0;
+	local->ap_power_level = 0;
 
 	del_timer_sync(&local->dynamic_ps_timer);
 	cancel_work_sync(&local->dynamic_ps_enable_work);
@@ -1692,11 +1701,12 @@
 }
 EXPORT_SYMBOL(ieee80211_ap_probereq_get);
 
-static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata)
+static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata,
+				   bool transmit_frame)
 {
 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 	struct ieee80211_local *local = sdata->local;
-	u8 frame_buf[DEAUTH_DISASSOC_LEN];
+	u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
 
 	mutex_lock(&ifmgd->mtx);
 	if (!ifmgd->associated) {
@@ -1704,19 +1714,17 @@
 		return;
 	}
 
-	sdata_info(sdata, "Connection to AP %pM lost\n",
-		   ifmgd->associated->bssid);
-
 	ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
 			       WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
-			       false, frame_buf);
+			       transmit_frame, frame_buf);
+	ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
 	mutex_unlock(&ifmgd->mtx);
 
 	/*
 	 * must be outside lock due to cfg80211,
 	 * but that's not a problem.
 	 */
-	cfg80211_send_deauth(sdata->dev, frame_buf, DEAUTH_DISASSOC_LEN);
+	cfg80211_send_deauth(sdata->dev, frame_buf, IEEE80211_DEAUTH_FRAME_LEN);
 
 	mutex_lock(&local->mtx);
 	ieee80211_recalc_idle(local);
@@ -1739,10 +1747,24 @@
 		rcu_read_unlock();
 	}
 
-	if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
-		__ieee80211_connection_loss(sdata);
-	else
+	if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR) {
+		sdata_info(sdata, "Connection to AP %pM lost\n",
+			   ifmgd->bssid);
+		__ieee80211_disconnect(sdata, false);
+	} else {
 		ieee80211_mgd_probe_ap(sdata, true);
+	}
+}
+
+static void ieee80211_csa_connection_drop_work(struct work_struct *work)
+{
+	struct ieee80211_sub_if_data *sdata =
+		container_of(work, struct ieee80211_sub_if_data,
+			     u.mgd.csa_connection_drop_work);
+
+	ieee80211_wake_queues_by_reason(&sdata->local->hw,
+					IEEE80211_QUEUE_STOP_REASON_CSA);
+	__ieee80211_disconnect(sdata, true);
 }
 
 void ieee80211_beacon_loss(struct ieee80211_vif *vif)
@@ -2530,15 +2552,13 @@
 						  bssid, true);
 	}
 
-	/* Note: country IE parsing is done for us by cfg80211 */
-	if (elems.country_elem) {
-		/* TODO: IBSS also needs this */
-		if (elems.pwr_constr_elem)
-			ieee80211_handle_pwr_constr(sdata,
-				le16_to_cpu(mgmt->u.probe_resp.capab_info),
-				elems.pwr_constr_elem,
-				elems.pwr_constr_elem_len);
-	}
+	if (elems.country_elem && elems.pwr_constr_elem &&
+	    mgmt->u.probe_resp.capab_info &
+				cpu_to_le16(WLAN_CAPABILITY_SPECTRUM_MGMT))
+		ieee80211_handle_pwr_constr(sdata, local->oper_channel,
+					    elems.country_elem,
+					    elems.country_elem_len,
+					    elems.pwr_constr_elem);
 
 	ieee80211_bss_info_change_notify(sdata, changed);
 }
@@ -2635,7 +2655,7 @@
 {
 	struct ieee80211_local *local = sdata->local;
 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-	u8 frame_buf[DEAUTH_DISASSOC_LEN];
+	u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
 
 	ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, reason,
 			       false, frame_buf);
@@ -2645,7 +2665,7 @@
 	 * must be outside lock due to cfg80211,
 	 * but that's not a problem.
 	 */
-	cfg80211_send_deauth(sdata->dev, frame_buf, DEAUTH_DISASSOC_LEN);
+	cfg80211_send_deauth(sdata->dev, frame_buf, IEEE80211_DEAUTH_FRAME_LEN);
 
 	mutex_lock(&local->mtx);
 	ieee80211_recalc_idle(local);
@@ -2929,6 +2949,7 @@
 
 	cancel_work_sync(&ifmgd->monitor_work);
 	cancel_work_sync(&ifmgd->beacon_connection_loss_work);
+	cancel_work_sync(&ifmgd->csa_connection_drop_work);
 	if (del_timer_sync(&ifmgd->timer))
 		set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running);
 
@@ -2985,6 +3006,8 @@
 	INIT_WORK(&ifmgd->chswitch_work, ieee80211_chswitch_work);
 	INIT_WORK(&ifmgd->beacon_connection_loss_work,
 		  ieee80211_beacon_connection_loss_work);
+	INIT_WORK(&ifmgd->csa_connection_drop_work,
+		  ieee80211_csa_connection_drop_work);
 	INIT_WORK(&ifmgd->request_smps_work, ieee80211_request_smps_work);
 	setup_timer(&ifmgd->timer, ieee80211_sta_timer,
 		    (unsigned long) sdata);
@@ -3525,7 +3548,7 @@
 			 struct cfg80211_deauth_request *req)
 {
 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-	u8 frame_buf[DEAUTH_DISASSOC_LEN];
+	u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
 
 	mutex_lock(&ifmgd->mtx);
 
@@ -3553,7 +3576,8 @@
 
 	mutex_unlock(&ifmgd->mtx);
 
-	__cfg80211_send_deauth(sdata->dev, frame_buf, DEAUTH_DISASSOC_LEN);
+	__cfg80211_send_deauth(sdata->dev, frame_buf,
+			       IEEE80211_DEAUTH_FRAME_LEN);
 
 	mutex_lock(&sdata->local->mtx);
 	ieee80211_recalc_idle(sdata->local);
@@ -3567,7 +3591,7 @@
 {
 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 	u8 bssid[ETH_ALEN];
-	u8 frame_buf[DEAUTH_DISASSOC_LEN];
+	u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
 
 	mutex_lock(&ifmgd->mtx);
 
@@ -3592,7 +3616,8 @@
 			       frame_buf);
 	mutex_unlock(&ifmgd->mtx);
 
-	__cfg80211_send_disassoc(sdata->dev, frame_buf, DEAUTH_DISASSOC_LEN);
+	__cfg80211_send_disassoc(sdata->dev, frame_buf,
+				 IEEE80211_DEAUTH_FRAME_LEN);
 
 	mutex_lock(&sdata->local->mtx);
 	ieee80211_recalc_idle(sdata->local);
diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c
index 507121d..83608ac 100644
--- a/net/mac80211/offchannel.c
+++ b/net/mac80211/offchannel.c
@@ -233,8 +233,7 @@
 			u32 dur = dep->duration;
 			dep->duration = dur - roc->duration;
 			roc->duration = dur;
-			list_del(&dep->list);
-			list_add(&dep->list, &roc->list);
+			list_move(&dep->list, &roc->list);
 		}
 	}
  out_unlock:
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index 740e414..c4cdbde 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -407,7 +407,7 @@
 	enum ieee80211_band band = local->hw.conf.channel->band;
 
 	sdata = rcu_dereference_protected(local->scan_sdata,
-					  lockdep_is_held(&local->mtx));;
+					  lockdep_is_held(&local->mtx));
 
 	for (i = 0; i < local->scan_req->n_ssids; i++)
 		ieee80211_send_probe_req(
@@ -917,6 +917,7 @@
 				       struct cfg80211_sched_scan_request *req)
 {
 	struct ieee80211_local *local = sdata->local;
+	struct ieee80211_sched_scan_ies sched_scan_ies;
 	int ret, i;
 
 	mutex_lock(&local->mtx);
@@ -935,33 +936,28 @@
 		if (!local->hw.wiphy->bands[i])
 			continue;
 
-		local->sched_scan_ies.ie[i] = kzalloc(2 +
-						      IEEE80211_MAX_SSID_LEN +
-						      local->scan_ies_len +
-						      req->ie_len,
-						      GFP_KERNEL);
-		if (!local->sched_scan_ies.ie[i]) {
+		sched_scan_ies.ie[i] = kzalloc(2 + IEEE80211_MAX_SSID_LEN +
+					       local->scan_ies_len +
+					       req->ie_len,
+					       GFP_KERNEL);
+		if (!sched_scan_ies.ie[i]) {
 			ret = -ENOMEM;
 			goto out_free;
 		}
 
-		local->sched_scan_ies.len[i] =
-			ieee80211_build_preq_ies(local,
-						 local->sched_scan_ies.ie[i],
+		sched_scan_ies.len[i] =
+			ieee80211_build_preq_ies(local, sched_scan_ies.ie[i],
 						 req->ie, req->ie_len, i,
 						 (u32) -1, 0);
 	}
 
-	ret = drv_sched_scan_start(local, sdata, req,
-				   &local->sched_scan_ies);
-	if (ret == 0) {
+	ret = drv_sched_scan_start(local, sdata, req, &sched_scan_ies);
+	if (ret == 0)
 		rcu_assign_pointer(local->sched_scan_sdata, sdata);
-		goto out;
-	}
 
 out_free:
 	while (i > 0)
-		kfree(local->sched_scan_ies.ie[--i]);
+		kfree(sched_scan_ies.ie[--i]);
 out:
 	mutex_unlock(&local->mtx);
 	return ret;
@@ -970,7 +966,7 @@
 int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata)
 {
 	struct ieee80211_local *local = sdata->local;
-	int ret = 0, i;
+	int ret = 0;
 
 	mutex_lock(&local->mtx);
 
@@ -979,12 +975,9 @@
 		goto out;
 	}
 
-	if (rcu_access_pointer(local->sched_scan_sdata)) {
-		for (i = 0; i < IEEE80211_NUM_BANDS; i++)
-			kfree(local->sched_scan_ies.ie[i]);
-
+	if (rcu_access_pointer(local->sched_scan_sdata))
 		drv_sched_scan_stop(local, sdata);
-	}
+
 out:
 	mutex_unlock(&local->mtx);
 
@@ -1006,7 +999,6 @@
 	struct ieee80211_local *local =
 		container_of(work, struct ieee80211_local,
 			     sched_scan_stopped_work);
-	int i;
 
 	mutex_lock(&local->mtx);
 
@@ -1015,9 +1007,6 @@
 		return;
 	}
 
-	for (i = 0; i < IEEE80211_NUM_BANDS; i++)
-		kfree(local->sched_scan_ies.ie[i]);
-
 	rcu_assign_pointer(local->sched_scan_sdata, NULL);
 
 	mutex_unlock(&local->mtx);
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 06fa75c..797dd36a 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -91,6 +91,70 @@
 	return -ENOENT;
 }
 
+static void free_sta_work(struct work_struct *wk)
+{
+	struct sta_info *sta = container_of(wk, struct sta_info, free_sta_wk);
+	int ac, i;
+	struct tid_ampdu_tx *tid_tx;
+	struct ieee80211_sub_if_data *sdata = sta->sdata;
+	struct ieee80211_local *local = sdata->local;
+
+	/*
+	 * At this point, when being called as call_rcu callback,
+	 * neither mac80211 nor the driver can reference this
+	 * sta struct any more except by still existing timers
+	 * associated with this station that we clean up below.
+	 */
+
+	if (test_sta_flag(sta, WLAN_STA_PS_STA)) {
+		BUG_ON(!sdata->bss);
+
+		clear_sta_flag(sta, WLAN_STA_PS_STA);
+
+		atomic_dec(&sdata->bss->num_sta_ps);
+		sta_info_recalc_tim(sta);
+	}
+
+	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+		local->total_ps_buffered -= skb_queue_len(&sta->ps_tx_buf[ac]);
+		__skb_queue_purge(&sta->ps_tx_buf[ac]);
+		__skb_queue_purge(&sta->tx_filtered[ac]);
+	}
+
+#ifdef CONFIG_MAC80211_MESH
+	if (ieee80211_vif_is_mesh(&sdata->vif)) {
+		mesh_accept_plinks_update(sdata);
+		mesh_plink_deactivate(sta);
+		del_timer_sync(&sta->plink_timer);
+	}
+#endif
+
+	cancel_work_sync(&sta->drv_unblock_wk);
+
+	/*
+	 * Destroy aggregation state here. It would be nice to wait for the
+	 * driver to finish aggregation stop and then clean up, but for now
+	 * drivers have to handle aggregation stop being requested, followed
+	 * directly by station destruction.
+	 */
+	for (i = 0; i < STA_TID_NUM; i++) {
+		tid_tx = rcu_dereference_raw(sta->ampdu_mlme.tid_tx[i]);
+		if (!tid_tx)
+			continue;
+		__skb_queue_purge(&tid_tx->pending);
+		kfree(tid_tx);
+	}
+
+	sta_info_free(local, sta);
+}
+
+static void free_sta_rcu(struct rcu_head *h)
+{
+	struct sta_info *sta = container_of(h, struct sta_info, rcu_head);
+
+	ieee80211_queue_work(&sta->local->hw, &sta->free_sta_wk);
+}
+
 /* protected by RCU */
 struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata,
 			      const u8 *addr)
@@ -241,6 +305,7 @@
 
 	spin_lock_init(&sta->lock);
 	INIT_WORK(&sta->drv_unblock_wk, sta_unblock);
+	INIT_WORK(&sta->free_sta_wk, free_sta_work);
 	INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work);
 	mutex_init(&sta->ampdu_mlme.mtx);
 
@@ -654,8 +719,7 @@
 {
 	struct ieee80211_local *local;
 	struct ieee80211_sub_if_data *sdata;
-	int ret, i, ac;
-	struct tid_ampdu_tx *tid_tx;
+	int ret, i;
 
 	might_sleep();
 
@@ -674,7 +738,7 @@
 	 * will be sufficient.
 	 */
 	set_sta_flag(sta, WLAN_STA_BLOCK_BA);
-	ieee80211_sta_tear_down_BA_sessions(sta, true);
+	ieee80211_sta_tear_down_BA_sessions(sta, false);
 
 	ret = sta_info_hash_del(local, sta);
 	if (ret)
@@ -711,65 +775,14 @@
 		WARN_ON_ONCE(ret != 0);
 	}
 
-	/*
-	 * At this point, after we wait for an RCU grace period,
-	 * neither mac80211 nor the driver can reference this
-	 * sta struct any more except by still existing timers
-	 * associated with this station that we clean up below.
-	 */
-	synchronize_rcu();
-
-	if (test_sta_flag(sta, WLAN_STA_PS_STA)) {
-		BUG_ON(!sdata->bss);
-
-		clear_sta_flag(sta, WLAN_STA_PS_STA);
-
-		atomic_dec(&sdata->bss->num_sta_ps);
-		sta_info_recalc_tim(sta);
-	}
-
-	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
-		local->total_ps_buffered -= skb_queue_len(&sta->ps_tx_buf[ac]);
-		__skb_queue_purge(&sta->ps_tx_buf[ac]);
-		__skb_queue_purge(&sta->tx_filtered[ac]);
-	}
-
-#ifdef CONFIG_MAC80211_MESH
-	if (ieee80211_vif_is_mesh(&sdata->vif))
-		mesh_accept_plinks_update(sdata);
-#endif
-
 	sta_dbg(sdata, "Removed STA %pM\n", sta->sta.addr);
 
-	cancel_work_sync(&sta->drv_unblock_wk);
-
 	cfg80211_del_sta(sdata->dev, sta->sta.addr, GFP_KERNEL);
 
 	rate_control_remove_sta_debugfs(sta);
 	ieee80211_sta_debugfs_remove(sta);
 
-#ifdef CONFIG_MAC80211_MESH
-	if (ieee80211_vif_is_mesh(&sta->sdata->vif)) {
-		mesh_plink_deactivate(sta);
-		del_timer_sync(&sta->plink_timer);
-	}
-#endif
-
-	/*
-	 * Destroy aggregation state here. It would be nice to wait for the
-	 * driver to finish aggregation stop and then clean up, but for now
-	 * drivers have to handle aggregation stop being requested, followed
-	 * directly by station destruction.
-	 */
-	for (i = 0; i < STA_TID_NUM; i++) {
-		tid_tx = rcu_dereference_raw(sta->ampdu_mlme.tid_tx[i]);
-		if (!tid_tx)
-			continue;
-		__skb_queue_purge(&tid_tx->pending);
-		kfree(tid_tx);
-	}
-
-	sta_info_free(local, sta);
+	call_rcu(&sta->rcu_head, free_sta_rcu);
 
 	return 0;
 }
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index a470e11..c88f161f 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -287,6 +287,7 @@
 struct sta_info {
 	/* General information, mostly static */
 	struct list_head list;
+	struct rcu_head rcu_head;
 	struct sta_info __rcu *hnext;
 	struct ieee80211_local *local;
 	struct ieee80211_sub_if_data *sdata;
@@ -297,6 +298,7 @@
 	spinlock_t lock;
 
 	struct work_struct drv_unblock_wk;
+	struct work_struct free_sta_wk;
 
 	u16 listen_interval;
 
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index b0801b7..2ce8973 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -517,29 +517,41 @@
 
 	if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) {
 		u64 cookie = (unsigned long)skb;
+		bool found = false;
+
 		acked = info->flags & IEEE80211_TX_STAT_ACK;
 
-		if (ieee80211_is_nullfunc(hdr->frame_control) ||
-		    ieee80211_is_qos_nullfunc(hdr->frame_control)) {
-			cfg80211_probe_status(skb->dev, hdr->addr1,
-					      cookie, acked, GFP_ATOMIC);
-		} else if (skb->dev) {
-			cfg80211_mgmt_tx_status(
-				skb->dev->ieee80211_ptr, cookie, skb->data,
-				skb->len, acked, GFP_ATOMIC);
-		} else {
-			struct ieee80211_sub_if_data *p2p_sdata;
+		rcu_read_lock();
 
-			rcu_read_lock();
+		list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+			if (!sdata->dev)
+				continue;
 
-			p2p_sdata = rcu_dereference(local->p2p_sdata);
-			if (p2p_sdata) {
-				cfg80211_mgmt_tx_status(
-					&p2p_sdata->wdev, cookie, skb->data,
-					skb->len, acked, GFP_ATOMIC);
-			}
-			rcu_read_unlock();
+			if (skb->dev != sdata->dev)
+				continue;
+
+			found = true;
+			break;
 		}
+
+		if (!skb->dev) {
+			sdata = rcu_dereference(local->p2p_sdata);
+			if (sdata)
+				found = true;
+		}
+
+		if (!found)
+			skb->dev = NULL;
+		else if (ieee80211_is_nullfunc(hdr->frame_control) ||
+			 ieee80211_is_qos_nullfunc(hdr->frame_control)) {
+			cfg80211_probe_status(sdata->dev, hdr->addr1,
+					      cookie, acked, GFP_ATOMIC);
+		} else {
+			cfg80211_mgmt_tx_status(&sdata->wdev, cookie, skb->data,
+						skb->len, acked, GFP_ATOMIC);
+		}
+
+		rcu_read_unlock();
 	}
 
 	if (unlikely(info->ack_frame_id)) {
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 29eb4e678..e0e0d1d 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -580,7 +580,7 @@
 				tx->key = NULL;
 			else
 				skip_hw = (tx->key->conf.flags &
-					   IEEE80211_KEY_FLAG_SW_MGMT) &&
+					   IEEE80211_KEY_FLAG_SW_MGMT_TX) &&
 					ieee80211_is_mgmt(hdr->frame_control);
 			break;
 		case WLAN_CIPHER_SUITE_AES_CMAC:
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 471fb05..22ca350 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -792,8 +792,11 @@
 			elems->country_elem_len = elen;
 			break;
 		case WLAN_EID_PWR_CONSTRAINT:
+			if (elen != 1) {
+				elem_parse_failed = true;
+				break;
+			}
 			elems->pwr_constr_elem = pos;
-			elems->pwr_constr_elem_len = elen;
 			break;
 		case WLAN_EID_TIMEOUT_INTERVAL:
 			elems->timeout_int = pos;
@@ -1004,6 +1007,45 @@
 	ieee80211_tx_skb(sdata, skb);
 }
 
+void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
+				    const u8 *bssid, u16 stype, u16 reason,
+				    bool send_frame, u8 *frame_buf)
+{
+	struct ieee80211_local *local = sdata->local;
+	struct sk_buff *skb;
+	struct ieee80211_mgmt *mgmt = (void *)frame_buf;
+
+	/* build frame */
+	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | stype);
+	mgmt->duration = 0; /* initialize only */
+	mgmt->seq_ctrl = 0; /* initialize only */
+	memcpy(mgmt->da, bssid, ETH_ALEN);
+	memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
+	memcpy(mgmt->bssid, bssid, ETH_ALEN);
+	/* u.deauth.reason_code == u.disassoc.reason_code */
+	mgmt->u.deauth.reason_code = cpu_to_le16(reason);
+
+	if (send_frame) {
+		skb = dev_alloc_skb(local->hw.extra_tx_headroom +
+				    IEEE80211_DEAUTH_FRAME_LEN);
+		if (!skb)
+			return;
+
+		skb_reserve(skb, local->hw.extra_tx_headroom);
+
+		/* copy in frame */
+		memcpy(skb_put(skb, IEEE80211_DEAUTH_FRAME_LEN),
+		       mgmt, IEEE80211_DEAUTH_FRAME_LEN);
+
+		if (sdata->vif.type != NL80211_IFTYPE_STATION ||
+		    !(sdata->u.mgd.flags & IEEE80211_STA_MFP_ENABLED))
+			IEEE80211_SKB_CB(skb)->flags |=
+				IEEE80211_TX_INTFL_DONT_ENCRYPT;
+
+		ieee80211_tx_skb(sdata, skb);
+	}
+}
+
 int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
 			     const u8 *ie, size_t ie_len,
 			     enum ieee80211_band band, u32 rate_mask,
@@ -1564,14 +1606,13 @@
 	return 0;
 }
 
-/* must hold iflist_mtx */
 void ieee80211_recalc_smps(struct ieee80211_local *local)
 {
 	struct ieee80211_sub_if_data *sdata;
 	enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_OFF;
 	int count = 0;
 
-	lockdep_assert_held(&local->iflist_mtx);
+	mutex_lock(&local->iflist_mtx);
 
 	/*
 	 * This function could be improved to handle multiple
@@ -1600,12 +1641,14 @@
 	}
 
 	if (smps_mode == local->smps_mode)
-		return;
+		goto unlock;
 
  set:
 	local->smps_mode = smps_mode;
 	/* changed flag is auto-detected for this */
 	ieee80211_hw_config(local, 0);
+ unlock:
+	mutex_unlock(&local->iflist_mtx);
 }
 
 static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id)
diff --git a/net/nfc/core.c b/net/nfc/core.c
index ff74979..c9eacc1 100644
--- a/net/nfc/core.c
+++ b/net/nfc/core.c
@@ -679,7 +679,7 @@
 
 	if (dev->ops->check_presence) {
 		del_timer_sync(&dev->check_pres_timer);
-		destroy_workqueue(dev->check_pres_wq);
+		cancel_work_sync(&dev->check_pres_work);
 	}
 
 	nfc_genl_data_exit(&dev->genl_data);
@@ -715,7 +715,7 @@
 {
 	struct nfc_dev *dev = (struct nfc_dev *)data;
 
-	queue_work(dev->check_pres_wq, &dev->check_pres_work);
+	queue_work(system_nrt_wq, &dev->check_pres_work);
 }
 
 struct class nfc_class = {
@@ -784,20 +784,11 @@
 	dev->targets_generation = 1;
 
 	if (ops->check_presence) {
-		char name[32];
 		init_timer(&dev->check_pres_timer);
 		dev->check_pres_timer.data = (unsigned long)dev;
 		dev->check_pres_timer.function = nfc_check_pres_timeout;
 
 		INIT_WORK(&dev->check_pres_work, nfc_check_pres_work);
-		snprintf(name, sizeof(name), "nfc%d_check_pres_wq", dev->idx);
-		dev->check_pres_wq = alloc_workqueue(name, WQ_NON_REENTRANT |
-						     WQ_UNBOUND |
-						     WQ_MEM_RECLAIM, 1);
-		if (dev->check_pres_wq == NULL) {
-			kfree(dev);
-			return NULL;
-		}
 	}
 
 	return dev;
diff --git a/net/nfc/hci/Makefile b/net/nfc/hci/Makefile
index f9c44b2..c5dbb689 100644
--- a/net/nfc/hci/Makefile
+++ b/net/nfc/hci/Makefile
@@ -4,5 +4,5 @@
 
 obj-$(CONFIG_NFC_HCI) += hci.o
 
-hci-y			:= core.o hcp.o command.o
-hci-$(CONFIG_NFC_SHDLC)	+= shdlc.o
+hci-y			:= core.o hcp.o command.o llc.o llc_nop.o
+hci-$(CONFIG_NFC_SHDLC) += llc_shdlc.o
diff --git a/net/nfc/hci/command.c b/net/nfc/hci/command.c
index 46362ef..71c6a70 100644
--- a/net/nfc/hci/command.c
+++ b/net/nfc/hci/command.c
@@ -28,10 +28,29 @@
 
 #include "hci.h"
 
-static void nfc_hci_execute_cb(struct nfc_hci_dev *hdev, int err,
-			       struct sk_buff *skb, void *cb_data)
+static int nfc_hci_execute_cmd_async(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd,
+			       const u8 *param, size_t param_len,
+			       data_exchange_cb_t cb, void *cb_context)
 {
-	struct hcp_exec_waiter *hcp_ew = (struct hcp_exec_waiter *)cb_data;
+	pr_debug("exec cmd async through pipe=%d, cmd=%d, plen=%zd\n", pipe,
+		 cmd, param_len);
+
+	/* TODO: Define hci cmd execution delay. Should it be the same
+	 * for all commands?
+	 */
+	return nfc_hci_hcp_message_tx(hdev, pipe, NFC_HCI_HCP_COMMAND, cmd,
+				      param, param_len, cb, cb_context, 3000);
+}
+
+/*
+ * HCI command execution completion callback.
+ * err will be a standard linux error (may be converted from HCI response)
+ * skb contains the response data and must be disposed, or may be NULL if
+ * an error occured
+ */
+static void nfc_hci_execute_cb(void *context, struct sk_buff *skb, int err)
+{
+	struct hcp_exec_waiter *hcp_ew = (struct hcp_exec_waiter *)context;
 
 	pr_debug("HCI Cmd completed with result=%d\n", err);
 
@@ -55,7 +74,8 @@
 	hcp_ew.exec_complete = false;
 	hcp_ew.result_skb = NULL;
 
-	pr_debug("through pipe=%d, cmd=%d, plen=%zd\n", pipe, cmd, param_len);
+	pr_debug("exec cmd sync through pipe=%d, cmd=%d, plen=%zd\n", pipe,
+		 cmd, param_len);
 
 	/* TODO: Define hci cmd execution delay. Should it be the same
 	 * for all commands?
@@ -133,6 +153,23 @@
 }
 EXPORT_SYMBOL(nfc_hci_send_cmd);
 
+int nfc_hci_send_cmd_async(struct nfc_hci_dev *hdev, u8 gate, u8 cmd,
+			   const u8 *param, size_t param_len,
+			   data_exchange_cb_t cb, void *cb_context)
+{
+	u8 pipe;
+
+	pr_debug("\n");
+
+	pipe = hdev->gate2pipe[gate];
+	if (pipe == NFC_HCI_INVALID_PIPE)
+		return -EADDRNOTAVAIL;
+
+	return nfc_hci_execute_cmd_async(hdev, pipe, cmd, param, param_len,
+					 cb, cb_context);
+}
+EXPORT_SYMBOL(nfc_hci_send_cmd_async);
+
 int nfc_hci_set_param(struct nfc_hci_dev *hdev, u8 gate, u8 idx,
 		      const u8 *param, size_t param_len)
 {
diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c
index 1ac7b3f..d378d93 100644
--- a/net/nfc/hci/core.c
+++ b/net/nfc/hci/core.c
@@ -26,6 +26,7 @@
 
 #include <net/nfc/nfc.h>
 #include <net/nfc/hci.h>
+#include <net/nfc/llc.h>
 
 #include "hci.h"
 
@@ -57,12 +58,11 @@
 	if (hdev->cmd_pending_msg) {
 		if (timer_pending(&hdev->cmd_timer) == 0) {
 			if (hdev->cmd_pending_msg->cb)
-				hdev->cmd_pending_msg->cb(hdev,
-							  -ETIME,
-							  NULL,
-							  hdev->
+				hdev->cmd_pending_msg->cb(hdev->
 							  cmd_pending_msg->
-							  cb_context);
+							  cb_context,
+							  NULL,
+							  -ETIME);
 			kfree(hdev->cmd_pending_msg);
 			hdev->cmd_pending_msg = NULL;
 		} else
@@ -78,12 +78,12 @@
 
 	pr_debug("msg_tx_queue has a cmd to send\n");
 	while ((skb = skb_dequeue(&msg->msg_frags)) != NULL) {
-		r = hdev->ops->xmit(hdev, skb);
+		r = nfc_llc_xmit_from_hci(hdev->llc, skb);
 		if (r < 0) {
 			kfree_skb(skb);
 			skb_queue_purge(&msg->msg_frags);
 			if (msg->cb)
-				msg->cb(hdev, r, NULL, msg->cb_context);
+				msg->cb(msg->cb_context, NULL, r);
 			kfree(msg);
 			break;
 		}
@@ -133,15 +133,15 @@
 	del_timer_sync(&hdev->cmd_timer);
 
 	if (hdev->cmd_pending_msg->cb)
-		hdev->cmd_pending_msg->cb(hdev, err, skb,
-					  hdev->cmd_pending_msg->cb_context);
+		hdev->cmd_pending_msg->cb(hdev->cmd_pending_msg->cb_context,
+					  skb, err);
 	else
 		kfree_skb(skb);
 
 	kfree(hdev->cmd_pending_msg);
 	hdev->cmd_pending_msg = NULL;
 
-	queue_work(hdev->msg_tx_wq, &hdev->msg_tx_work);
+	queue_work(system_nrt_wq, &hdev->msg_tx_work);
 }
 
 void nfc_hci_resp_received(struct nfc_hci_dev *hdev, u8 result,
@@ -326,7 +326,7 @@
 {
 	struct nfc_hci_dev *hdev = (struct nfc_hci_dev *)data;
 
-	queue_work(hdev->msg_tx_wq, &hdev->msg_tx_work);
+	queue_work(system_nrt_wq, &hdev->msg_tx_work);
 }
 
 static int hci_dev_connect_gates(struct nfc_hci_dev *hdev, u8 gate_count,
@@ -398,8 +398,7 @@
 	nfc_hci_disconnect_all_gates(hdev);
 
 exit:
-	if (skb)
-		kfree_skb(skb);
+	kfree_skb(skb);
 
 	return r;
 }
@@ -470,29 +469,38 @@
 			return r;
 	}
 
+	r = nfc_llc_start(hdev->llc);
+	if (r < 0)
+		goto exit_close;
+
 	r = hci_dev_session_init(hdev);
 	if (r < 0)
-		goto exit;
+		goto exit_llc;
 
 	r = nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE,
 			       NFC_HCI_EVT_END_OPERATION, NULL, 0);
 	if (r < 0)
-		goto exit;
+		goto exit_llc;
 
 	if (hdev->ops->hci_ready) {
 		r = hdev->ops->hci_ready(hdev);
 		if (r < 0)
-			goto exit;
+			goto exit_llc;
 	}
 
 	r = hci_dev_version(hdev);
 	if (r < 0)
-		goto exit;
+		goto exit_llc;
 
-exit:
-	if (r < 0)
-		if (hdev->ops->close)
-			hdev->ops->close(hdev);
+	return 0;
+
+exit_llc:
+	nfc_llc_stop(hdev->llc);
+
+exit_close:
+	if (hdev->ops->close)
+		hdev->ops->close(hdev);
+
 	return r;
 }
 
@@ -500,6 +508,8 @@
 {
 	struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
 
+	nfc_llc_stop(hdev->llc);
+
 	if (hdev->ops->close)
 		hdev->ops->close(hdev);
 
@@ -539,13 +549,37 @@
 {
 }
 
+#define HCI_CB_TYPE_TRANSCEIVE 1
+
+static void hci_transceive_cb(void *context, struct sk_buff *skb, int err)
+{
+	struct nfc_hci_dev *hdev = context;
+
+	switch (hdev->async_cb_type) {
+	case HCI_CB_TYPE_TRANSCEIVE:
+		/*
+		 * TODO: Check RF Error indicator to make sure data is valid.
+		 * It seems that HCI cmd can complete without error, but data
+		 * can be invalid if an RF error occured? Ignore for now.
+		 */
+		if (err == 0)
+			skb_trim(skb, skb->len - 1); /* RF Err ind */
+
+		hdev->async_cb(hdev->async_cb_context, skb, err);
+		break;
+	default:
+		if (err == 0)
+			kfree_skb(skb);
+		break;
+	}
+}
+
 static int hci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target,
 			  struct sk_buff *skb, data_exchange_cb_t cb,
 			  void *cb_context)
 {
 	struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
 	int r;
-	struct sk_buff *res_skb = NULL;
 
 	pr_debug("target_idx=%d\n", target->idx);
 
@@ -553,40 +587,37 @@
 	case NFC_HCI_RF_READER_A_GATE:
 	case NFC_HCI_RF_READER_B_GATE:
 		if (hdev->ops->data_exchange) {
-			r = hdev->ops->data_exchange(hdev, target, skb,
-						     &res_skb);
+			r = hdev->ops->data_exchange(hdev, target, skb, cb,
+						     cb_context);
 			if (r <= 0)	/* handled */
 				break;
 		}
 
 		*skb_push(skb, 1) = 0;	/* CTR, see spec:10.2.2.1 */
-		r = nfc_hci_send_cmd(hdev, target->hci_reader_gate,
-				     NFC_HCI_WR_XCHG_DATA,
-				     skb->data, skb->len, &res_skb);
-		/*
-		 * TODO: Check RF Error indicator to make sure data is valid.
-		 * It seems that HCI cmd can complete without error, but data
-		 * can be invalid if an RF error occured? Ignore for now.
-		 */
-		if (r == 0)
-			skb_trim(res_skb, res_skb->len - 1); /* RF Err ind */
+
+		hdev->async_cb_type = HCI_CB_TYPE_TRANSCEIVE;
+		hdev->async_cb = cb;
+		hdev->async_cb_context = cb_context;
+
+		r = nfc_hci_send_cmd_async(hdev, target->hci_reader_gate,
+					   NFC_HCI_WR_XCHG_DATA, skb->data,
+					   skb->len, hci_transceive_cb, hdev);
 		break;
 	default:
 		if (hdev->ops->data_exchange) {
-			r = hdev->ops->data_exchange(hdev, target, skb,
-						     &res_skb);
+			r = hdev->ops->data_exchange(hdev, target, skb, cb,
+						     cb_context);
 			if (r == 1)
 				r = -ENOTSUPP;
 		}
 		else
 			r = -ENOTSUPP;
+		break;
 	}
 
 	kfree_skb(skb);
 
-	cb(cb_context, res_skb, r);
-
-	return 0;
+	return r;
 }
 
 static int hci_check_presence(struct nfc_dev *nfc_dev,
@@ -600,149 +631,6 @@
 	return 0;
 }
 
-static struct nfc_ops hci_nfc_ops = {
-	.dev_up = hci_dev_up,
-	.dev_down = hci_dev_down,
-	.start_poll = hci_start_poll,
-	.stop_poll = hci_stop_poll,
-	.activate_target = hci_activate_target,
-	.deactivate_target = hci_deactivate_target,
-	.im_transceive = hci_transceive,
-	.check_presence = hci_check_presence,
-};
-
-struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops,
-					    struct nfc_hci_init_data *init_data,
-					    u32 protocols,
-					    int tx_headroom,
-					    int tx_tailroom,
-					    int max_link_payload)
-{
-	struct nfc_hci_dev *hdev;
-
-	if (ops->xmit == NULL)
-		return NULL;
-
-	if (protocols == 0)
-		return NULL;
-
-	hdev = kzalloc(sizeof(struct nfc_hci_dev), GFP_KERNEL);
-	if (hdev == NULL)
-		return NULL;
-
-	hdev->ndev = nfc_allocate_device(&hci_nfc_ops, protocols,
-					 tx_headroom + HCI_CMDS_HEADROOM,
-					 tx_tailroom);
-	if (!hdev->ndev) {
-		kfree(hdev);
-		return NULL;
-	}
-
-	hdev->ops = ops;
-	hdev->max_data_link_payload = max_link_payload;
-	hdev->init_data = *init_data;
-
-	nfc_set_drvdata(hdev->ndev, hdev);
-
-	memset(hdev->gate2pipe, NFC_HCI_INVALID_PIPE, sizeof(hdev->gate2pipe));
-
-	return hdev;
-}
-EXPORT_SYMBOL(nfc_hci_allocate_device);
-
-void nfc_hci_free_device(struct nfc_hci_dev *hdev)
-{
-	nfc_free_device(hdev->ndev);
-	kfree(hdev);
-}
-EXPORT_SYMBOL(nfc_hci_free_device);
-
-int nfc_hci_register_device(struct nfc_hci_dev *hdev)
-{
-	struct device *dev = &hdev->ndev->dev;
-	const char *devname = dev_name(dev);
-	char name[32];
-	int r = 0;
-
-	mutex_init(&hdev->msg_tx_mutex);
-
-	INIT_LIST_HEAD(&hdev->msg_tx_queue);
-
-	INIT_WORK(&hdev->msg_tx_work, nfc_hci_msg_tx_work);
-	snprintf(name, sizeof(name), "%s_hci_msg_tx_wq", devname);
-	hdev->msg_tx_wq = alloc_workqueue(name, WQ_NON_REENTRANT | WQ_UNBOUND |
-					  WQ_MEM_RECLAIM, 1);
-	if (hdev->msg_tx_wq == NULL) {
-		r = -ENOMEM;
-		goto exit;
-	}
-
-	init_timer(&hdev->cmd_timer);
-	hdev->cmd_timer.data = (unsigned long)hdev;
-	hdev->cmd_timer.function = nfc_hci_cmd_timeout;
-
-	skb_queue_head_init(&hdev->rx_hcp_frags);
-
-	INIT_WORK(&hdev->msg_rx_work, nfc_hci_msg_rx_work);
-	snprintf(name, sizeof(name), "%s_hci_msg_rx_wq", devname);
-	hdev->msg_rx_wq = alloc_workqueue(name, WQ_NON_REENTRANT | WQ_UNBOUND |
-					  WQ_MEM_RECLAIM, 1);
-	if (hdev->msg_rx_wq == NULL) {
-		r = -ENOMEM;
-		goto exit;
-	}
-
-	skb_queue_head_init(&hdev->msg_rx_queue);
-
-	r = nfc_register_device(hdev->ndev);
-
-exit:
-	if (r < 0) {
-		if (hdev->msg_tx_wq)
-			destroy_workqueue(hdev->msg_tx_wq);
-		if (hdev->msg_rx_wq)
-			destroy_workqueue(hdev->msg_rx_wq);
-	}
-
-	return r;
-}
-EXPORT_SYMBOL(nfc_hci_register_device);
-
-void nfc_hci_unregister_device(struct nfc_hci_dev *hdev)
-{
-	struct hci_msg *msg, *n;
-
-	skb_queue_purge(&hdev->rx_hcp_frags);
-	skb_queue_purge(&hdev->msg_rx_queue);
-
-	list_for_each_entry_safe(msg, n, &hdev->msg_tx_queue, msg_l) {
-		list_del(&msg->msg_l);
-		skb_queue_purge(&msg->msg_frags);
-		kfree(msg);
-	}
-
-	del_timer_sync(&hdev->cmd_timer);
-
-	nfc_unregister_device(hdev->ndev);
-
-	destroy_workqueue(hdev->msg_tx_wq);
-
-	destroy_workqueue(hdev->msg_rx_wq);
-}
-EXPORT_SYMBOL(nfc_hci_unregister_device);
-
-void nfc_hci_set_clientdata(struct nfc_hci_dev *hdev, void *clientdata)
-{
-	hdev->clientdata = clientdata;
-}
-EXPORT_SYMBOL(nfc_hci_set_clientdata);
-
-void *nfc_hci_get_clientdata(struct nfc_hci_dev *hdev)
-{
-	return hdev->clientdata;
-}
-EXPORT_SYMBOL(nfc_hci_get_clientdata);
-
 static void nfc_hci_failure(struct nfc_hci_dev *hdev, int err)
 {
 	mutex_lock(&hdev->msg_tx_mutex);
@@ -758,13 +646,12 @@
 	mutex_unlock(&hdev->msg_tx_mutex);
 }
 
-void nfc_hci_driver_failure(struct nfc_hci_dev *hdev, int err)
+static void nfc_hci_llc_failure(struct nfc_hci_dev *hdev, int err)
 {
 	nfc_hci_failure(hdev, err);
 }
-EXPORT_SYMBOL(nfc_hci_driver_failure);
 
-void nfc_hci_recv_frame(struct nfc_hci_dev *hdev, struct sk_buff *skb)
+static void nfc_hci_recv_from_llc(struct nfc_hci_dev *hdev, struct sk_buff *skb)
 {
 	struct hcp_packet *packet;
 	u8 type;
@@ -827,9 +714,158 @@
 		nfc_hci_hcp_message_rx(hdev, pipe, type, instruction, hcp_skb);
 	} else {
 		skb_queue_tail(&hdev->msg_rx_queue, hcp_skb);
-		queue_work(hdev->msg_rx_wq, &hdev->msg_rx_work);
+		queue_work(system_nrt_wq, &hdev->msg_rx_work);
 	}
 }
+
+static struct nfc_ops hci_nfc_ops = {
+	.dev_up = hci_dev_up,
+	.dev_down = hci_dev_down,
+	.start_poll = hci_start_poll,
+	.stop_poll = hci_stop_poll,
+	.activate_target = hci_activate_target,
+	.deactivate_target = hci_deactivate_target,
+	.im_transceive = hci_transceive,
+	.check_presence = hci_check_presence,
+};
+
+struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops,
+					    struct nfc_hci_init_data *init_data,
+					    u32 protocols,
+					    const char *llc_name,
+					    int tx_headroom,
+					    int tx_tailroom,
+					    int max_link_payload)
+{
+	struct nfc_hci_dev *hdev;
+
+	if (ops->xmit == NULL)
+		return NULL;
+
+	if (protocols == 0)
+		return NULL;
+
+	hdev = kzalloc(sizeof(struct nfc_hci_dev), GFP_KERNEL);
+	if (hdev == NULL)
+		return NULL;
+
+	hdev->llc = nfc_llc_allocate(llc_name, hdev, ops->xmit,
+				     nfc_hci_recv_from_llc, tx_headroom,
+				     tx_tailroom, nfc_hci_llc_failure);
+	if (hdev->llc == NULL) {
+		kfree(hdev);
+		return NULL;
+	}
+
+	hdev->ndev = nfc_allocate_device(&hci_nfc_ops, protocols,
+					 tx_headroom + HCI_CMDS_HEADROOM,
+					 tx_tailroom);
+	if (!hdev->ndev) {
+		nfc_llc_free(hdev->llc);
+		kfree(hdev);
+		return NULL;
+	}
+
+	hdev->ops = ops;
+	hdev->max_data_link_payload = max_link_payload;
+	hdev->init_data = *init_data;
+
+	nfc_set_drvdata(hdev->ndev, hdev);
+
+	memset(hdev->gate2pipe, NFC_HCI_INVALID_PIPE, sizeof(hdev->gate2pipe));
+
+	return hdev;
+}
+EXPORT_SYMBOL(nfc_hci_allocate_device);
+
+void nfc_hci_free_device(struct nfc_hci_dev *hdev)
+{
+	nfc_free_device(hdev->ndev);
+	nfc_llc_free(hdev->llc);
+	kfree(hdev);
+}
+EXPORT_SYMBOL(nfc_hci_free_device);
+
+int nfc_hci_register_device(struct nfc_hci_dev *hdev)
+{
+	mutex_init(&hdev->msg_tx_mutex);
+
+	INIT_LIST_HEAD(&hdev->msg_tx_queue);
+
+	INIT_WORK(&hdev->msg_tx_work, nfc_hci_msg_tx_work);
+
+	init_timer(&hdev->cmd_timer);
+	hdev->cmd_timer.data = (unsigned long)hdev;
+	hdev->cmd_timer.function = nfc_hci_cmd_timeout;
+
+	skb_queue_head_init(&hdev->rx_hcp_frags);
+
+	INIT_WORK(&hdev->msg_rx_work, nfc_hci_msg_rx_work);
+
+	skb_queue_head_init(&hdev->msg_rx_queue);
+
+	return nfc_register_device(hdev->ndev);
+}
+EXPORT_SYMBOL(nfc_hci_register_device);
+
+void nfc_hci_unregister_device(struct nfc_hci_dev *hdev)
+{
+	struct hci_msg *msg, *n;
+
+	skb_queue_purge(&hdev->rx_hcp_frags);
+	skb_queue_purge(&hdev->msg_rx_queue);
+
+	list_for_each_entry_safe(msg, n, &hdev->msg_tx_queue, msg_l) {
+		list_del(&msg->msg_l);
+		skb_queue_purge(&msg->msg_frags);
+		kfree(msg);
+	}
+
+	del_timer_sync(&hdev->cmd_timer);
+
+	nfc_unregister_device(hdev->ndev);
+
+	cancel_work_sync(&hdev->msg_tx_work);
+	cancel_work_sync(&hdev->msg_rx_work);
+}
+EXPORT_SYMBOL(nfc_hci_unregister_device);
+
+void nfc_hci_set_clientdata(struct nfc_hci_dev *hdev, void *clientdata)
+{
+	hdev->clientdata = clientdata;
+}
+EXPORT_SYMBOL(nfc_hci_set_clientdata);
+
+void *nfc_hci_get_clientdata(struct nfc_hci_dev *hdev)
+{
+	return hdev->clientdata;
+}
+EXPORT_SYMBOL(nfc_hci_get_clientdata);
+
+void nfc_hci_driver_failure(struct nfc_hci_dev *hdev, int err)
+{
+	nfc_hci_failure(hdev, err);
+}
+EXPORT_SYMBOL(nfc_hci_driver_failure);
+
+void inline nfc_hci_recv_frame(struct nfc_hci_dev *hdev, struct sk_buff *skb)
+{
+	nfc_llc_rcv_from_drv(hdev->llc, skb);
+}
 EXPORT_SYMBOL(nfc_hci_recv_frame);
 
+static int __init nfc_hci_init(void)
+{
+	return nfc_llc_init();
+}
+
+static void __exit nfc_hci_exit(void)
+{
+	nfc_llc_exit();
+}
+
+subsys_initcall(nfc_hci_init);
+module_exit(nfc_hci_exit);
+
 MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("NFC HCI Core");
diff --git a/net/nfc/hci/hci.h b/net/nfc/hci/hci.h
index fa9a21e..b274d12 100644
--- a/net/nfc/hci/hci.h
+++ b/net/nfc/hci/hci.h
@@ -20,6 +20,8 @@
 #ifndef __LOCAL_HCI_H
 #define __LOCAL_HCI_H
 
+#include <net/nfc/hci.h>
+
 struct gate_pipe_map {
 	u8 gate;
 	u8 pipe;
@@ -35,15 +37,6 @@
 	struct hcp_message message;
 } __packed;
 
-/*
- * HCI command execution completion callback.
- * result will be a standard linux error (may be converted from HCI response)
- * skb contains the response data and must be disposed, or may be NULL if
- * an error occured
- */
-typedef void (*hci_cmd_cb_t) (struct nfc_hci_dev *hdev, int result,
-			      struct sk_buff *skb, void *cb_data);
-
 struct hcp_exec_waiter {
 	wait_queue_head_t *wq;
 	bool exec_complete;
@@ -55,7 +48,7 @@
 	struct list_head msg_l;
 	struct sk_buff_head msg_frags;
 	bool wait_response;
-	hci_cmd_cb_t cb;
+	data_exchange_cb_t cb;
 	void *cb_context;
 	unsigned long completion_delay;
 };
@@ -83,7 +76,7 @@
 int nfc_hci_hcp_message_tx(struct nfc_hci_dev *hdev, u8 pipe,
 			   u8 type, u8 instruction,
 			   const u8 *payload, size_t payload_len,
-			   hci_cmd_cb_t cb, void *cb_data,
+			   data_exchange_cb_t cb, void *cb_context,
 			   unsigned long completion_delay);
 
 u8 nfc_hci_pipe2gate(struct nfc_hci_dev *hdev, u8 pipe);
diff --git a/net/nfc/hci/hcp.c b/net/nfc/hci/hcp.c
index f4dad1a..208eedd 100644
--- a/net/nfc/hci/hcp.c
+++ b/net/nfc/hci/hcp.c
@@ -35,7 +35,7 @@
 int nfc_hci_hcp_message_tx(struct nfc_hci_dev *hdev, u8 pipe,
 			   u8 type, u8 instruction,
 			   const u8 *payload, size_t payload_len,
-			   hci_cmd_cb_t cb, void *cb_data,
+			   data_exchange_cb_t cb, void *cb_context,
 			   unsigned long completion_delay)
 {
 	struct nfc_dev *ndev = hdev->ndev;
@@ -52,7 +52,7 @@
 	skb_queue_head_init(&cmd->msg_frags);
 	cmd->wait_response = (type == NFC_HCI_HCP_COMMAND) ? true : false;
 	cmd->cb = cb;
-	cmd->cb_context = cb_data;
+	cmd->cb_context = cb_context;
 	cmd->completion_delay = completion_delay;
 
 	hci_len = payload_len + 1;
@@ -108,7 +108,7 @@
 	list_add_tail(&cmd->msg_l, &hdev->msg_tx_queue);
 	mutex_unlock(&hdev->msg_tx_mutex);
 
-	queue_work(hdev->msg_tx_wq, &hdev->msg_tx_work);
+	queue_work(system_nrt_wq, &hdev->msg_tx_work);
 
 	return 0;
 
diff --git a/net/nfc/hci/llc.c b/net/nfc/hci/llc.c
new file mode 100644
index 0000000..ae1205d
--- /dev/null
+++ b/net/nfc/hci/llc.c
@@ -0,0 +1,170 @@
+/*
+ * Link Layer Control manager
+ *
+ * Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <net/nfc/llc.h>
+
+#include "llc.h"
+
+static struct list_head llc_engines;
+
+int nfc_llc_init(void)
+{
+	int r;
+
+	INIT_LIST_HEAD(&llc_engines);
+
+	r = nfc_llc_nop_register();
+	if (r)
+		goto exit;
+
+	r = nfc_llc_shdlc_register();
+	if (r)
+		goto exit;
+
+	return 0;
+
+exit:
+	nfc_llc_exit();
+	return r;
+}
+
+void nfc_llc_exit(void)
+{
+	struct nfc_llc_engine *llc_engine, *n;
+
+	list_for_each_entry_safe(llc_engine, n, &llc_engines, entry) {
+		list_del(&llc_engine->entry);
+		kfree(llc_engine->name);
+		kfree(llc_engine);
+	}
+}
+
+int nfc_llc_register(const char *name, struct nfc_llc_ops *ops)
+{
+	struct nfc_llc_engine *llc_engine;
+
+	llc_engine = kzalloc(sizeof(struct nfc_llc_engine), GFP_KERNEL);
+	if (llc_engine == NULL)
+		return -ENOMEM;
+
+	llc_engine->name = kstrdup(name, GFP_KERNEL);
+	if (llc_engine->name == NULL) {
+		kfree(llc_engine);
+		return -ENOMEM;
+	}
+	llc_engine->ops = ops;
+
+	INIT_LIST_HEAD(&llc_engine->entry);
+	list_add_tail (&llc_engine->entry, &llc_engines);
+
+	return 0;
+}
+
+static struct nfc_llc_engine *nfc_llc_name_to_engine(const char *name)
+{
+	struct nfc_llc_engine *llc_engine;
+
+	list_for_each_entry(llc_engine, &llc_engines, entry) {
+		if (strcmp(llc_engine->name, name) == 0)
+			return llc_engine;
+	}
+
+	return NULL;
+}
+
+void nfc_llc_unregister(const char *name)
+{
+	struct nfc_llc_engine *llc_engine;
+
+	llc_engine = nfc_llc_name_to_engine(name);
+	if (llc_engine == NULL)
+		return;
+
+	list_del(&llc_engine->entry);
+	kfree(llc_engine->name);
+	kfree(llc_engine);
+}
+
+struct nfc_llc *nfc_llc_allocate(const char *name, struct nfc_hci_dev *hdev,
+				 xmit_to_drv_t xmit_to_drv,
+				 rcv_to_hci_t rcv_to_hci, int tx_headroom,
+				 int tx_tailroom, llc_failure_t llc_failure)
+{
+	struct nfc_llc_engine *llc_engine;
+	struct nfc_llc *llc;
+
+	llc_engine = nfc_llc_name_to_engine(name);
+	if (llc_engine == NULL)
+		return NULL;
+
+	llc = kzalloc(sizeof(struct nfc_llc), GFP_KERNEL);
+	if (llc == NULL)
+		return NULL;
+
+	llc->data = llc_engine->ops->init(hdev, xmit_to_drv, rcv_to_hci,
+					  tx_headroom, tx_tailroom,
+					  &llc->rx_headroom, &llc->rx_tailroom,
+					  llc_failure);
+	if (llc->data == NULL) {
+		kfree(llc);
+		return NULL;
+	}
+	llc->ops = llc_engine->ops;
+
+	return llc;
+}
+
+void nfc_llc_free(struct nfc_llc *llc)
+{
+	llc->ops->deinit(llc);
+	kfree(llc);
+}
+
+inline void nfc_llc_get_rx_head_tail_room(struct nfc_llc *llc, int *rx_headroom,
+					  int *rx_tailroom)
+{
+	*rx_headroom = llc->rx_headroom;
+	*rx_tailroom = llc->rx_tailroom;
+}
+
+inline int nfc_llc_start(struct nfc_llc *llc)
+{
+	return llc->ops->start(llc);
+}
+
+inline int nfc_llc_stop(struct nfc_llc *llc)
+{
+	return llc->ops->stop(llc);
+}
+
+inline void nfc_llc_rcv_from_drv(struct nfc_llc *llc, struct sk_buff *skb)
+{
+	llc->ops->rcv_from_drv(llc, skb);
+}
+
+inline int nfc_llc_xmit_from_hci(struct nfc_llc *llc, struct sk_buff *skb)
+{
+	return llc->ops->xmit_from_hci(llc, skb);
+}
+
+inline void *nfc_llc_get_data(struct nfc_llc *llc)
+{
+	return llc->data;
+}
diff --git a/net/nfc/hci/llc.h b/net/nfc/hci/llc.h
new file mode 100644
index 0000000..7be0b7f
--- /dev/null
+++ b/net/nfc/hci/llc.h
@@ -0,0 +1,69 @@
+/*
+ * Link Layer Control manager
+ *
+ * Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __LOCAL_LLC_H_
+#define __LOCAL_LLC_H_
+
+#include <net/nfc/hci.h>
+#include <net/nfc/llc.h>
+#include <linux/skbuff.h>
+
+struct nfc_llc_ops {
+	void *(*init) (struct nfc_hci_dev *hdev, xmit_to_drv_t xmit_to_drv,
+		       rcv_to_hci_t rcv_to_hci, int tx_headroom,
+		       int tx_tailroom, int *rx_headroom, int *rx_tailroom,
+		       llc_failure_t llc_failure);
+	void (*deinit) (struct nfc_llc *llc);
+	int (*start) (struct nfc_llc *llc);
+	int (*stop) (struct nfc_llc *llc);
+	void (*rcv_from_drv) (struct nfc_llc *llc, struct sk_buff *skb);
+	int (*xmit_from_hci) (struct nfc_llc *llc, struct sk_buff *skb);
+};
+
+struct nfc_llc_engine {
+	const char *name;
+	struct nfc_llc_ops *ops;
+	struct list_head entry;
+};
+
+struct nfc_llc {
+	void *data;
+	struct nfc_llc_ops *ops;
+	int rx_headroom;
+	int rx_tailroom;
+};
+
+void *nfc_llc_get_data(struct nfc_llc *llc);
+
+int nfc_llc_register(const char *name, struct nfc_llc_ops *ops);
+void nfc_llc_unregister(const char *name);
+
+int nfc_llc_nop_register(void);
+
+#if defined(CONFIG_NFC_SHDLC)
+int nfc_llc_shdlc_register(void);
+#else
+static inline int nfc_llc_shdlc_register(void)
+{
+	return 0;
+}
+#endif
+
+#endif /* __LOCAL_LLC_H_ */
diff --git a/net/nfc/hci/llc_nop.c b/net/nfc/hci/llc_nop.c
new file mode 100644
index 0000000..87b1029
--- /dev/null
+++ b/net/nfc/hci/llc_nop.c
@@ -0,0 +1,99 @@
+/*
+ * nop (passthrough) Link Layer Control
+ *
+ * Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/types.h>
+
+#include "llc.h"
+
+struct llc_nop {
+	struct nfc_hci_dev *hdev;
+	xmit_to_drv_t xmit_to_drv;
+	rcv_to_hci_t rcv_to_hci;
+	int tx_headroom;
+	int tx_tailroom;
+	llc_failure_t llc_failure;
+};
+
+static void *llc_nop_init(struct nfc_hci_dev *hdev, xmit_to_drv_t xmit_to_drv,
+			  rcv_to_hci_t rcv_to_hci, int tx_headroom,
+			  int tx_tailroom, int *rx_headroom, int *rx_tailroom,
+			  llc_failure_t llc_failure)
+{
+	struct llc_nop *llc_nop;
+
+	*rx_headroom = 0;
+	*rx_tailroom = 0;
+
+	llc_nop = kzalloc(sizeof(struct llc_nop), GFP_KERNEL);
+	if (llc_nop == NULL)
+		return NULL;
+
+	llc_nop->hdev = hdev;
+	llc_nop->xmit_to_drv = xmit_to_drv;
+	llc_nop->rcv_to_hci = rcv_to_hci;
+	llc_nop->tx_headroom = tx_headroom;
+	llc_nop->tx_tailroom = tx_tailroom;
+	llc_nop->llc_failure = llc_failure;
+
+	return llc_nop;
+}
+
+static void llc_nop_deinit(struct nfc_llc *llc)
+{
+	kfree(nfc_llc_get_data(llc));
+}
+
+static int llc_nop_start(struct nfc_llc *llc)
+{
+	return 0;
+}
+
+static int llc_nop_stop(struct nfc_llc *llc)
+{
+	return 0;
+}
+
+static void llc_nop_rcv_from_drv(struct nfc_llc *llc, struct sk_buff *skb)
+{
+	struct llc_nop *llc_nop = nfc_llc_get_data(llc);
+
+	llc_nop->rcv_to_hci(llc_nop->hdev, skb);
+}
+
+static int llc_nop_xmit_from_hci(struct nfc_llc *llc, struct sk_buff *skb)
+{
+	struct llc_nop *llc_nop = nfc_llc_get_data(llc);
+
+	return llc_nop->xmit_to_drv(llc_nop->hdev, skb);
+}
+
+static struct nfc_llc_ops llc_nop_ops = {
+	.init = llc_nop_init,
+	.deinit = llc_nop_deinit,
+	.start = llc_nop_start,
+	.stop = llc_nop_stop,
+	.rcv_from_drv = llc_nop_rcv_from_drv,
+	.xmit_from_hci = llc_nop_xmit_from_hci,
+};
+
+int nfc_llc_nop_register(void)
+{
+	return nfc_llc_register(LLC_NOP_NAME, &llc_nop_ops);
+}
diff --git a/net/nfc/hci/llc_shdlc.c b/net/nfc/hci/llc_shdlc.c
new file mode 100644
index 0000000..8f69d79
--- /dev/null
+++ b/net/nfc/hci/llc_shdlc.c
@@ -0,0 +1,857 @@
+/*
+ * shdlc Link Layer Control
+ *
+ * Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define pr_fmt(fmt) "shdlc: %s: " fmt, __func__
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+
+#include "llc.h"
+
+enum shdlc_state {
+	SHDLC_DISCONNECTED = 0,
+	SHDLC_CONNECTING = 1,
+	SHDLC_NEGOTIATING = 2,
+	SHDLC_HALF_CONNECTED = 3,
+	SHDLC_CONNECTED = 4
+};
+
+struct llc_shdlc {
+	struct nfc_hci_dev *hdev;
+	xmit_to_drv_t xmit_to_drv;
+	rcv_to_hci_t rcv_to_hci;
+
+	struct mutex state_mutex;
+	enum shdlc_state state;
+	int hard_fault;
+
+	wait_queue_head_t *connect_wq;
+	int connect_tries;
+	int connect_result;
+	struct timer_list connect_timer;/* aka T3 in spec 10.6.1 */
+
+	u8 w;				/* window size */
+	bool srej_support;
+
+	struct timer_list t1_timer;	/* send ack timeout */
+	bool t1_active;
+
+	struct timer_list t2_timer;	/* guard/retransmit timeout */
+	bool t2_active;
+
+	int ns;				/* next seq num for send */
+	int nr;				/* next expected seq num for receive */
+	int dnr;			/* oldest sent unacked seq num */
+
+	struct sk_buff_head rcv_q;
+
+	struct sk_buff_head send_q;
+	bool rnr;			/* other side is not ready to receive */
+
+	struct sk_buff_head ack_pending_q;
+
+	struct work_struct sm_work;
+
+	int tx_headroom;
+	int tx_tailroom;
+
+	llc_failure_t llc_failure;
+};
+
+#define SHDLC_LLC_HEAD_ROOM	2
+
+#define SHDLC_MAX_WINDOW	4
+#define SHDLC_SREJ_SUPPORT	false
+
+#define SHDLC_CONTROL_HEAD_MASK	0xe0
+#define SHDLC_CONTROL_HEAD_I	0x80
+#define SHDLC_CONTROL_HEAD_I2	0xa0
+#define SHDLC_CONTROL_HEAD_S	0xc0
+#define SHDLC_CONTROL_HEAD_U	0xe0
+
+#define SHDLC_CONTROL_NS_MASK	0x38
+#define SHDLC_CONTROL_NR_MASK	0x07
+#define SHDLC_CONTROL_TYPE_MASK	0x18
+
+#define SHDLC_CONTROL_M_MASK	0x1f
+
+enum sframe_type {
+	S_FRAME_RR = 0x00,
+	S_FRAME_REJ = 0x01,
+	S_FRAME_RNR = 0x02,
+	S_FRAME_SREJ = 0x03
+};
+
+enum uframe_modifier {
+	U_FRAME_UA = 0x06,
+	U_FRAME_RSET = 0x19
+};
+
+#define SHDLC_CONNECT_VALUE_MS	5
+#define SHDLC_T1_VALUE_MS(w)	((5 * w) / 4)
+#define SHDLC_T2_VALUE_MS	300
+
+#define SHDLC_DUMP_SKB(info, skb)				  \
+do {								  \
+	pr_debug("%s:\n", info);				  \
+	print_hex_dump(KERN_DEBUG, "shdlc: ", DUMP_PREFIX_OFFSET, \
+		       16, 1, skb->data, skb->len, 0);		  \
+} while (0)
+
+/* checks x < y <= z modulo 8 */
+static bool llc_shdlc_x_lt_y_lteq_z(int x, int y, int z)
+{
+	if (x < z)
+		return ((x < y) && (y <= z)) ? true : false;
+	else
+		return ((y > x) || (y <= z)) ? true : false;
+}
+
+/* checks x <= y < z modulo 8 */
+static bool llc_shdlc_x_lteq_y_lt_z(int x, int y, int z)
+{
+	if (x <= z)
+		return ((x <= y) && (y < z)) ? true : false;
+	else			/* x > z -> z+8 > x */
+		return ((y >= x) || (y < z)) ? true : false;
+}
+
+static struct sk_buff *llc_shdlc_alloc_skb(struct llc_shdlc *shdlc,
+					   int payload_len)
+{
+	struct sk_buff *skb;
+
+	skb = alloc_skb(shdlc->tx_headroom + SHDLC_LLC_HEAD_ROOM +
+			shdlc->tx_tailroom + payload_len, GFP_KERNEL);
+	if (skb)
+		skb_reserve(skb, shdlc->tx_headroom + SHDLC_LLC_HEAD_ROOM);
+
+	return skb;
+}
+
+/* immediately sends an S frame. */
+static int llc_shdlc_send_s_frame(struct llc_shdlc *shdlc,
+				  enum sframe_type sframe_type, int nr)
+{
+	int r;
+	struct sk_buff *skb;
+
+	pr_debug("sframe_type=%d nr=%d\n", sframe_type, nr);
+
+	skb = llc_shdlc_alloc_skb(shdlc, 0);
+	if (skb == NULL)
+		return -ENOMEM;
+
+	*skb_push(skb, 1) = SHDLC_CONTROL_HEAD_S | (sframe_type << 3) | nr;
+
+	r = shdlc->xmit_to_drv(shdlc->hdev, skb);
+
+	kfree_skb(skb);
+
+	return r;
+}
+
+/* immediately sends an U frame. skb may contain optional payload */
+static int llc_shdlc_send_u_frame(struct llc_shdlc *shdlc,
+				  struct sk_buff *skb,
+				  enum uframe_modifier uframe_modifier)
+{
+	int r;
+
+	pr_debug("uframe_modifier=%d\n", uframe_modifier);
+
+	*skb_push(skb, 1) = SHDLC_CONTROL_HEAD_U | uframe_modifier;
+
+	r = shdlc->xmit_to_drv(shdlc->hdev, skb);
+
+	kfree_skb(skb);
+
+	return r;
+}
+
+/*
+ * Free ack_pending frames until y_nr - 1, and reset t2 according to
+ * the remaining oldest ack_pending frame sent time
+ */
+static void llc_shdlc_reset_t2(struct llc_shdlc *shdlc, int y_nr)
+{
+	struct sk_buff *skb;
+	int dnr = shdlc->dnr;	/* MUST initially be < y_nr */
+
+	pr_debug("release ack pending up to frame %d excluded\n", y_nr);
+
+	while (dnr != y_nr) {
+		pr_debug("release ack pending frame %d\n", dnr);
+
+		skb = skb_dequeue(&shdlc->ack_pending_q);
+		kfree_skb(skb);
+
+		dnr = (dnr + 1) % 8;
+	}
+
+	if (skb_queue_empty(&shdlc->ack_pending_q)) {
+		if (shdlc->t2_active) {
+			del_timer_sync(&shdlc->t2_timer);
+			shdlc->t2_active = false;
+
+			pr_debug
+			    ("All sent frames acked. Stopped T2(retransmit)\n");
+		}
+	} else {
+		skb = skb_peek(&shdlc->ack_pending_q);
+
+		mod_timer(&shdlc->t2_timer, *(unsigned long *)skb->cb +
+			  msecs_to_jiffies(SHDLC_T2_VALUE_MS));
+		shdlc->t2_active = true;
+
+		pr_debug
+		    ("Start T2(retransmit) for remaining unacked sent frames\n");
+	}
+}
+
+/*
+ * Receive validated frames from lower layer. skb contains HCI payload only.
+ * Handle according to algorithm at spec:10.8.2
+ */
+static void llc_shdlc_rcv_i_frame(struct llc_shdlc *shdlc,
+				  struct sk_buff *skb, int ns, int nr)
+{
+	int x_ns = ns;
+	int y_nr = nr;
+
+	pr_debug("recvd I-frame %d, remote waiting frame %d\n", ns, nr);
+
+	if (shdlc->state != SHDLC_CONNECTED)
+		goto exit;
+
+	if (x_ns != shdlc->nr) {
+		llc_shdlc_send_s_frame(shdlc, S_FRAME_REJ, shdlc->nr);
+		goto exit;
+	}
+
+	if (shdlc->t1_active == false) {
+		shdlc->t1_active = true;
+		mod_timer(&shdlc->t1_timer, jiffies +
+			  msecs_to_jiffies(SHDLC_T1_VALUE_MS(shdlc->w)));
+		pr_debug("(re)Start T1(send ack)\n");
+	}
+
+	if (skb->len) {
+		shdlc->rcv_to_hci(shdlc->hdev, skb);
+		skb = NULL;
+	}
+
+	shdlc->nr = (shdlc->nr + 1) % 8;
+
+	if (llc_shdlc_x_lt_y_lteq_z(shdlc->dnr, y_nr, shdlc->ns)) {
+		llc_shdlc_reset_t2(shdlc, y_nr);
+
+		shdlc->dnr = y_nr;
+	}
+
+exit:
+	kfree_skb(skb);
+}
+
+static void llc_shdlc_rcv_ack(struct llc_shdlc *shdlc, int y_nr)
+{
+	pr_debug("remote acked up to frame %d excluded\n", y_nr);
+
+	if (llc_shdlc_x_lt_y_lteq_z(shdlc->dnr, y_nr, shdlc->ns)) {
+		llc_shdlc_reset_t2(shdlc, y_nr);
+		shdlc->dnr = y_nr;
+	}
+}
+
+static void llc_shdlc_requeue_ack_pending(struct llc_shdlc *shdlc)
+{
+	struct sk_buff *skb;
+
+	pr_debug("ns reset to %d\n", shdlc->dnr);
+
+	while ((skb = skb_dequeue_tail(&shdlc->ack_pending_q))) {
+		skb_pull(skb, 1);	/* remove control field */
+		skb_queue_head(&shdlc->send_q, skb);
+	}
+	shdlc->ns = shdlc->dnr;
+}
+
+static void llc_shdlc_rcv_rej(struct llc_shdlc *shdlc, int y_nr)
+{
+	struct sk_buff *skb;
+
+	pr_debug("remote asks retransmition from frame %d\n", y_nr);
+
+	if (llc_shdlc_x_lteq_y_lt_z(shdlc->dnr, y_nr, shdlc->ns)) {
+		if (shdlc->t2_active) {
+			del_timer_sync(&shdlc->t2_timer);
+			shdlc->t2_active = false;
+			pr_debug("Stopped T2(retransmit)\n");
+		}
+
+		if (shdlc->dnr != y_nr) {
+			while ((shdlc->dnr = ((shdlc->dnr + 1) % 8)) != y_nr) {
+				skb = skb_dequeue(&shdlc->ack_pending_q);
+				kfree_skb(skb);
+			}
+		}
+
+		llc_shdlc_requeue_ack_pending(shdlc);
+	}
+}
+
+/* See spec RR:10.8.3 REJ:10.8.4 */
+static void llc_shdlc_rcv_s_frame(struct llc_shdlc *shdlc,
+				  enum sframe_type s_frame_type, int nr)
+{
+	struct sk_buff *skb;
+
+	if (shdlc->state != SHDLC_CONNECTED)
+		return;
+
+	switch (s_frame_type) {
+	case S_FRAME_RR:
+		llc_shdlc_rcv_ack(shdlc, nr);
+		if (shdlc->rnr == true) {	/* see SHDLC 10.7.7 */
+			shdlc->rnr = false;
+			if (shdlc->send_q.qlen == 0) {
+				skb = llc_shdlc_alloc_skb(shdlc, 0);
+				if (skb)
+					skb_queue_tail(&shdlc->send_q, skb);
+			}
+		}
+		break;
+	case S_FRAME_REJ:
+		llc_shdlc_rcv_rej(shdlc, nr);
+		break;
+	case S_FRAME_RNR:
+		llc_shdlc_rcv_ack(shdlc, nr);
+		shdlc->rnr = true;
+		break;
+	default:
+		break;
+	}
+}
+
+static void llc_shdlc_connect_complete(struct llc_shdlc *shdlc, int r)
+{
+	pr_debug("result=%d\n", r);
+
+	del_timer_sync(&shdlc->connect_timer);
+
+	if (r == 0) {
+		shdlc->ns = 0;
+		shdlc->nr = 0;
+		shdlc->dnr = 0;
+
+		shdlc->state = SHDLC_HALF_CONNECTED;
+	} else {
+		shdlc->state = SHDLC_DISCONNECTED;
+	}
+
+	shdlc->connect_result = r;
+
+	wake_up(shdlc->connect_wq);
+}
+
+static int llc_shdlc_connect_initiate(struct llc_shdlc *shdlc)
+{
+	struct sk_buff *skb;
+
+	pr_debug("\n");
+
+	skb = llc_shdlc_alloc_skb(shdlc, 2);
+	if (skb == NULL)
+		return -ENOMEM;
+
+	*skb_put(skb, 1) = SHDLC_MAX_WINDOW;
+	*skb_put(skb, 1) = SHDLC_SREJ_SUPPORT ? 1 : 0;
+
+	return llc_shdlc_send_u_frame(shdlc, skb, U_FRAME_RSET);
+}
+
+static int llc_shdlc_connect_send_ua(struct llc_shdlc *shdlc)
+{
+	struct sk_buff *skb;
+
+	pr_debug("\n");
+
+	skb = llc_shdlc_alloc_skb(shdlc, 0);
+	if (skb == NULL)
+		return -ENOMEM;
+
+	return llc_shdlc_send_u_frame(shdlc, skb, U_FRAME_UA);
+}
+
+static void llc_shdlc_rcv_u_frame(struct llc_shdlc *shdlc,
+				  struct sk_buff *skb,
+				  enum uframe_modifier u_frame_modifier)
+{
+	u8 w = SHDLC_MAX_WINDOW;
+	bool srej_support = SHDLC_SREJ_SUPPORT;
+	int r;
+
+	pr_debug("u_frame_modifier=%d\n", u_frame_modifier);
+
+	switch (u_frame_modifier) {
+	case U_FRAME_RSET:
+		switch (shdlc->state) {
+		case SHDLC_NEGOTIATING:
+		case SHDLC_CONNECTING:
+			/*
+			 * We sent RSET, but chip wants to negociate or we
+			 * got RSET before we managed to send out our.
+			 */
+			if (skb->len > 0)
+				w = skb->data[0];
+
+			if (skb->len > 1)
+				srej_support = skb->data[1] & 0x01 ? true :
+					       false;
+
+			if ((w <= SHDLC_MAX_WINDOW) &&
+			    (SHDLC_SREJ_SUPPORT || (srej_support == false))) {
+				shdlc->w = w;
+				shdlc->srej_support = srej_support;
+				r = llc_shdlc_connect_send_ua(shdlc);
+				llc_shdlc_connect_complete(shdlc, r);
+			}
+			break;
+		case SHDLC_HALF_CONNECTED:
+			/*
+			 * Chip resent RSET due to its timeout - Ignote it
+			 * as we already sent UA.
+			 */
+			break;
+		case SHDLC_CONNECTED:
+			/*
+			 * Chip wants to reset link. This is unexpected and
+			 * unsupported.
+			 */
+			shdlc->hard_fault = -ECONNRESET;
+			break;
+		default:
+			break;
+		}
+		break;
+	case U_FRAME_UA:
+		if ((shdlc->state == SHDLC_CONNECTING &&
+		     shdlc->connect_tries > 0) ||
+		    (shdlc->state == SHDLC_NEGOTIATING)) {
+			llc_shdlc_connect_complete(shdlc, 0);
+			shdlc->state = SHDLC_CONNECTED;
+		}
+		break;
+	default:
+		break;
+	}
+
+	kfree_skb(skb);
+}
+
+static void llc_shdlc_handle_rcv_queue(struct llc_shdlc *shdlc)
+{
+	struct sk_buff *skb;
+	u8 control;
+	int nr;
+	int ns;
+	enum sframe_type s_frame_type;
+	enum uframe_modifier u_frame_modifier;
+
+	if (shdlc->rcv_q.qlen)
+		pr_debug("rcvQlen=%d\n", shdlc->rcv_q.qlen);
+
+	while ((skb = skb_dequeue(&shdlc->rcv_q)) != NULL) {
+		control = skb->data[0];
+		skb_pull(skb, 1);
+		switch (control & SHDLC_CONTROL_HEAD_MASK) {
+		case SHDLC_CONTROL_HEAD_I:
+		case SHDLC_CONTROL_HEAD_I2:
+			if (shdlc->state == SHDLC_HALF_CONNECTED)
+				shdlc->state = SHDLC_CONNECTED;
+
+			ns = (control & SHDLC_CONTROL_NS_MASK) >> 3;
+			nr = control & SHDLC_CONTROL_NR_MASK;
+			llc_shdlc_rcv_i_frame(shdlc, skb, ns, nr);
+			break;
+		case SHDLC_CONTROL_HEAD_S:
+			if (shdlc->state == SHDLC_HALF_CONNECTED)
+				shdlc->state = SHDLC_CONNECTED;
+
+			s_frame_type = (control & SHDLC_CONTROL_TYPE_MASK) >> 3;
+			nr = control & SHDLC_CONTROL_NR_MASK;
+			llc_shdlc_rcv_s_frame(shdlc, s_frame_type, nr);
+			kfree_skb(skb);
+			break;
+		case SHDLC_CONTROL_HEAD_U:
+			u_frame_modifier = control & SHDLC_CONTROL_M_MASK;
+			llc_shdlc_rcv_u_frame(shdlc, skb, u_frame_modifier);
+			break;
+		default:
+			pr_err("UNKNOWN Control=%d\n", control);
+			kfree_skb(skb);
+			break;
+		}
+	}
+}
+
+static int llc_shdlc_w_used(int ns, int dnr)
+{
+	int unack_count;
+
+	if (dnr <= ns)
+		unack_count = ns - dnr;
+	else
+		unack_count = 8 - dnr + ns;
+
+	return unack_count;
+}
+
+/* Send frames according to algorithm at spec:10.8.1 */
+static void llc_shdlc_handle_send_queue(struct llc_shdlc *shdlc)
+{
+	struct sk_buff *skb;
+	int r;
+	unsigned long time_sent;
+
+	if (shdlc->send_q.qlen)
+		pr_debug
+		    ("sendQlen=%d ns=%d dnr=%d rnr=%s w_room=%d unackQlen=%d\n",
+		     shdlc->send_q.qlen, shdlc->ns, shdlc->dnr,
+		     shdlc->rnr == false ? "false" : "true",
+		     shdlc->w - llc_shdlc_w_used(shdlc->ns, shdlc->dnr),
+		     shdlc->ack_pending_q.qlen);
+
+	while (shdlc->send_q.qlen && shdlc->ack_pending_q.qlen < shdlc->w &&
+	       (shdlc->rnr == false)) {
+
+		if (shdlc->t1_active) {
+			del_timer_sync(&shdlc->t1_timer);
+			shdlc->t1_active = false;
+			pr_debug("Stopped T1(send ack)\n");
+		}
+
+		skb = skb_dequeue(&shdlc->send_q);
+
+		*skb_push(skb, 1) = SHDLC_CONTROL_HEAD_I | (shdlc->ns << 3) |
+				    shdlc->nr;
+
+		pr_debug("Sending I-Frame %d, waiting to rcv %d\n", shdlc->ns,
+			 shdlc->nr);
+		SHDLC_DUMP_SKB("shdlc frame written", skb);
+
+		r = shdlc->xmit_to_drv(shdlc->hdev, skb);
+		if (r < 0) {
+			shdlc->hard_fault = r;
+			break;
+		}
+
+		shdlc->ns = (shdlc->ns + 1) % 8;
+
+		time_sent = jiffies;
+		*(unsigned long *)skb->cb = time_sent;
+
+		skb_queue_tail(&shdlc->ack_pending_q, skb);
+
+		if (shdlc->t2_active == false) {
+			shdlc->t2_active = true;
+			mod_timer(&shdlc->t2_timer, time_sent +
+				  msecs_to_jiffies(SHDLC_T2_VALUE_MS));
+			pr_debug("Started T2 (retransmit)\n");
+		}
+	}
+}
+
+static void llc_shdlc_connect_timeout(unsigned long data)
+{
+	struct llc_shdlc *shdlc = (struct llc_shdlc *)data;
+
+	pr_debug("\n");
+
+	queue_work(system_nrt_wq, &shdlc->sm_work);
+}
+
+static void llc_shdlc_t1_timeout(unsigned long data)
+{
+	struct llc_shdlc *shdlc = (struct llc_shdlc *)data;
+
+	pr_debug("SoftIRQ: need to send ack\n");
+
+	queue_work(system_nrt_wq, &shdlc->sm_work);
+}
+
+static void llc_shdlc_t2_timeout(unsigned long data)
+{
+	struct llc_shdlc *shdlc = (struct llc_shdlc *)data;
+
+	pr_debug("SoftIRQ: need to retransmit\n");
+
+	queue_work(system_nrt_wq, &shdlc->sm_work);
+}
+
+static void llc_shdlc_sm_work(struct work_struct *work)
+{
+	struct llc_shdlc *shdlc = container_of(work, struct llc_shdlc, sm_work);
+	int r;
+
+	pr_debug("\n");
+
+	mutex_lock(&shdlc->state_mutex);
+
+	switch (shdlc->state) {
+	case SHDLC_DISCONNECTED:
+		skb_queue_purge(&shdlc->rcv_q);
+		skb_queue_purge(&shdlc->send_q);
+		skb_queue_purge(&shdlc->ack_pending_q);
+		break;
+	case SHDLC_CONNECTING:
+		if (shdlc->hard_fault) {
+			llc_shdlc_connect_complete(shdlc, shdlc->hard_fault);
+			break;
+		}
+
+		if (shdlc->connect_tries++ < 5)
+			r = llc_shdlc_connect_initiate(shdlc);
+		else
+			r = -ETIME;
+		if (r < 0)
+			llc_shdlc_connect_complete(shdlc, r);
+		else {
+			mod_timer(&shdlc->connect_timer, jiffies +
+				  msecs_to_jiffies(SHDLC_CONNECT_VALUE_MS));
+
+			shdlc->state = SHDLC_NEGOTIATING;
+		}
+		break;
+	case SHDLC_NEGOTIATING:
+		if (timer_pending(&shdlc->connect_timer) == 0) {
+			shdlc->state = SHDLC_CONNECTING;
+			queue_work(system_nrt_wq, &shdlc->sm_work);
+		}
+
+		llc_shdlc_handle_rcv_queue(shdlc);
+
+		if (shdlc->hard_fault) {
+			llc_shdlc_connect_complete(shdlc, shdlc->hard_fault);
+			break;
+		}
+		break;
+	case SHDLC_HALF_CONNECTED:
+	case SHDLC_CONNECTED:
+		llc_shdlc_handle_rcv_queue(shdlc);
+		llc_shdlc_handle_send_queue(shdlc);
+
+		if (shdlc->t1_active && timer_pending(&shdlc->t1_timer) == 0) {
+			pr_debug
+			    ("Handle T1(send ack) elapsed (T1 now inactive)\n");
+
+			shdlc->t1_active = false;
+			r = llc_shdlc_send_s_frame(shdlc, S_FRAME_RR,
+						   shdlc->nr);
+			if (r < 0)
+				shdlc->hard_fault = r;
+		}
+
+		if (shdlc->t2_active && timer_pending(&shdlc->t2_timer) == 0) {
+			pr_debug
+			    ("Handle T2(retransmit) elapsed (T2 inactive)\n");
+
+			shdlc->t2_active = false;
+
+			llc_shdlc_requeue_ack_pending(shdlc);
+			llc_shdlc_handle_send_queue(shdlc);
+		}
+
+		if (shdlc->hard_fault) {
+			shdlc->llc_failure(shdlc->hdev, shdlc->hard_fault);
+		}
+		break;
+	default:
+		break;
+	}
+	mutex_unlock(&shdlc->state_mutex);
+}
+
+/*
+ * Called from syscall context to establish shdlc link. Sleeps until
+ * link is ready or failure.
+ */
+static int llc_shdlc_connect(struct llc_shdlc *shdlc)
+{
+	DECLARE_WAIT_QUEUE_HEAD_ONSTACK(connect_wq);
+
+	pr_debug("\n");
+
+	mutex_lock(&shdlc->state_mutex);
+
+	shdlc->state = SHDLC_CONNECTING;
+	shdlc->connect_wq = &connect_wq;
+	shdlc->connect_tries = 0;
+	shdlc->connect_result = 1;
+
+	mutex_unlock(&shdlc->state_mutex);
+
+	queue_work(system_nrt_wq, &shdlc->sm_work);
+
+	wait_event(connect_wq, shdlc->connect_result != 1);
+
+	return shdlc->connect_result;
+}
+
+static void llc_shdlc_disconnect(struct llc_shdlc *shdlc)
+{
+	pr_debug("\n");
+
+	mutex_lock(&shdlc->state_mutex);
+
+	shdlc->state = SHDLC_DISCONNECTED;
+
+	mutex_unlock(&shdlc->state_mutex);
+
+	queue_work(system_nrt_wq, &shdlc->sm_work);
+}
+
+/*
+ * Receive an incoming shdlc frame. Frame has already been crc-validated.
+ * skb contains only LLC header and payload.
+ * If skb == NULL, it is a notification that the link below is dead.
+ */
+static void llc_shdlc_recv_frame(struct llc_shdlc *shdlc, struct sk_buff *skb)
+{
+	if (skb == NULL) {
+		pr_err("NULL Frame -> link is dead\n");
+		shdlc->hard_fault = -EREMOTEIO;
+	} else {
+		SHDLC_DUMP_SKB("incoming frame", skb);
+		skb_queue_tail(&shdlc->rcv_q, skb);
+	}
+
+	queue_work(system_nrt_wq, &shdlc->sm_work);
+}
+
+static void *llc_shdlc_init(struct nfc_hci_dev *hdev, xmit_to_drv_t xmit_to_drv,
+			    rcv_to_hci_t rcv_to_hci, int tx_headroom,
+			    int tx_tailroom, int *rx_headroom, int *rx_tailroom,
+			    llc_failure_t llc_failure)
+{
+	struct llc_shdlc *shdlc;
+
+	*rx_headroom = SHDLC_LLC_HEAD_ROOM;
+	*rx_tailroom = 0;
+
+	shdlc = kzalloc(sizeof(struct llc_shdlc), GFP_KERNEL);
+	if (shdlc == NULL)
+		return NULL;
+
+	mutex_init(&shdlc->state_mutex);
+	shdlc->state = SHDLC_DISCONNECTED;
+
+	init_timer(&shdlc->connect_timer);
+	shdlc->connect_timer.data = (unsigned long)shdlc;
+	shdlc->connect_timer.function = llc_shdlc_connect_timeout;
+
+	init_timer(&shdlc->t1_timer);
+	shdlc->t1_timer.data = (unsigned long)shdlc;
+	shdlc->t1_timer.function = llc_shdlc_t1_timeout;
+
+	init_timer(&shdlc->t2_timer);
+	shdlc->t2_timer.data = (unsigned long)shdlc;
+	shdlc->t2_timer.function = llc_shdlc_t2_timeout;
+
+	shdlc->w = SHDLC_MAX_WINDOW;
+	shdlc->srej_support = SHDLC_SREJ_SUPPORT;
+
+	skb_queue_head_init(&shdlc->rcv_q);
+	skb_queue_head_init(&shdlc->send_q);
+	skb_queue_head_init(&shdlc->ack_pending_q);
+
+	INIT_WORK(&shdlc->sm_work, llc_shdlc_sm_work);
+
+	shdlc->hdev = hdev;
+	shdlc->xmit_to_drv = xmit_to_drv;
+	shdlc->rcv_to_hci = rcv_to_hci;
+	shdlc->tx_headroom = tx_headroom;
+	shdlc->tx_tailroom = tx_tailroom;
+	shdlc->llc_failure = llc_failure;
+
+	return shdlc;
+}
+
+static void llc_shdlc_deinit(struct nfc_llc *llc)
+{
+	struct llc_shdlc *shdlc = nfc_llc_get_data(llc);
+
+	skb_queue_purge(&shdlc->rcv_q);
+	skb_queue_purge(&shdlc->send_q);
+	skb_queue_purge(&shdlc->ack_pending_q);
+
+	kfree(shdlc);
+}
+
+static int llc_shdlc_start(struct nfc_llc *llc)
+{
+	struct llc_shdlc *shdlc = nfc_llc_get_data(llc);
+
+	return llc_shdlc_connect(shdlc);
+}
+
+static int llc_shdlc_stop(struct nfc_llc *llc)
+{
+	struct llc_shdlc *shdlc = nfc_llc_get_data(llc);
+
+	llc_shdlc_disconnect(shdlc);
+
+	return 0;
+}
+
+static void llc_shdlc_rcv_from_drv(struct nfc_llc *llc, struct sk_buff *skb)
+{
+	struct llc_shdlc *shdlc = nfc_llc_get_data(llc);
+
+	llc_shdlc_recv_frame(shdlc, skb);
+}
+
+static int llc_shdlc_xmit_from_hci(struct nfc_llc *llc, struct sk_buff *skb)
+{
+	struct llc_shdlc *shdlc = nfc_llc_get_data(llc);
+
+	skb_queue_tail(&shdlc->send_q, skb);
+
+	queue_work(system_nrt_wq, &shdlc->sm_work);
+
+	return 0;
+}
+
+static struct nfc_llc_ops llc_shdlc_ops = {
+	.init = llc_shdlc_init,
+	.deinit = llc_shdlc_deinit,
+	.start = llc_shdlc_start,
+	.stop = llc_shdlc_stop,
+	.rcv_from_drv = llc_shdlc_rcv_from_drv,
+	.xmit_from_hci = llc_shdlc_xmit_from_hci,
+};
+
+int nfc_llc_shdlc_register(void)
+{
+	return nfc_llc_register(LLC_SHDLC_NAME, &llc_shdlc_ops);
+}
diff --git a/net/nfc/hci/shdlc.c b/net/nfc/hci/shdlc.c
deleted file mode 100644
index 6f840c1..0000000
--- a/net/nfc/hci/shdlc.c
+++ /dev/null
@@ -1,951 +0,0 @@
-/*
- * Copyright (C) 2012  Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License 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, write to the
- * Free Software Foundation, Inc.,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#define pr_fmt(fmt) "shdlc: %s: " fmt, __func__
-
-#include <linux/sched.h>
-#include <linux/export.h>
-#include <linux/wait.h>
-#include <linux/crc-ccitt.h>
-#include <linux/slab.h>
-#include <linux/skbuff.h>
-
-#include <net/nfc/hci.h>
-#include <net/nfc/shdlc.h>
-
-#define SHDLC_LLC_HEAD_ROOM	2
-#define SHDLC_LLC_TAIL_ROOM	2
-
-#define SHDLC_MAX_WINDOW	4
-#define SHDLC_SREJ_SUPPORT	false
-
-#define SHDLC_CONTROL_HEAD_MASK	0xe0
-#define SHDLC_CONTROL_HEAD_I	0x80
-#define SHDLC_CONTROL_HEAD_I2	0xa0
-#define SHDLC_CONTROL_HEAD_S	0xc0
-#define SHDLC_CONTROL_HEAD_U	0xe0
-
-#define SHDLC_CONTROL_NS_MASK	0x38
-#define SHDLC_CONTROL_NR_MASK	0x07
-#define SHDLC_CONTROL_TYPE_MASK	0x18
-
-#define SHDLC_CONTROL_M_MASK	0x1f
-
-enum sframe_type {
-	S_FRAME_RR = 0x00,
-	S_FRAME_REJ = 0x01,
-	S_FRAME_RNR = 0x02,
-	S_FRAME_SREJ = 0x03
-};
-
-enum uframe_modifier {
-	U_FRAME_UA = 0x06,
-	U_FRAME_RSET = 0x19
-};
-
-#define SHDLC_CONNECT_VALUE_MS	5
-#define SHDLC_T1_VALUE_MS(w)	((5 * w) / 4)
-#define SHDLC_T2_VALUE_MS	300
-
-#define SHDLC_DUMP_SKB(info, skb)				  \
-do {								  \
-	pr_debug("%s:\n", info);				  \
-	print_hex_dump(KERN_DEBUG, "shdlc: ", DUMP_PREFIX_OFFSET, \
-		       16, 1, skb->data, skb->len, 0);		  \
-} while (0)
-
-/* checks x < y <= z modulo 8 */
-static bool nfc_shdlc_x_lt_y_lteq_z(int x, int y, int z)
-{
-	if (x < z)
-		return ((x < y) && (y <= z)) ? true : false;
-	else
-		return ((y > x) || (y <= z)) ? true : false;
-}
-
-/* checks x <= y < z modulo 8 */
-static bool nfc_shdlc_x_lteq_y_lt_z(int x, int y, int z)
-{
-	if (x <= z)
-		return ((x <= y) && (y < z)) ? true : false;
-	else			/* x > z -> z+8 > x */
-		return ((y >= x) || (y < z)) ? true : false;
-}
-
-static struct sk_buff *nfc_shdlc_alloc_skb(struct nfc_shdlc *shdlc,
-					   int payload_len)
-{
-	struct sk_buff *skb;
-
-	skb = alloc_skb(shdlc->client_headroom + SHDLC_LLC_HEAD_ROOM +
-			shdlc->client_tailroom + SHDLC_LLC_TAIL_ROOM +
-			payload_len, GFP_KERNEL);
-	if (skb)
-		skb_reserve(skb, shdlc->client_headroom + SHDLC_LLC_HEAD_ROOM);
-
-	return skb;
-}
-
-static void nfc_shdlc_add_len_crc(struct sk_buff *skb)
-{
-	u16 crc;
-	int len;
-
-	len = skb->len + 2;
-	*skb_push(skb, 1) = len;
-
-	crc = crc_ccitt(0xffff, skb->data, skb->len);
-	crc = ~crc;
-	*skb_put(skb, 1) = crc & 0xff;
-	*skb_put(skb, 1) = crc >> 8;
-}
-
-/* immediately sends an S frame. */
-static int nfc_shdlc_send_s_frame(struct nfc_shdlc *shdlc,
-				  enum sframe_type sframe_type, int nr)
-{
-	int r;
-	struct sk_buff *skb;
-
-	pr_debug("sframe_type=%d nr=%d\n", sframe_type, nr);
-
-	skb = nfc_shdlc_alloc_skb(shdlc, 0);
-	if (skb == NULL)
-		return -ENOMEM;
-
-	*skb_push(skb, 1) = SHDLC_CONTROL_HEAD_S | (sframe_type << 3) | nr;
-
-	nfc_shdlc_add_len_crc(skb);
-
-	r = shdlc->ops->xmit(shdlc, skb);
-
-	kfree_skb(skb);
-
-	return r;
-}
-
-/* immediately sends an U frame. skb may contain optional payload */
-static int nfc_shdlc_send_u_frame(struct nfc_shdlc *shdlc,
-				  struct sk_buff *skb,
-				  enum uframe_modifier uframe_modifier)
-{
-	int r;
-
-	pr_debug("uframe_modifier=%d\n", uframe_modifier);
-
-	*skb_push(skb, 1) = SHDLC_CONTROL_HEAD_U | uframe_modifier;
-
-	nfc_shdlc_add_len_crc(skb);
-
-	r = shdlc->ops->xmit(shdlc, skb);
-
-	kfree_skb(skb);
-
-	return r;
-}
-
-/*
- * Free ack_pending frames until y_nr - 1, and reset t2 according to
- * the remaining oldest ack_pending frame sent time
- */
-static void nfc_shdlc_reset_t2(struct nfc_shdlc *shdlc, int y_nr)
-{
-	struct sk_buff *skb;
-	int dnr = shdlc->dnr;	/* MUST initially be < y_nr */
-
-	pr_debug("release ack pending up to frame %d excluded\n", y_nr);
-
-	while (dnr != y_nr) {
-		pr_debug("release ack pending frame %d\n", dnr);
-
-		skb = skb_dequeue(&shdlc->ack_pending_q);
-		kfree_skb(skb);
-
-		dnr = (dnr + 1) % 8;
-	}
-
-	if (skb_queue_empty(&shdlc->ack_pending_q)) {
-		if (shdlc->t2_active) {
-			del_timer_sync(&shdlc->t2_timer);
-			shdlc->t2_active = false;
-
-			pr_debug
-			    ("All sent frames acked. Stopped T2(retransmit)\n");
-		}
-	} else {
-		skb = skb_peek(&shdlc->ack_pending_q);
-
-		mod_timer(&shdlc->t2_timer, *(unsigned long *)skb->cb +
-			  msecs_to_jiffies(SHDLC_T2_VALUE_MS));
-		shdlc->t2_active = true;
-
-		pr_debug
-		    ("Start T2(retransmit) for remaining unacked sent frames\n");
-	}
-}
-
-/*
- * Receive validated frames from lower layer. skb contains HCI payload only.
- * Handle according to algorithm at spec:10.8.2
- */
-static void nfc_shdlc_rcv_i_frame(struct nfc_shdlc *shdlc,
-				  struct sk_buff *skb, int ns, int nr)
-{
-	int x_ns = ns;
-	int y_nr = nr;
-
-	pr_debug("recvd I-frame %d, remote waiting frame %d\n", ns, nr);
-
-	if (shdlc->state != SHDLC_CONNECTED)
-		goto exit;
-
-	if (x_ns != shdlc->nr) {
-		nfc_shdlc_send_s_frame(shdlc, S_FRAME_REJ, shdlc->nr);
-		goto exit;
-	}
-
-	if (shdlc->t1_active == false) {
-		shdlc->t1_active = true;
-		mod_timer(&shdlc->t1_timer,
-			  msecs_to_jiffies(SHDLC_T1_VALUE_MS(shdlc->w)));
-		pr_debug("(re)Start T1(send ack)\n");
-	}
-
-	if (skb->len) {
-		nfc_hci_recv_frame(shdlc->hdev, skb);
-		skb = NULL;
-	}
-
-	shdlc->nr = (shdlc->nr + 1) % 8;
-
-	if (nfc_shdlc_x_lt_y_lteq_z(shdlc->dnr, y_nr, shdlc->ns)) {
-		nfc_shdlc_reset_t2(shdlc, y_nr);
-
-		shdlc->dnr = y_nr;
-	}
-
-exit:
-	if (skb)
-		kfree_skb(skb);
-}
-
-static void nfc_shdlc_rcv_ack(struct nfc_shdlc *shdlc, int y_nr)
-{
-	pr_debug("remote acked up to frame %d excluded\n", y_nr);
-
-	if (nfc_shdlc_x_lt_y_lteq_z(shdlc->dnr, y_nr, shdlc->ns)) {
-		nfc_shdlc_reset_t2(shdlc, y_nr);
-		shdlc->dnr = y_nr;
-	}
-}
-
-static void nfc_shdlc_requeue_ack_pending(struct nfc_shdlc *shdlc)
-{
-	struct sk_buff *skb;
-
-	pr_debug("ns reset to %d\n", shdlc->dnr);
-
-	while ((skb = skb_dequeue_tail(&shdlc->ack_pending_q))) {
-		skb_pull(skb, 2);	/* remove len+control */
-		skb_trim(skb, skb->len - 2);	/* remove crc */
-		skb_queue_head(&shdlc->send_q, skb);
-	}
-	shdlc->ns = shdlc->dnr;
-}
-
-static void nfc_shdlc_rcv_rej(struct nfc_shdlc *shdlc, int y_nr)
-{
-	struct sk_buff *skb;
-
-	pr_debug("remote asks retransmition from frame %d\n", y_nr);
-
-	if (nfc_shdlc_x_lteq_y_lt_z(shdlc->dnr, y_nr, shdlc->ns)) {
-		if (shdlc->t2_active) {
-			del_timer_sync(&shdlc->t2_timer);
-			shdlc->t2_active = false;
-			pr_debug("Stopped T2(retransmit)\n");
-		}
-
-		if (shdlc->dnr != y_nr) {
-			while ((shdlc->dnr = ((shdlc->dnr + 1) % 8)) != y_nr) {
-				skb = skb_dequeue(&shdlc->ack_pending_q);
-				kfree_skb(skb);
-			}
-		}
-
-		nfc_shdlc_requeue_ack_pending(shdlc);
-	}
-}
-
-/* See spec RR:10.8.3 REJ:10.8.4 */
-static void nfc_shdlc_rcv_s_frame(struct nfc_shdlc *shdlc,
-				  enum sframe_type s_frame_type, int nr)
-{
-	struct sk_buff *skb;
-
-	if (shdlc->state != SHDLC_CONNECTED)
-		return;
-
-	switch (s_frame_type) {
-	case S_FRAME_RR:
-		nfc_shdlc_rcv_ack(shdlc, nr);
-		if (shdlc->rnr == true) {	/* see SHDLC 10.7.7 */
-			shdlc->rnr = false;
-			if (shdlc->send_q.qlen == 0) {
-				skb = nfc_shdlc_alloc_skb(shdlc, 0);
-				if (skb)
-					skb_queue_tail(&shdlc->send_q, skb);
-			}
-		}
-		break;
-	case S_FRAME_REJ:
-		nfc_shdlc_rcv_rej(shdlc, nr);
-		break;
-	case S_FRAME_RNR:
-		nfc_shdlc_rcv_ack(shdlc, nr);
-		shdlc->rnr = true;
-		break;
-	default:
-		break;
-	}
-}
-
-static void nfc_shdlc_connect_complete(struct nfc_shdlc *shdlc, int r)
-{
-	pr_debug("result=%d\n", r);
-
-	del_timer_sync(&shdlc->connect_timer);
-
-	if (r == 0) {
-		shdlc->ns = 0;
-		shdlc->nr = 0;
-		shdlc->dnr = 0;
-
-		shdlc->state = SHDLC_CONNECTED;
-	} else {
-		shdlc->state = SHDLC_DISCONNECTED;
-	}
-
-	shdlc->connect_result = r;
-
-	wake_up(shdlc->connect_wq);
-}
-
-static int nfc_shdlc_connect_initiate(struct nfc_shdlc *shdlc)
-{
-	struct sk_buff *skb;
-
-	pr_debug("\n");
-
-	skb = nfc_shdlc_alloc_skb(shdlc, 2);
-	if (skb == NULL)
-		return -ENOMEM;
-
-	*skb_put(skb, 1) = SHDLC_MAX_WINDOW;
-	*skb_put(skb, 1) = SHDLC_SREJ_SUPPORT ? 1 : 0;
-
-	return nfc_shdlc_send_u_frame(shdlc, skb, U_FRAME_RSET);
-}
-
-static int nfc_shdlc_connect_send_ua(struct nfc_shdlc *shdlc)
-{
-	struct sk_buff *skb;
-
-	pr_debug("\n");
-
-	skb = nfc_shdlc_alloc_skb(shdlc, 0);
-	if (skb == NULL)
-		return -ENOMEM;
-
-	return nfc_shdlc_send_u_frame(shdlc, skb, U_FRAME_UA);
-}
-
-static void nfc_shdlc_rcv_u_frame(struct nfc_shdlc *shdlc,
-				  struct sk_buff *skb,
-				  enum uframe_modifier u_frame_modifier)
-{
-	u8 w = SHDLC_MAX_WINDOW;
-	bool srej_support = SHDLC_SREJ_SUPPORT;
-	int r;
-
-	pr_debug("u_frame_modifier=%d\n", u_frame_modifier);
-
-	switch (u_frame_modifier) {
-	case U_FRAME_RSET:
-		if (shdlc->state == SHDLC_NEGOCIATING) {
-			/* we sent RSET, but chip wants to negociate */
-			if (skb->len > 0)
-				w = skb->data[0];
-
-			if (skb->len > 1)
-				srej_support = skb->data[1] & 0x01 ? true :
-					       false;
-
-			if ((w <= SHDLC_MAX_WINDOW) &&
-			    (SHDLC_SREJ_SUPPORT || (srej_support == false))) {
-				shdlc->w = w;
-				shdlc->srej_support = srej_support;
-				r = nfc_shdlc_connect_send_ua(shdlc);
-				nfc_shdlc_connect_complete(shdlc, r);
-			}
-		} else if (shdlc->state == SHDLC_CONNECTED) {
-			/*
-			 * Chip wants to reset link. This is unexpected and
-			 * unsupported.
-			 */
-			shdlc->hard_fault = -ECONNRESET;
-		}
-		break;
-	case U_FRAME_UA:
-		if ((shdlc->state == SHDLC_CONNECTING &&
-		     shdlc->connect_tries > 0) ||
-		    (shdlc->state == SHDLC_NEGOCIATING))
-			nfc_shdlc_connect_complete(shdlc, 0);
-		break;
-	default:
-		break;
-	}
-
-	kfree_skb(skb);
-}
-
-static void nfc_shdlc_handle_rcv_queue(struct nfc_shdlc *shdlc)
-{
-	struct sk_buff *skb;
-	u8 control;
-	int nr;
-	int ns;
-	enum sframe_type s_frame_type;
-	enum uframe_modifier u_frame_modifier;
-
-	if (shdlc->rcv_q.qlen)
-		pr_debug("rcvQlen=%d\n", shdlc->rcv_q.qlen);
-
-	while ((skb = skb_dequeue(&shdlc->rcv_q)) != NULL) {
-		control = skb->data[0];
-		skb_pull(skb, 1);
-		switch (control & SHDLC_CONTROL_HEAD_MASK) {
-		case SHDLC_CONTROL_HEAD_I:
-		case SHDLC_CONTROL_HEAD_I2:
-			ns = (control & SHDLC_CONTROL_NS_MASK) >> 3;
-			nr = control & SHDLC_CONTROL_NR_MASK;
-			nfc_shdlc_rcv_i_frame(shdlc, skb, ns, nr);
-			break;
-		case SHDLC_CONTROL_HEAD_S:
-			s_frame_type = (control & SHDLC_CONTROL_TYPE_MASK) >> 3;
-			nr = control & SHDLC_CONTROL_NR_MASK;
-			nfc_shdlc_rcv_s_frame(shdlc, s_frame_type, nr);
-			kfree_skb(skb);
-			break;
-		case SHDLC_CONTROL_HEAD_U:
-			u_frame_modifier = control & SHDLC_CONTROL_M_MASK;
-			nfc_shdlc_rcv_u_frame(shdlc, skb, u_frame_modifier);
-			break;
-		default:
-			pr_err("UNKNOWN Control=%d\n", control);
-			kfree_skb(skb);
-			break;
-		}
-	}
-}
-
-static int nfc_shdlc_w_used(int ns, int dnr)
-{
-	int unack_count;
-
-	if (dnr <= ns)
-		unack_count = ns - dnr;
-	else
-		unack_count = 8 - dnr + ns;
-
-	return unack_count;
-}
-
-/* Send frames according to algorithm at spec:10.8.1 */
-static void nfc_shdlc_handle_send_queue(struct nfc_shdlc *shdlc)
-{
-	struct sk_buff *skb;
-	int r;
-	unsigned long time_sent;
-
-	if (shdlc->send_q.qlen)
-		pr_debug
-		    ("sendQlen=%d ns=%d dnr=%d rnr=%s w_room=%d unackQlen=%d\n",
-		     shdlc->send_q.qlen, shdlc->ns, shdlc->dnr,
-		     shdlc->rnr == false ? "false" : "true",
-		     shdlc->w - nfc_shdlc_w_used(shdlc->ns, shdlc->dnr),
-		     shdlc->ack_pending_q.qlen);
-
-	while (shdlc->send_q.qlen && shdlc->ack_pending_q.qlen < shdlc->w &&
-	       (shdlc->rnr == false)) {
-
-		if (shdlc->t1_active) {
-			del_timer_sync(&shdlc->t1_timer);
-			shdlc->t1_active = false;
-			pr_debug("Stopped T1(send ack)\n");
-		}
-
-		skb = skb_dequeue(&shdlc->send_q);
-
-		*skb_push(skb, 1) = SHDLC_CONTROL_HEAD_I | (shdlc->ns << 3) |
-				    shdlc->nr;
-
-		pr_debug("Sending I-Frame %d, waiting to rcv %d\n", shdlc->ns,
-			 shdlc->nr);
-	/*	SHDLC_DUMP_SKB("shdlc frame written", skb); */
-
-		nfc_shdlc_add_len_crc(skb);
-
-		r = shdlc->ops->xmit(shdlc, skb);
-		if (r < 0) {
-			shdlc->hard_fault = r;
-			break;
-		}
-
-		shdlc->ns = (shdlc->ns + 1) % 8;
-
-		time_sent = jiffies;
-		*(unsigned long *)skb->cb = time_sent;
-
-		skb_queue_tail(&shdlc->ack_pending_q, skb);
-
-		if (shdlc->t2_active == false) {
-			shdlc->t2_active = true;
-			mod_timer(&shdlc->t2_timer, time_sent +
-				  msecs_to_jiffies(SHDLC_T2_VALUE_MS));
-			pr_debug("Started T2 (retransmit)\n");
-		}
-	}
-}
-
-static void nfc_shdlc_connect_timeout(unsigned long data)
-{
-	struct nfc_shdlc *shdlc = (struct nfc_shdlc *)data;
-
-	pr_debug("\n");
-
-	queue_work(shdlc->sm_wq, &shdlc->sm_work);
-}
-
-static void nfc_shdlc_t1_timeout(unsigned long data)
-{
-	struct nfc_shdlc *shdlc = (struct nfc_shdlc *)data;
-
-	pr_debug("SoftIRQ: need to send ack\n");
-
-	queue_work(shdlc->sm_wq, &shdlc->sm_work);
-}
-
-static void nfc_shdlc_t2_timeout(unsigned long data)
-{
-	struct nfc_shdlc *shdlc = (struct nfc_shdlc *)data;
-
-	pr_debug("SoftIRQ: need to retransmit\n");
-
-	queue_work(shdlc->sm_wq, &shdlc->sm_work);
-}
-
-static void nfc_shdlc_sm_work(struct work_struct *work)
-{
-	struct nfc_shdlc *shdlc = container_of(work, struct nfc_shdlc, sm_work);
-	int r;
-
-	pr_debug("\n");
-
-	mutex_lock(&shdlc->state_mutex);
-
-	switch (shdlc->state) {
-	case SHDLC_DISCONNECTED:
-		skb_queue_purge(&shdlc->rcv_q);
-		skb_queue_purge(&shdlc->send_q);
-		skb_queue_purge(&shdlc->ack_pending_q);
-		break;
-	case SHDLC_CONNECTING:
-		if (shdlc->hard_fault) {
-			nfc_shdlc_connect_complete(shdlc, shdlc->hard_fault);
-			break;
-		}
-
-		if (shdlc->connect_tries++ < 5)
-			r = nfc_shdlc_connect_initiate(shdlc);
-		else
-			r = -ETIME;
-		if (r < 0)
-			nfc_shdlc_connect_complete(shdlc, r);
-		else {
-			mod_timer(&shdlc->connect_timer, jiffies +
-				  msecs_to_jiffies(SHDLC_CONNECT_VALUE_MS));
-
-			shdlc->state = SHDLC_NEGOCIATING;
-		}
-		break;
-	case SHDLC_NEGOCIATING:
-		if (timer_pending(&shdlc->connect_timer) == 0) {
-			shdlc->state = SHDLC_CONNECTING;
-			queue_work(shdlc->sm_wq, &shdlc->sm_work);
-		}
-
-		nfc_shdlc_handle_rcv_queue(shdlc);
-
-		if (shdlc->hard_fault) {
-			nfc_shdlc_connect_complete(shdlc, shdlc->hard_fault);
-			break;
-		}
-		break;
-	case SHDLC_CONNECTED:
-		nfc_shdlc_handle_rcv_queue(shdlc);
-		nfc_shdlc_handle_send_queue(shdlc);
-
-		if (shdlc->t1_active && timer_pending(&shdlc->t1_timer) == 0) {
-			pr_debug
-			    ("Handle T1(send ack) elapsed (T1 now inactive)\n");
-
-			shdlc->t1_active = false;
-			r = nfc_shdlc_send_s_frame(shdlc, S_FRAME_RR,
-						   shdlc->nr);
-			if (r < 0)
-				shdlc->hard_fault = r;
-		}
-
-		if (shdlc->t2_active && timer_pending(&shdlc->t2_timer) == 0) {
-			pr_debug
-			    ("Handle T2(retransmit) elapsed (T2 inactive)\n");
-
-			shdlc->t2_active = false;
-
-			nfc_shdlc_requeue_ack_pending(shdlc);
-			nfc_shdlc_handle_send_queue(shdlc);
-		}
-
-		if (shdlc->hard_fault) {
-			nfc_hci_driver_failure(shdlc->hdev, shdlc->hard_fault);
-		}
-		break;
-	default:
-		break;
-	}
-	mutex_unlock(&shdlc->state_mutex);
-}
-
-/*
- * Called from syscall context to establish shdlc link. Sleeps until
- * link is ready or failure.
- */
-static int nfc_shdlc_connect(struct nfc_shdlc *shdlc)
-{
-	DECLARE_WAIT_QUEUE_HEAD_ONSTACK(connect_wq);
-
-	pr_debug("\n");
-
-	mutex_lock(&shdlc->state_mutex);
-
-	shdlc->state = SHDLC_CONNECTING;
-	shdlc->connect_wq = &connect_wq;
-	shdlc->connect_tries = 0;
-	shdlc->connect_result = 1;
-
-	mutex_unlock(&shdlc->state_mutex);
-
-	queue_work(shdlc->sm_wq, &shdlc->sm_work);
-
-	wait_event(connect_wq, shdlc->connect_result != 1);
-
-	return shdlc->connect_result;
-}
-
-static void nfc_shdlc_disconnect(struct nfc_shdlc *shdlc)
-{
-	pr_debug("\n");
-
-	mutex_lock(&shdlc->state_mutex);
-
-	shdlc->state = SHDLC_DISCONNECTED;
-
-	mutex_unlock(&shdlc->state_mutex);
-
-	queue_work(shdlc->sm_wq, &shdlc->sm_work);
-}
-
-/*
- * Receive an incoming shdlc frame. Frame has already been crc-validated.
- * skb contains only LLC header and payload.
- * If skb == NULL, it is a notification that the link below is dead.
- */
-void nfc_shdlc_recv_frame(struct nfc_shdlc *shdlc, struct sk_buff *skb)
-{
-	if (skb == NULL) {
-		pr_err("NULL Frame -> link is dead\n");
-		shdlc->hard_fault = -EREMOTEIO;
-	} else {
-		SHDLC_DUMP_SKB("incoming frame", skb);
-		skb_queue_tail(&shdlc->rcv_q, skb);
-	}
-
-	queue_work(shdlc->sm_wq, &shdlc->sm_work);
-}
-EXPORT_SYMBOL(nfc_shdlc_recv_frame);
-
-static int nfc_shdlc_open(struct nfc_hci_dev *hdev)
-{
-	struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev);
-	int r;
-
-	pr_debug("\n");
-
-	if (shdlc->ops->open) {
-		r = shdlc->ops->open(shdlc);
-		if (r < 0)
-			return r;
-	}
-
-	r = nfc_shdlc_connect(shdlc);
-	if (r < 0 && shdlc->ops->close)
-		shdlc->ops->close(shdlc);
-
-	return r;
-}
-
-static void nfc_shdlc_close(struct nfc_hci_dev *hdev)
-{
-	struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev);
-
-	pr_debug("\n");
-
-	nfc_shdlc_disconnect(shdlc);
-
-	if (shdlc->ops->close)
-		shdlc->ops->close(shdlc);
-}
-
-static int nfc_shdlc_hci_ready(struct nfc_hci_dev *hdev)
-{
-	struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev);
-	int r = 0;
-
-	pr_debug("\n");
-
-	if (shdlc->ops->hci_ready)
-		r = shdlc->ops->hci_ready(shdlc);
-
-	return r;
-}
-
-static int nfc_shdlc_xmit(struct nfc_hci_dev *hdev, struct sk_buff *skb)
-{
-	struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev);
-
-	SHDLC_DUMP_SKB("queuing HCP packet to shdlc", skb);
-
-	skb_queue_tail(&shdlc->send_q, skb);
-
-	queue_work(shdlc->sm_wq, &shdlc->sm_work);
-
-	return 0;
-}
-
-static int nfc_shdlc_start_poll(struct nfc_hci_dev *hdev,
-				u32 im_protocols, u32 tm_protocols)
-{
-	struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev);
-
-	pr_debug("\n");
-
-	if (shdlc->ops->start_poll)
-		return shdlc->ops->start_poll(shdlc,
-					      im_protocols, tm_protocols);
-
-	return 0;
-}
-
-static int nfc_shdlc_target_from_gate(struct nfc_hci_dev *hdev, u8 gate,
-				      struct nfc_target *target)
-{
-	struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev);
-
-	if (shdlc->ops->target_from_gate)
-		return shdlc->ops->target_from_gate(shdlc, gate, target);
-
-	return -EPERM;
-}
-
-static int nfc_shdlc_complete_target_discovered(struct nfc_hci_dev *hdev,
-						u8 gate,
-						struct nfc_target *target)
-{
-	struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev);
-
-	pr_debug("\n");
-
-	if (shdlc->ops->complete_target_discovered)
-		return shdlc->ops->complete_target_discovered(shdlc, gate,
-							      target);
-
-	return 0;
-}
-
-static int nfc_shdlc_data_exchange(struct nfc_hci_dev *hdev,
-				   struct nfc_target *target,
-				   struct sk_buff *skb,
-				   struct sk_buff **res_skb)
-{
-	struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev);
-
-	if (shdlc->ops->data_exchange)
-		return shdlc->ops->data_exchange(shdlc, target, skb, res_skb);
-
-	return -EPERM;
-}
-
-static int nfc_shdlc_check_presence(struct nfc_hci_dev *hdev,
-				    struct nfc_target *target)
-{
-	struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev);
-
-	if (shdlc->ops->check_presence)
-		return shdlc->ops->check_presence(shdlc, target);
-
-	return 0;
-}
-
-static struct nfc_hci_ops shdlc_ops = {
-	.open = nfc_shdlc_open,
-	.close = nfc_shdlc_close,
-	.hci_ready = nfc_shdlc_hci_ready,
-	.xmit = nfc_shdlc_xmit,
-	.start_poll = nfc_shdlc_start_poll,
-	.target_from_gate = nfc_shdlc_target_from_gate,
-	.complete_target_discovered = nfc_shdlc_complete_target_discovered,
-	.data_exchange = nfc_shdlc_data_exchange,
-	.check_presence = nfc_shdlc_check_presence,
-};
-
-struct nfc_shdlc *nfc_shdlc_allocate(struct nfc_shdlc_ops *ops,
-				     struct nfc_hci_init_data *init_data,
-				     u32 protocols,
-				     int tx_headroom, int tx_tailroom,
-				     int max_link_payload, const char *devname)
-{
-	struct nfc_shdlc *shdlc;
-	int r;
-	char name[32];
-
-	if (ops->xmit == NULL)
-		return NULL;
-
-	shdlc = kzalloc(sizeof(struct nfc_shdlc), GFP_KERNEL);
-	if (shdlc == NULL)
-		return NULL;
-
-	mutex_init(&shdlc->state_mutex);
-	shdlc->ops = ops;
-	shdlc->state = SHDLC_DISCONNECTED;
-
-	init_timer(&shdlc->connect_timer);
-	shdlc->connect_timer.data = (unsigned long)shdlc;
-	shdlc->connect_timer.function = nfc_shdlc_connect_timeout;
-
-	init_timer(&shdlc->t1_timer);
-	shdlc->t1_timer.data = (unsigned long)shdlc;
-	shdlc->t1_timer.function = nfc_shdlc_t1_timeout;
-
-	init_timer(&shdlc->t2_timer);
-	shdlc->t2_timer.data = (unsigned long)shdlc;
-	shdlc->t2_timer.function = nfc_shdlc_t2_timeout;
-
-	shdlc->w = SHDLC_MAX_WINDOW;
-	shdlc->srej_support = SHDLC_SREJ_SUPPORT;
-
-	skb_queue_head_init(&shdlc->rcv_q);
-	skb_queue_head_init(&shdlc->send_q);
-	skb_queue_head_init(&shdlc->ack_pending_q);
-
-	INIT_WORK(&shdlc->sm_work, nfc_shdlc_sm_work);
-	snprintf(name, sizeof(name), "%s_shdlc_sm_wq", devname);
-	shdlc->sm_wq = alloc_workqueue(name, WQ_NON_REENTRANT | WQ_UNBOUND |
-				       WQ_MEM_RECLAIM, 1);
-	if (shdlc->sm_wq == NULL)
-		goto err_allocwq;
-
-	shdlc->client_headroom = tx_headroom;
-	shdlc->client_tailroom = tx_tailroom;
-
-	shdlc->hdev = nfc_hci_allocate_device(&shdlc_ops, init_data, protocols,
-					      tx_headroom + SHDLC_LLC_HEAD_ROOM,
-					      tx_tailroom + SHDLC_LLC_TAIL_ROOM,
-					      max_link_payload);
-	if (shdlc->hdev == NULL)
-		goto err_allocdev;
-
-	nfc_hci_set_clientdata(shdlc->hdev, shdlc);
-
-	r = nfc_hci_register_device(shdlc->hdev);
-	if (r < 0)
-		goto err_regdev;
-
-	return shdlc;
-
-err_regdev:
-	nfc_hci_free_device(shdlc->hdev);
-
-err_allocdev:
-	destroy_workqueue(shdlc->sm_wq);
-
-err_allocwq:
-	kfree(shdlc);
-
-	return NULL;
-}
-EXPORT_SYMBOL(nfc_shdlc_allocate);
-
-void nfc_shdlc_free(struct nfc_shdlc *shdlc)
-{
-	pr_debug("\n");
-
-	nfc_hci_unregister_device(shdlc->hdev);
-	nfc_hci_free_device(shdlc->hdev);
-
-	destroy_workqueue(shdlc->sm_wq);
-
-	skb_queue_purge(&shdlc->rcv_q);
-	skb_queue_purge(&shdlc->send_q);
-	skb_queue_purge(&shdlc->ack_pending_q);
-
-	kfree(shdlc);
-}
-EXPORT_SYMBOL(nfc_shdlc_free);
-
-void nfc_shdlc_set_clientdata(struct nfc_shdlc *shdlc, void *clientdata)
-{
-	pr_debug("\n");
-
-	shdlc->clientdata = clientdata;
-}
-EXPORT_SYMBOL(nfc_shdlc_set_clientdata);
-
-void *nfc_shdlc_get_clientdata(struct nfc_shdlc *shdlc)
-{
-	return shdlc->clientdata;
-}
-EXPORT_SYMBOL(nfc_shdlc_get_clientdata);
-
-struct nfc_hci_dev *nfc_shdlc_get_hci_dev(struct nfc_shdlc *shdlc)
-{
-	return shdlc->hdev;
-}
-EXPORT_SYMBOL(nfc_shdlc_get_hci_dev);
diff --git a/net/nfc/llcp/commands.c b/net/nfc/llcp/commands.c
index b982b5b..c45ccd6 100644
--- a/net/nfc/llcp/commands.c
+++ b/net/nfc/llcp/commands.c
@@ -312,6 +312,8 @@
 
 	skb = llcp_add_header(skb, 0, 0, LLCP_PDU_SYMM);
 
+	nfc_llcp_send_to_raw_sock(local, skb, NFC_LLCP_DIRECTION_TX);
+
 	return nfc_data_exchange(dev, local->target_idx, skb,
 				 nfc_llcp_recv, local);
 }
diff --git a/net/nfc/llcp/llcp.c b/net/nfc/llcp/llcp.c
index 82f0f75..c12c5ef 100644
--- a/net/nfc/llcp/llcp.c
+++ b/net/nfc/llcp/llcp.c
@@ -56,7 +56,7 @@
 	sk_for_each_safe(sk, node, tmp, &local->sockets.head) {
 		llcp_sock = nfc_llcp_sock(sk);
 
-		lock_sock(sk);
+		bh_lock_sock(sk);
 
 		if (sk->sk_state == LLCP_CONNECTED)
 			nfc_put_device(llcp_sock->dev);
@@ -68,26 +68,26 @@
 			list_for_each_entry_safe(lsk, n, &llcp_sock->accept_queue,
 						 accept_queue) {
 				accept_sk = &lsk->sk;
-				lock_sock(accept_sk);
+				bh_lock_sock(accept_sk);
 
 				nfc_llcp_accept_unlink(accept_sk);
 
 				accept_sk->sk_state = LLCP_CLOSED;
 
-				release_sock(accept_sk);
+				bh_unlock_sock(accept_sk);
 
 				sock_orphan(accept_sk);
 			}
 
 			if (listen == true) {
-				release_sock(sk);
+				bh_unlock_sock(sk);
 				continue;
 			}
 		}
 
 		sk->sk_state = LLCP_CLOSED;
 
-		release_sock(sk);
+		bh_unlock_sock(sk);
 
 		sock_orphan(sk);
 
@@ -114,9 +114,9 @@
 	nfc_llcp_socket_release(local, false);
 	del_timer_sync(&local->link_timer);
 	skb_queue_purge(&local->tx_queue);
-	destroy_workqueue(local->tx_wq);
-	destroy_workqueue(local->rx_wq);
-	destroy_workqueue(local->timeout_wq);
+	cancel_work_sync(&local->tx_work);
+	cancel_work_sync(&local->rx_work);
+	cancel_work_sync(&local->timeout_work);
 	kfree_skb(local->rx_pending);
 	kfree(local);
 }
@@ -181,7 +181,7 @@
 
 	pr_err("SYMM timeout\n");
 
-	queue_work(local->timeout_wq, &local->timeout_work);
+	queue_work(system_nrt_wq, &local->timeout_work);
 }
 
 struct nfc_llcp_local *nfc_llcp_find_local(struct nfc_dev *dev)
@@ -426,6 +426,7 @@
 	u8 *miux_tlv, miux_length;
 	__be16 miux;
 	u8 gb_len = 0;
+	int ret = 0;
 
 	version = LLCP_VERSION_11;
 	version_tlv = nfc_llcp_build_tlv(LLCP_TLV_VERSION, &version,
@@ -450,8 +451,8 @@
 	gb_len += ARRAY_SIZE(llcp_magic);
 
 	if (gb_len > NFC_MAX_GT_LEN) {
-		kfree(version_tlv);
-		return -EINVAL;
+		ret = -EINVAL;
+		goto out;
 	}
 
 	gb_cur = local->gb;
@@ -471,12 +472,15 @@
 	memcpy(gb_cur, miux_tlv, miux_length);
 	gb_cur += miux_length;
 
-	kfree(version_tlv);
-	kfree(lto_tlv);
-
 	local->gb_len = gb_len;
 
-	return 0;
+out:
+	kfree(version_tlv);
+	kfree(lto_tlv);
+	kfree(wks_tlv);
+	kfree(miux_tlv);
+
+	return ret;
 }
 
 u8 *nfc_llcp_general_bytes(struct nfc_dev *dev, size_t *general_bytes_len)
@@ -554,6 +558,46 @@
 	sock->recv_ack_n = (sock->recv_n - 1) % 16;
 }
 
+void nfc_llcp_send_to_raw_sock(struct nfc_llcp_local *local,
+			       struct sk_buff *skb, u8 direction)
+{
+	struct hlist_node *node;
+	struct sk_buff *skb_copy = NULL, *nskb;
+	struct sock *sk;
+	u8 *data;
+
+	read_lock(&local->raw_sockets.lock);
+
+	sk_for_each(sk, node, &local->raw_sockets.head) {
+		if (sk->sk_state != LLCP_BOUND)
+			continue;
+
+		if (skb_copy == NULL) {
+			skb_copy = __pskb_copy(skb, NFC_LLCP_RAW_HEADER_SIZE,
+					       GFP_ATOMIC);
+
+			if (skb_copy == NULL)
+				continue;
+
+			data = skb_push(skb_copy, NFC_LLCP_RAW_HEADER_SIZE);
+
+			data[0] = local->dev ? local->dev->idx : 0xFF;
+			data[1] = direction;
+		}
+
+		nskb = skb_clone(skb_copy, GFP_ATOMIC);
+		if (!nskb)
+			continue;
+
+		if (sock_queue_rcv_skb(sk, nskb))
+			kfree_skb(nskb);
+	}
+
+	read_unlock(&local->raw_sockets.lock);
+
+	kfree_skb(skb_copy);
+}
+
 static void nfc_llcp_tx_work(struct work_struct *work)
 {
 	struct nfc_llcp_local *local = container_of(work, struct nfc_llcp_local,
@@ -574,6 +618,9 @@
 				       DUMP_PREFIX_OFFSET, 16, 1,
 				       skb->data, skb->len, true);
 
+			nfc_llcp_send_to_raw_sock(local, skb,
+						  NFC_LLCP_DIRECTION_TX);
+
 			ret = nfc_data_exchange(local->dev, local->target_idx,
 						skb, nfc_llcp_recv, local);
 
@@ -1018,6 +1065,8 @@
 		print_hex_dump(KERN_DEBUG, "LLCP Rx: ", DUMP_PREFIX_OFFSET,
 			       16, 1, skb->data, skb->len, true);
 
+	nfc_llcp_send_to_raw_sock(local, skb, NFC_LLCP_DIRECTION_RX);
+
 	switch (ptype) {
 	case LLCP_PDU_SYMM:
 		pr_debug("SYMM\n");
@@ -1052,7 +1101,7 @@
 
 	}
 
-	queue_work(local->tx_wq, &local->tx_work);
+	queue_work(system_nrt_wq, &local->tx_work);
 	kfree_skb(local->rx_pending);
 	local->rx_pending = NULL;
 
@@ -1071,7 +1120,7 @@
 
 	local->rx_pending = skb_get(skb);
 	del_timer(&local->link_timer);
-	queue_work(local->rx_wq, &local->rx_work);
+	queue_work(system_nrt_wq, &local->rx_work);
 
 	return;
 }
@@ -1086,7 +1135,7 @@
 
 	local->rx_pending = skb_get(skb);
 	del_timer(&local->link_timer);
-	queue_work(local->rx_wq, &local->rx_work);
+	queue_work(system_nrt_wq, &local->rx_work);
 
 	return 0;
 }
@@ -1121,7 +1170,7 @@
 	if (rf_mode == NFC_RF_INITIATOR) {
 		pr_debug("Queueing Tx work\n");
 
-		queue_work(local->tx_wq, &local->tx_work);
+		queue_work(system_nrt_wq, &local->tx_work);
 	} else {
 		mod_timer(&local->link_timer,
 			  jiffies + msecs_to_jiffies(local->remote_lto));
@@ -1130,10 +1179,7 @@
 
 int nfc_llcp_register_device(struct nfc_dev *ndev)
 {
-	struct device *dev = &ndev->dev;
 	struct nfc_llcp_local *local;
-	char name[32];
-	int err;
 
 	local = kzalloc(sizeof(struct nfc_llcp_local), GFP_KERNEL);
 	if (local == NULL)
@@ -1149,41 +1195,15 @@
 
 	skb_queue_head_init(&local->tx_queue);
 	INIT_WORK(&local->tx_work, nfc_llcp_tx_work);
-	snprintf(name, sizeof(name), "%s_llcp_tx_wq", dev_name(dev));
-	local->tx_wq =
-		alloc_workqueue(name,
-				WQ_NON_REENTRANT | WQ_UNBOUND | WQ_MEM_RECLAIM,
-				1);
-	if (local->tx_wq == NULL) {
-		err = -ENOMEM;
-		goto err_local;
-	}
 
 	local->rx_pending = NULL;
 	INIT_WORK(&local->rx_work, nfc_llcp_rx_work);
-	snprintf(name, sizeof(name), "%s_llcp_rx_wq", dev_name(dev));
-	local->rx_wq =
-		alloc_workqueue(name,
-				WQ_NON_REENTRANT | WQ_UNBOUND | WQ_MEM_RECLAIM,
-				1);
-	if (local->rx_wq == NULL) {
-		err = -ENOMEM;
-		goto err_tx_wq;
-	}
 
 	INIT_WORK(&local->timeout_work, nfc_llcp_timeout_work);
-	snprintf(name, sizeof(name), "%s_llcp_timeout_wq", dev_name(dev));
-	local->timeout_wq =
-		alloc_workqueue(name,
-				WQ_NON_REENTRANT | WQ_UNBOUND | WQ_MEM_RECLAIM,
-				1);
-	if (local->timeout_wq == NULL) {
-		err = -ENOMEM;
-		goto err_rx_wq;
-	}
 
-	local->sockets.lock = __RW_LOCK_UNLOCKED(local->sockets.lock);
-	local->connecting_sockets.lock = __RW_LOCK_UNLOCKED(local->connecting_sockets.lock);
+	rwlock_init(&local->sockets.lock);
+	rwlock_init(&local->connecting_sockets.lock);
+	rwlock_init(&local->raw_sockets.lock);
 
 	nfc_llcp_build_gb(local);
 
@@ -1193,17 +1213,6 @@
 	list_add(&llcp_devices, &local->list);
 
 	return 0;
-
-err_rx_wq:
-	destroy_workqueue(local->rx_wq);
-
-err_tx_wq:
-	destroy_workqueue(local->tx_wq);
-
-err_local:
-	kfree(local);
-
-	return 0;
 }
 
 void nfc_llcp_unregister_device(struct nfc_dev *dev)
diff --git a/net/nfc/llcp/llcp.h b/net/nfc/llcp/llcp.h
index 83b8bba..fdb2d24 100644
--- a/net/nfc/llcp/llcp.h
+++ b/net/nfc/llcp/llcp.h
@@ -56,12 +56,9 @@
 
 	struct timer_list link_timer;
 	struct sk_buff_head tx_queue;
-	struct workqueue_struct	*tx_wq;
 	struct work_struct	 tx_work;
-	struct workqueue_struct	*rx_wq;
 	struct work_struct	 rx_work;
 	struct sk_buff *rx_pending;
-	struct workqueue_struct	*timeout_wq;
 	struct work_struct	 timeout_work;
 
 	u32 target_idx;
@@ -89,6 +86,7 @@
 	/* sockets array */
 	struct llcp_sock_list sockets;
 	struct llcp_sock_list connecting_sockets;
+	struct llcp_sock_list raw_sockets;
 };
 
 struct nfc_llcp_sock {
@@ -187,6 +185,8 @@
 u8 nfc_llcp_get_local_ssap(struct nfc_llcp_local *local);
 void nfc_llcp_put_ssap(struct nfc_llcp_local *local, u8 ssap);
 int nfc_llcp_queue_i_frames(struct nfc_llcp_sock *sock);
+void nfc_llcp_send_to_raw_sock(struct nfc_llcp_local *local,
+			       struct sk_buff *skb, u8 direction);
 
 /* Sock API */
 struct sock *nfc_llcp_sock_alloc(struct socket *sock, int type, gfp_t gfp);
diff --git a/net/nfc/llcp/sock.c b/net/nfc/llcp/sock.c
index ddeb9aa..40f056d 100644
--- a/net/nfc/llcp/sock.c
+++ b/net/nfc/llcp/sock.c
@@ -142,6 +142,60 @@
 	return ret;
 }
 
+static int llcp_raw_sock_bind(struct socket *sock, struct sockaddr *addr,
+			      int alen)
+{
+	struct sock *sk = sock->sk;
+	struct nfc_llcp_sock *llcp_sock = nfc_llcp_sock(sk);
+	struct nfc_llcp_local *local;
+	struct nfc_dev *dev;
+	struct sockaddr_nfc_llcp llcp_addr;
+	int len, ret = 0;
+
+	if (!addr || addr->sa_family != AF_NFC)
+		return -EINVAL;
+
+	pr_debug("sk %p addr %p family %d\n", sk, addr, addr->sa_family);
+
+	memset(&llcp_addr, 0, sizeof(llcp_addr));
+	len = min_t(unsigned int, sizeof(llcp_addr), alen);
+	memcpy(&llcp_addr, addr, len);
+
+	lock_sock(sk);
+
+	if (sk->sk_state != LLCP_CLOSED) {
+		ret = -EBADFD;
+		goto error;
+	}
+
+	dev = nfc_get_device(llcp_addr.dev_idx);
+	if (dev == NULL) {
+		ret = -ENODEV;
+		goto error;
+	}
+
+	local = nfc_llcp_find_local(dev);
+	if (local == NULL) {
+		ret = -ENODEV;
+		goto put_dev;
+	}
+
+	llcp_sock->dev = dev;
+	llcp_sock->local = nfc_llcp_local_get(local);
+	llcp_sock->nfc_protocol = llcp_addr.nfc_protocol;
+
+	nfc_llcp_sock_link(&local->raw_sockets, sk);
+
+	sk->sk_state = LLCP_BOUND;
+
+put_dev:
+	nfc_put_device(dev);
+
+error:
+	release_sock(sk);
+	return ret;
+}
+
 static int llcp_sock_listen(struct socket *sock, int backlog)
 {
 	struct sock *sk = sock->sk;
@@ -300,9 +354,6 @@
 	pr_debug("%p %d %d %d\n", sk, llcp_sock->target_idx,
 		 llcp_sock->dsap, llcp_sock->ssap);
 
-	if (llcp_sock == NULL || llcp_sock->dev == NULL)
-		return -EBADFD;
-
 	uaddr->sa_family = AF_NFC;
 
 	*len = sizeof(struct sockaddr_nfc_llcp);
@@ -421,7 +472,10 @@
 
 	release_sock(sk);
 
-	nfc_llcp_sock_unlink(&local->sockets, sk);
+	if (sock->type == SOCK_RAW)
+		nfc_llcp_sock_unlink(&local->raw_sockets, sk);
+	else
+		nfc_llcp_sock_unlink(&local->sockets, sk);
 
 out:
 	sock_orphan(sk);
@@ -617,7 +671,7 @@
 	if (!(flags & MSG_PEEK)) {
 
 		/* SOCK_STREAM: re-queue skb if it contains unreceived data */
-		if (sk->sk_type == SOCK_STREAM) {
+		if (sk->sk_type == SOCK_STREAM || sk->sk_type == SOCK_RAW) {
 			skb_pull(skb, copied);
 			if (skb->len) {
 				skb_queue_head(&sk->sk_receive_queue, skb);
@@ -658,6 +712,26 @@
 	.mmap           = sock_no_mmap,
 };
 
+static const struct proto_ops llcp_rawsock_ops = {
+	.family         = PF_NFC,
+	.owner          = THIS_MODULE,
+	.bind           = llcp_raw_sock_bind,
+	.connect        = sock_no_connect,
+	.release        = llcp_sock_release,
+	.socketpair     = sock_no_socketpair,
+	.accept         = sock_no_accept,
+	.getname        = llcp_sock_getname,
+	.poll           = llcp_sock_poll,
+	.ioctl          = sock_no_ioctl,
+	.listen         = sock_no_listen,
+	.shutdown       = sock_no_shutdown,
+	.setsockopt     = sock_no_setsockopt,
+	.getsockopt     = sock_no_getsockopt,
+	.sendmsg        = sock_no_sendmsg,
+	.recvmsg        = llcp_sock_recvmsg,
+	.mmap           = sock_no_mmap,
+};
+
 static void llcp_sock_destruct(struct sock *sk)
 {
 	struct nfc_llcp_sock *llcp_sock = nfc_llcp_sock(sk);
@@ -735,10 +809,15 @@
 
 	pr_debug("%p\n", sock);
 
-	if (sock->type != SOCK_STREAM && sock->type != SOCK_DGRAM)
+	if (sock->type != SOCK_STREAM &&
+	    sock->type != SOCK_DGRAM &&
+	    sock->type != SOCK_RAW)
 		return -ESOCKTNOSUPPORT;
 
-	sock->ops = &llcp_sock_ops;
+	if (sock->type == SOCK_RAW)
+		sock->ops = &llcp_rawsock_ops;
+	else
+		sock->ops = &llcp_sock_ops;
 
 	sk = nfc_llcp_sock_alloc(sock, sock->type, GFP_ATOMIC);
 	if (sk == NULL)
diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c
index f81efe1..acf9abb 100644
--- a/net/nfc/nci/core.c
+++ b/net/nfc/nci/core.c
@@ -176,6 +176,27 @@
 		     (1 + ((*num) * sizeof(struct disc_map_config))), &cmd);
 }
 
+struct nci_set_config_param {
+	__u8	id;
+	size_t	len;
+	__u8	*val;
+};
+
+static void nci_set_config_req(struct nci_dev *ndev, unsigned long opt)
+{
+	struct nci_set_config_param *param = (struct nci_set_config_param *)opt;
+	struct nci_core_set_config_cmd cmd;
+
+	BUG_ON(param->len > NCI_MAX_PARAM_LEN);
+
+	cmd.num_params = 1;
+	cmd.param.id = param->id;
+	cmd.param.len = param->len;
+	memcpy(cmd.param.val, param->val, param->len);
+
+	nci_send_cmd(ndev, NCI_OP_CORE_SET_CONFIG_CMD, (3 + param->len), &cmd);
+}
+
 static void nci_rf_discover_req(struct nci_dev *ndev, unsigned long opt)
 {
 	struct nci_rf_disc_cmd cmd;
@@ -388,6 +409,32 @@
 	return nci_close_device(ndev);
 }
 
+static int nci_set_local_general_bytes(struct nfc_dev *nfc_dev)
+{
+	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
+	struct nci_set_config_param param;
+	__u8 local_gb[NFC_MAX_GT_LEN];
+	int i, rc = 0;
+
+	param.val = nfc_get_local_general_bytes(nfc_dev, &param.len);
+	if ((param.val == NULL) || (param.len == 0))
+		return rc;
+
+	if (param.len > NCI_MAX_PARAM_LEN)
+		return -EINVAL;
+
+	for (i = 0; i < param.len; i++)
+		local_gb[param.len-1-i] = param.val[i];
+
+	param.id = NCI_PN_ATR_REQ_GEN_BYTES;
+	param.val = local_gb;
+
+	rc = nci_request(ndev, nci_set_config_req, (unsigned long)&param,
+			 msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT));
+
+	return rc;
+}
+
 static int nci_start_poll(struct nfc_dev *nfc_dev,
 			  __u32 im_protocols, __u32 tm_protocols)
 {
@@ -415,6 +462,14 @@
 			return -EBUSY;
 	}
 
+	if (im_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");
+			return rc;
+		}
+	}
+
 	rc = nci_request(ndev, nci_rf_discover_req, im_protocols,
 			 msecs_to_jiffies(NCI_RF_DISC_TIMEOUT));
 
@@ -509,7 +564,7 @@
 {
 	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
 
-	pr_debug("target_idx %d\n", target->idx);
+	pr_debug("entry\n");
 
 	if (!ndev->target_active_prot) {
 		pr_err("unable to deactivate target, no active target\n");
@@ -524,6 +579,38 @@
 	}
 }
 
+
+static int nci_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
+			   __u8 comm_mode, __u8 *gb, size_t gb_len)
+{
+	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
+	int rc;
+
+	pr_debug("target_idx %d, comm_mode %d\n", target->idx, comm_mode);
+
+	rc = nci_activate_target(nfc_dev, target, NFC_PROTO_NFC_DEP);
+	if (rc)
+		return rc;
+
+	rc = nfc_set_remote_general_bytes(nfc_dev, ndev->remote_gb,
+					  ndev->remote_gb_len);
+	if (!rc)
+		rc = nfc_dep_link_is_up(nfc_dev, target->idx, NFC_COMM_PASSIVE,
+					NFC_RF_INITIATOR);
+
+	return rc;
+}
+
+static int nci_dep_link_down(struct nfc_dev *nfc_dev)
+{
+	pr_debug("entry\n");
+
+	nci_deactivate_target(nfc_dev, NULL);
+
+	return 0;
+}
+
+
 static int nci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target,
 			  struct sk_buff *skb,
 			  data_exchange_cb_t cb, void *cb_context)
@@ -557,6 +644,8 @@
 	.dev_down = nci_dev_down,
 	.start_poll = nci_start_poll,
 	.stop_poll = nci_stop_poll,
+	.dep_link_up = nci_dep_link_up,
+	.dep_link_down = nci_dep_link_down,
 	.activate_target = nci_activate_target,
 	.deactivate_target = nci_deactivate_target,
 	.im_transceive = nci_transceive,
diff --git a/net/nfc/nci/ntf.c b/net/nfc/nci/ntf.c
index af7a93b..b2aa98e 100644
--- a/net/nfc/nci/ntf.c
+++ b/net/nfc/nci/ntf.c
@@ -176,6 +176,8 @@
 			protocol = NFC_PROTO_ISO14443_B_MASK;
 	else if (rf_protocol == NCI_RF_PROTOCOL_T3T)
 		protocol = NFC_PROTO_FELICA_MASK;
+	else if (rf_protocol == NCI_RF_PROTOCOL_NFC_DEP)
+		protocol = NFC_PROTO_NFC_DEP_MASK;
 	else
 		protocol = 0;
 
@@ -361,6 +363,33 @@
 	return NCI_STATUS_OK;
 }
 
+static int nci_extract_activation_params_nfc_dep(struct nci_dev *ndev,
+			struct nci_rf_intf_activated_ntf *ntf, __u8 *data)
+{
+	struct activation_params_poll_nfc_dep *poll;
+	int i;
+
+	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);
+		pr_debug("atr_res_len %d\n", poll->atr_res_len);
+		if (poll->atr_res_len > 0) {
+			for (i = 0; i < poll->atr_res_len; i++)
+				poll->atr_res[poll->atr_res_len-1-i] = data[i];
+		}
+		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_target_auto_activated(struct nci_dev *ndev,
 				      struct nci_rf_intf_activated_ntf *ntf)
 {
@@ -454,6 +483,11 @@
 								    &ntf, data);
 			break;
 
+		case NCI_RF_INTERFACE_NFC_DEP:
+			err = nci_extract_activation_params_nfc_dep(ndev,
+								    &ntf, data);
+			break;
+
 		case NCI_RF_INTERFACE_FRAME:
 			/* no activation params */
 			break;
@@ -473,6 +507,24 @@
 
 		/* set the available credits to initial value */
 		atomic_set(&ndev->credits_cnt, ndev->initial_num_credits);
+
+		/* 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);
+			}
+		}
 	}
 
 	if (atomic_read(&ndev->state) == NCI_DISCOVERY) {
diff --git a/net/nfc/nci/rsp.c b/net/nfc/nci/rsp.c
index 3003c33..dd072f3 100644
--- a/net/nfc/nci/rsp.c
+++ b/net/nfc/nci/rsp.c
@@ -119,6 +119,16 @@
 	nci_req_complete(ndev, rsp_1->status);
 }
 
+static void nci_core_set_config_rsp_packet(struct nci_dev *ndev,
+					   struct sk_buff *skb)
+{
+	struct nci_core_set_config_rsp *rsp = (void *) skb->data;
+
+	pr_debug("status 0x%x\n", rsp->status);
+
+	nci_req_complete(ndev, rsp->status);
+}
+
 static void nci_rf_disc_map_rsp_packet(struct nci_dev *ndev,
 				       struct sk_buff *skb)
 {
@@ -194,6 +204,10 @@
 		nci_core_init_rsp_packet(ndev, skb);
 		break;
 
+	case NCI_OP_CORE_SET_CONFIG_RSP:
+		nci_core_set_config_rsp_packet(ndev, skb);
+		break;
+
 	case NCI_OP_RF_DISCOVER_MAP_RSP:
 		nci_rf_disc_map_rsp_packet(ndev, skb);
 		break;
diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c
index 4bbb70e..c1b5285 100644
--- a/net/nfc/netlink.c
+++ b/net/nfc/netlink.c
@@ -761,31 +761,63 @@
 	},
 };
 
+
+struct urelease_work {
+	struct	work_struct w;
+	int	portid;
+};
+
+static void nfc_urelease_event_work(struct work_struct *work)
+{
+	struct urelease_work *w = container_of(work, struct urelease_work, w);
+	struct class_dev_iter iter;
+	struct nfc_dev *dev;
+
+	pr_debug("portid %d\n", w->portid);
+
+	mutex_lock(&nfc_devlist_mutex);
+
+	nfc_device_iter_init(&iter);
+	dev = nfc_device_iter_next(&iter);
+
+	while (dev) {
+		mutex_lock(&dev->genl_data.genl_data_mutex);
+
+		if (dev->genl_data.poll_req_portid == w->portid) {
+			nfc_stop_poll(dev);
+			dev->genl_data.poll_req_portid = 0;
+		}
+
+		mutex_unlock(&dev->genl_data.genl_data_mutex);
+
+		dev = nfc_device_iter_next(&iter);
+	}
+
+	nfc_device_iter_exit(&iter);
+
+	mutex_unlock(&nfc_devlist_mutex);
+
+	kfree(w);
+}
+
 static int nfc_genl_rcv_nl_event(struct notifier_block *this,
 				 unsigned long event, void *ptr)
 {
 	struct netlink_notify *n = ptr;
-	struct class_dev_iter iter;
-	struct nfc_dev *dev;
+	struct urelease_work *w;
 
 	if (event != NETLINK_URELEASE || n->protocol != NETLINK_GENERIC)
 		goto out;
 
 	pr_debug("NETLINK_URELEASE event from id %d\n", n->portid);
 
-	nfc_device_iter_init(&iter);
-	dev = nfc_device_iter_next(&iter);
-
-	while (dev) {
-		if (dev->genl_data.poll_req_portid == n->portid) {
-			nfc_stop_poll(dev);
-			dev->genl_data.poll_req_portid = 0;
-		}
-		dev = nfc_device_iter_next(&iter);
+	w = kmalloc(sizeof(*w), GFP_ATOMIC);
+	if (w) {
+		INIT_WORK((struct work_struct *) w, nfc_urelease_event_work);
+		w->portid = n->portid;
+		schedule_work((struct work_struct *) w);
 	}
 
-	nfc_device_iter_exit(&iter);
-
 out:
 	return NOTIFY_DONE;
 }
diff --git a/net/rfkill/core.c b/net/rfkill/core.c
index c275bad..a5c9527 100644
--- a/net/rfkill/core.c
+++ b/net/rfkill/core.c
@@ -270,6 +270,7 @@
 static void rfkill_set_block(struct rfkill *rfkill, bool blocked)
 {
 	unsigned long flags;
+	bool prev, curr;
 	int err;
 
 	if (unlikely(rfkill->dev.power.power_state.event & PM_EVENT_SLEEP))
@@ -284,6 +285,8 @@
 		rfkill->ops->query(rfkill, rfkill->data);
 
 	spin_lock_irqsave(&rfkill->lock, flags);
+	prev = rfkill->state & RFKILL_BLOCK_SW;
+
 	if (rfkill->state & RFKILL_BLOCK_SW)
 		rfkill->state |= RFKILL_BLOCK_SW_PREV;
 	else
@@ -313,10 +316,13 @@
 	}
 	rfkill->state &= ~RFKILL_BLOCK_SW_SETCALL;
 	rfkill->state &= ~RFKILL_BLOCK_SW_PREV;
+	curr = rfkill->state & RFKILL_BLOCK_SW;
 	spin_unlock_irqrestore(&rfkill->lock, flags);
 
 	rfkill_led_trigger_event(rfkill);
-	rfkill_event(rfkill);
+
+	if (prev != curr)
+		rfkill_event(rfkill);
 }
 
 #ifdef CONFIG_RFKILL_INPUT
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index ec7fcee..8016fee 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -612,6 +612,17 @@
 }
 EXPORT_SYMBOL(cfg80211_del_sta);
 
+void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr,
+			  enum nl80211_connect_failed_reason reason,
+			  gfp_t gfp)
+{
+	struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
+	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+	nl80211_send_conn_failed_event(rdev, dev, mac_addr, reason, gfp);
+}
+EXPORT_SYMBOL(cfg80211_conn_failed);
+
 struct cfg80211_mgmt_registration {
 	struct list_head list;
 
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 139946d..0418a6d 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -8364,6 +8364,40 @@
 	nlmsg_free(msg);
 }
 
+void nl80211_send_conn_failed_event(struct cfg80211_registered_device *rdev,
+				    struct net_device *dev, const u8 *mac_addr,
+				    enum nl80211_connect_failed_reason reason,
+				    gfp_t gfp)
+{
+	struct sk_buff *msg;
+	void *hdr;
+
+	msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
+	if (!msg)
+		return;
+
+	hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CONN_FAILED);
+	if (!hdr) {
+		nlmsg_free(msg);
+		return;
+	}
+
+	if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr) ||
+	    nla_put_u32(msg, NL80211_ATTR_CONN_FAILED_REASON, reason))
+		goto nla_put_failure;
+
+	genlmsg_end(msg, hdr);
+
+	genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
+				nl80211_mlme_mcgrp.id, gfp);
+	return;
+
+ nla_put_failure:
+	genlmsg_cancel(msg, hdr);
+	nlmsg_free(msg);
+}
+
 static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd,
 				       const u8 *addr, gfp_t gfp)
 {
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h
index 9f2616f..f615351 100644
--- a/net/wireless/nl80211.h
+++ b/net/wireless/nl80211.h
@@ -91,6 +91,11 @@
 				struct net_device *dev, const u8 *mac_addr,
 				gfp_t gfp);
 
+void nl80211_send_conn_failed_event(struct cfg80211_registered_device *rdev,
+				    struct net_device *dev, const u8 *mac_addr,
+				    enum nl80211_connect_failed_reason reason,
+				    gfp_t gfp);
+
 int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
 		      struct wireless_dev *wdev, u32 nlpid,
 		      int freq, int sig_dbm,
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 1ad04e5..8448239 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -504,9 +504,11 @@
  *
  * This lets us know if a specific frequency rule is or is not relevant to
  * a specific frequency's band. Bands are device specific and artificial
- * definitions (the "2.4 GHz band" and the "5 GHz band"), however it is
- * safe for now to assume that a frequency rule should not be part of a
- * frequency's band if the start freq or end freq are off by more than 2 GHz.
+ * definitions (the "2.4 GHz band", the "5 GHz band" and the "60GHz band"),
+ * however it is safe for now to assume that a frequency rule should not be
+ * part of a frequency's band if the start freq or end freq are off by more
+ * than 2 GHz for the 2.4 and 5 GHz bands, and by more than 10 GHz for the
+ * 60 GHz band.
  * This resolution can be lowered and should be considered as we add
  * regulatory rule support for other "bands".
  **/
@@ -514,9 +516,16 @@
 	u32 freq_khz)
 {
 #define ONE_GHZ_IN_KHZ	1000000
-	if (abs(freq_khz - freq_range->start_freq_khz) <= (2 * ONE_GHZ_IN_KHZ))
+	/*
+	 * From 802.11ad: directional multi-gigabit (DMG):
+	 * Pertaining to operation in a frequency band containing a channel
+	 * with the Channel starting frequency above 45 GHz.
+	 */
+	u32 limit = freq_khz > 45 * ONE_GHZ_IN_KHZ ?
+			10 * ONE_GHZ_IN_KHZ : 2 * ONE_GHZ_IN_KHZ;
+	if (abs(freq_khz - freq_range->start_freq_khz) <= limit)
 		return true;
-	if (abs(freq_khz - freq_range->end_freq_khz) <= (2 * ONE_GHZ_IN_KHZ))
+	if (abs(freq_khz - freq_range->end_freq_khz) <= limit)
 		return true;
 	return false;
 #undef ONE_GHZ_IN_KHZ
@@ -2193,7 +2202,6 @@
 static int __set_regdom(const struct ieee80211_regdomain *rd)
 {
 	const struct ieee80211_regdomain *intersected_rd = NULL;
-	struct cfg80211_registered_device *rdev = NULL;
 	struct wiphy *request_wiphy;
 	/* Some basic sanity checks first */
 
@@ -2305,24 +2313,7 @@
 		return 0;
 	}
 
-	if (!intersected_rd)
-		return -EINVAL;
-
-	rdev = wiphy_to_dev(request_wiphy);
-
-	rdev->country_ie_alpha2[0] = rd->alpha2[0];
-	rdev->country_ie_alpha2[1] = rd->alpha2[1];
-	rdev->env = last_request->country_ie_env;
-
-	BUG_ON(intersected_rd == rd);
-
-	kfree(rd);
-	rd = NULL;
-
-	reset_regdomains(false);
-	cfg80211_regdomain = intersected_rd;
-
-	return 0;
+	return -EINVAL;
 }