Browse Source

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

David S. Miller 15 years ago
parent
commit
7905e357eb
82 changed files with 9032 additions and 1503 deletions
  1. 17 18
      drivers/net/ps3_gelic_wireless.c
  2. 1 1
      drivers/net/wireless/ath/Kconfig
  3. 21 0
      drivers/net/wireless/ath/ath9k/Kconfig
  4. 10 0
      drivers/net/wireless/ath/ath9k/Makefile
  5. 18 7
      drivers/net/wireless/ath/ath9k/calib.c
  6. 421 0
      drivers/net/wireless/ath/ath9k/common.c
  7. 17 0
      drivers/net/wireless/ath/ath9k/common.h
  8. 993 0
      drivers/net/wireless/ath/ath9k/hif_usb.c
  9. 105 0
      drivers/net/wireless/ath/ath9k/hif_usb.h
  10. 441 0
      drivers/net/wireless/ath/ath9k/htc.h
  11. 260 0
      drivers/net/wireless/ath/ath9k/htc_drv_beacon.c
  12. 713 0
      drivers/net/wireless/ath/ath9k/htc_drv_init.c
  13. 1626 0
      drivers/net/wireless/ath/ath9k/htc_drv_main.c
  14. 604 0
      drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
  15. 463 0
      drivers/net/wireless/ath/ath9k/htc_hst.c
  16. 246 0
      drivers/net/wireless/ath/ath9k/htc_hst.h
  17. 68 66
      drivers/net/wireless/ath/ath9k/hw.c
  18. 8 0
      drivers/net/wireless/ath/ath9k/hw.h
  19. 3 0
      drivers/net/wireless/ath/ath9k/init.c
  20. 102 39
      drivers/net/wireless/ath/ath9k/initvals.h
  21. 1 1
      drivers/net/wireless/ath/ath9k/mac.c
  22. 26 0
      drivers/net/wireless/ath/ath9k/mac.h
  23. 2 2
      drivers/net/wireless/ath/ath9k/rc.h
  24. 3 0
      drivers/net/wireless/ath/ath9k/reg.h
  25. 319 0
      drivers/net/wireless/ath/ath9k/wmi.c
  26. 126 0
      drivers/net/wireless/ath/ath9k/wmi.h
  27. 1 0
      drivers/net/wireless/ath/debug.h
  28. 2 3
      drivers/net/wireless/b43/main.c
  29. 41 42
      drivers/net/wireless/ipw2x00/ipw2200.c
  30. 7 2
      drivers/net/wireless/iwlwifi/iwl-1000.c
  31. 52 31
      drivers/net/wireless/iwlwifi/iwl-3945-rs.c
  32. 8 14
      drivers/net/wireless/iwlwifi/iwl-3945.c
  33. 0 1
      drivers/net/wireless/iwlwifi/iwl-3945.h
  34. 4 1
      drivers/net/wireless/iwlwifi/iwl-4965.c
  35. 20 8
      drivers/net/wireless/iwlwifi/iwl-5000.c
  36. 18 6
      drivers/net/wireless/iwlwifi/iwl-6000.c
  37. 61 63
      drivers/net/wireless/iwlwifi/iwl-agn-rs.c
  38. 6 1
      drivers/net/wireless/iwlwifi/iwl-agn-rs.h
  39. 95 59
      drivers/net/wireless/iwlwifi/iwl-agn.c
  40. 99 3
      drivers/net/wireless/iwlwifi/iwl-core.c
  41. 18 1
      drivers/net/wireless/iwlwifi/iwl-core.h
  42. 22 1
      drivers/net/wireless/iwlwifi/iwl-dev.h
  43. 127 28
      drivers/net/wireless/iwlwifi/iwl-rx.c
  44. 375 255
      drivers/net/wireless/iwlwifi/iwl-sta.c
  45. 15 5
      drivers/net/wireless/iwlwifi/iwl-sta.h
  46. 2 0
      drivers/net/wireless/iwlwifi/iwl-tx.c
  47. 58 14
      drivers/net/wireless/iwlwifi/iwl3945-base.c
  48. 21 1
      drivers/net/wireless/libertas/assoc.c
  49. 1 0
      drivers/net/wireless/libertas/dev.h
  50. 1 0
      drivers/net/wireless/libertas/main.c
  51. 4 0
      drivers/net/wireless/libertas/wext.c
  52. 46 48
      drivers/net/wireless/orinoco/wext.c
  53. 97 115
      drivers/net/wireless/ray_cs.c
  54. 7 0
      drivers/net/wireless/wl12xx/wl1271.h
  55. 9 16
      drivers/net/wireless/wl12xx/wl1271_acx.c
  56. 7 61
      drivers/net/wireless/wl12xx/wl1271_acx.h
  57. 8 0
      drivers/net/wireless/wl12xx/wl1271_boot.c
  58. 79 139
      drivers/net/wireless/wl12xx/wl1271_cmd.c
  59. 7 5
      drivers/net/wireless/wl12xx/wl1271_cmd.h
  60. 269 61
      drivers/net/wireless/wl12xx/wl1271_conf.h
  61. 5 7
      drivers/net/wireless/wl12xx/wl1271_debugfs.c
  62. 2 0
      drivers/net/wireless/wl12xx/wl1271_event.c
  63. 2 2
      drivers/net/wireless/wl12xx/wl1271_init.c
  64. 13 0
      drivers/net/wireless/wl12xx/wl1271_io.h
  65. 303 193
      drivers/net/wireless/wl12xx/wl1271_main.c
  66. 29 45
      drivers/net/wireless/wl12xx/wl1271_sdio.c
  67. 8 27
      drivers/net/wireless/wl12xx/wl1271_spi.c
  68. 0 3
      drivers/net/wireless/wl12xx/wl1271_tx.h
  69. 26 26
      drivers/net/wireless/wl3501_cs.c
  70. 50 0
      include/linux/nl80211.h
  71. 3 1
      include/linux/wireless.h
  72. 19 0
      include/net/cfg80211.h
  73. 52 2
      include/net/mac80211.h
  74. 27 0
      net/mac80211/cfg.c
  75. 2 2
      net/mac80211/ieee80211_i.h
  76. 1 1
      net/mac80211/iface.c
  77. 72 7
      net/mac80211/mlme.c
  78. 0 2
      net/mac80211/tx.c
  79. 13 0
      net/wireless/mlme.c
  80. 131 0
      net/wireless/nl80211.c
  81. 6 0
      net/wireless/nl80211.h
  82. 67 67
      net/wireless/wext-core.c

+ 17 - 18
drivers/net/ps3_gelic_wireless.c

@@ -2279,26 +2279,25 @@ void gelic_wl_interrupt(struct net_device *netdev, u64 status)
 /*
 /*
  * driver helpers
  * driver helpers
  */
  */
-#define IW_IOCTL(n) [(n) - SIOCSIWCOMMIT]
 static const iw_handler gelic_wl_wext_handler[] =
 static const iw_handler gelic_wl_wext_handler[] =
 {
 {
-	IW_IOCTL(SIOCGIWNAME)		= gelic_wl_get_name,
-	IW_IOCTL(SIOCGIWRANGE)		= gelic_wl_get_range,
-	IW_IOCTL(SIOCSIWSCAN)		= gelic_wl_set_scan,
-	IW_IOCTL(SIOCGIWSCAN)		= gelic_wl_get_scan,
-	IW_IOCTL(SIOCSIWAUTH)		= gelic_wl_set_auth,
-	IW_IOCTL(SIOCGIWAUTH)		= gelic_wl_get_auth,
-	IW_IOCTL(SIOCSIWESSID)		= gelic_wl_set_essid,
-	IW_IOCTL(SIOCGIWESSID)		= gelic_wl_get_essid,
-	IW_IOCTL(SIOCSIWENCODE)		= gelic_wl_set_encode,
-	IW_IOCTL(SIOCGIWENCODE)		= gelic_wl_get_encode,
-	IW_IOCTL(SIOCSIWAP)		= gelic_wl_set_ap,
-	IW_IOCTL(SIOCGIWAP)		= gelic_wl_get_ap,
-	IW_IOCTL(SIOCSIWENCODEEXT)	= gelic_wl_set_encodeext,
-	IW_IOCTL(SIOCGIWENCODEEXT)	= gelic_wl_get_encodeext,
-	IW_IOCTL(SIOCSIWMODE)		= gelic_wl_set_mode,
-	IW_IOCTL(SIOCGIWMODE)		= gelic_wl_get_mode,
-	IW_IOCTL(SIOCGIWNICKN)		= gelic_wl_get_nick,
+	IW_HANDLER(SIOCGIWNAME, gelic_wl_get_name),
+	IW_HANDLER(SIOCGIWRANGE, gelic_wl_get_range),
+	IW_HANDLER(SIOCSIWSCAN, gelic_wl_set_scan),
+	IW_HANDLER(SIOCGIWSCAN, gelic_wl_get_scan),
+	IW_HANDLER(SIOCSIWAUTH, gelic_wl_set_auth),
+	IW_HANDLER(SIOCGIWAUTH, gelic_wl_get_auth),
+	IW_HANDLER(SIOCSIWESSID, gelic_wl_set_essid),
+	IW_HANDLER(SIOCGIWESSID, gelic_wl_get_essid),
+	IW_HANDLER(SIOCSIWENCODE, gelic_wl_set_encode),
+	IW_HANDLER(SIOCGIWENCODE, gelic_wl_get_encode),
+	IW_HANDLER(SIOCSIWAP, gelic_wl_set_ap),
+	IW_HANDLER(SIOCGIWAP, gelic_wl_get_ap),
+	IW_HANDLER(SIOCSIWENCODEEXT, gelic_wl_set_encodeext),
+	IW_HANDLER(SIOCGIWENCODEEXT, gelic_wl_get_encodeext),
+	IW_HANDLER(SIOCSIWMODE, gelic_wl_set_mode),
+	IW_HANDLER(SIOCGIWMODE, gelic_wl_get_mode),
+	IW_HANDLER(SIOCGIWNICKN, gelic_wl_get_nick),
 };
 };
 
 
 static const struct iw_handler_def gelic_wl_wext_handler_def = {
 static const struct iw_handler_def gelic_wl_wext_handler_def = {

+ 1 - 1
drivers/net/wireless/ath/Kconfig

@@ -3,7 +3,7 @@ menuconfig ATH_COMMON
 	depends on CFG80211
 	depends on CFG80211
 	---help---
 	---help---
 	  This will enable the support for the Atheros wireless drivers.
 	  This will enable the support for the Atheros wireless drivers.
-	  ath5k, ath9k and ar9170 drivers share some common code, this option
+	  ath5k, ath9k, ath9k_htc and ar9170 drivers share some common code, this option
 	  enables the common ath.ko module which shares common helpers.
 	  enables the common ath.ko module which shares common helpers.
 
 
 	  For more information and documentation on this module you can visit:
 	  For more information and documentation on this module you can visit:

+ 21 - 0
drivers/net/wireless/ath/ath9k/Kconfig

@@ -32,3 +32,24 @@ config ATH9K_DEBUGFS
 
 
 	  Also required for changing debug message flags at run time.
 	  Also required for changing debug message flags at run time.
 
 
+config ATH9K_HTC
+       tristate "Atheros HTC based wireless cards support"
+       depends on USB && MAC80211
+       select ATH9K_HW
+       select MAC80211_LEDS
+       select LEDS_CLASS
+       select NEW_LEDS
+       select ATH9K_COMMON
+       ---help---
+	 Support for Atheros HTC based cards.
+	 Chipsets supported: AR9271
+
+	 For more information: http://wireless.kernel.org/en/users/Drivers/ath9k_htc
+
+	 The built module will be ath9k_htc.
+
+config ATH9K_HTC_DEBUGFS
+	bool "Atheros ath9k_htc debugging"
+	depends on ATH9K_HTC && DEBUG_FS
+	---help---
+	  Say Y, if you need access to ath9k_htc's statistics.

+ 10 - 0
drivers/net/wireless/ath/ath9k/Makefile

@@ -28,3 +28,13 @@ obj-$(CONFIG_ATH9K_HW) += ath9k_hw.o
 
 
 obj-$(CONFIG_ATH9K_COMMON) += ath9k_common.o
 obj-$(CONFIG_ATH9K_COMMON) += ath9k_common.o
 ath9k_common-y:=	common.o
 ath9k_common-y:=	common.o
+
+ath9k_htc-y +=	htc_hst.o \
+		hif_usb.o \
+		wmi.o \
+		htc_drv_txrx.o \
+		htc_drv_main.o \
+		htc_drv_beacon.o \
+		htc_drv_init.o
+
+obj-$(CONFIG_ATH9K_HTC) += ath9k_htc.o

+ 18 - 7
drivers/net/wireless/ath/ath9k/calib.c

@@ -101,9 +101,13 @@ static void ath9k_hw_do_getnf(struct ath_hw *ah,
 		nf = 0 - ((nf ^ 0x1ff) + 1);
 		nf = 0 - ((nf ^ 0x1ff) + 1);
 	ath_print(common, ATH_DBG_CALIBRATE,
 	ath_print(common, ATH_DBG_CALIBRATE,
 		  "NF calibrated [ctl] [chain 0] is %d\n", nf);
 		  "NF calibrated [ctl] [chain 0] is %d\n", nf);
+
+	if (AR_SREV_9271(ah) && (nf >= -114))
+		nf = -116;
+
 	nfarray[0] = nf;
 	nfarray[0] = nf;
 
 
-	if (!AR_SREV_9285(ah)) {
+	if (!AR_SREV_9285(ah) && !AR_SREV_9271(ah)) {
 		if (AR_SREV_9280_10_OR_LATER(ah))
 		if (AR_SREV_9280_10_OR_LATER(ah))
 			nf = MS(REG_READ(ah, AR_PHY_CH1_CCA),
 			nf = MS(REG_READ(ah, AR_PHY_CH1_CCA),
 					AR9280_PHY_CH1_MINCCA_PWR);
 					AR9280_PHY_CH1_MINCCA_PWR);
@@ -139,9 +143,13 @@ static void ath9k_hw_do_getnf(struct ath_hw *ah,
 		nf = 0 - ((nf ^ 0x1ff) + 1);
 		nf = 0 - ((nf ^ 0x1ff) + 1);
 	ath_print(common, ATH_DBG_CALIBRATE,
 	ath_print(common, ATH_DBG_CALIBRATE,
 		  "NF calibrated [ext] [chain 0] is %d\n", nf);
 		  "NF calibrated [ext] [chain 0] is %d\n", nf);
+
+	if (AR_SREV_9271(ah) && (nf >= -114))
+		nf = -116;
+
 	nfarray[3] = nf;
 	nfarray[3] = nf;
 
 
-	if (!AR_SREV_9285(ah)) {
+	if (!AR_SREV_9285(ah) && !AR_SREV_9271(ah)) {
 		if (AR_SREV_9280_10_OR_LATER(ah))
 		if (AR_SREV_9280_10_OR_LATER(ah))
 			nf = MS(REG_READ(ah, AR_PHY_CH1_EXT_CCA),
 			nf = MS(REG_READ(ah, AR_PHY_CH1_EXT_CCA),
 					AR9280_PHY_CH1_EXT_MINCCA_PWR);
 					AR9280_PHY_CH1_EXT_MINCCA_PWR);
@@ -621,7 +629,7 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan)
 	u8 chainmask, rx_chain_status;
 	u8 chainmask, rx_chain_status;
 
 
 	rx_chain_status = REG_READ(ah, AR_PHY_RX_CHAINMASK);
 	rx_chain_status = REG_READ(ah, AR_PHY_RX_CHAINMASK);
-	if (AR_SREV_9285(ah))
+	if (AR_SREV_9285(ah) || AR_SREV_9271(ah))
 		chainmask = 0x9;
 		chainmask = 0x9;
 	else if (AR_SREV_9280(ah) || AR_SREV_9287(ah)) {
 	else if (AR_SREV_9280(ah) || AR_SREV_9287(ah)) {
 		if ((rx_chain_status & 0x2) || (rx_chain_status & 0x4))
 		if ((rx_chain_status & 0x2) || (rx_chain_status & 0x4))
@@ -715,7 +723,7 @@ void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah)
 
 
 	if (AR_SREV_9280(ah))
 	if (AR_SREV_9280(ah))
 		noise_floor = AR_PHY_CCA_MAX_AR9280_GOOD_VALUE;
 		noise_floor = AR_PHY_CCA_MAX_AR9280_GOOD_VALUE;
-	else if (AR_SREV_9285(ah))
+	else if (AR_SREV_9285(ah) || AR_SREV_9271(ah))
 		noise_floor = AR_PHY_CCA_MAX_AR9285_GOOD_VALUE;
 		noise_floor = AR_PHY_CCA_MAX_AR9285_GOOD_VALUE;
 	else if (AR_SREV_9287(ah))
 	else if (AR_SREV_9287(ah))
 		noise_floor = AR_PHY_CCA_MAX_AR9287_GOOD_VALUE;
 		noise_floor = AR_PHY_CCA_MAX_AR9287_GOOD_VALUE;
@@ -1051,9 +1059,12 @@ bool ath9k_hw_calibrate(struct ath_hw *ah, struct ath9k_channel *chan,
 	/* Do NF cal only at longer intervals */
 	/* Do NF cal only at longer intervals */
 	if (longcal) {
 	if (longcal) {
 		/* Do periodic PAOffset Cal */
 		/* Do periodic PAOffset Cal */
-		if (AR_SREV_9271(ah))
-			ath9k_hw_9271_pa_cal(ah, false);
-		else if (AR_SREV_9285_11_OR_LATER(ah)) {
+		if (AR_SREV_9271(ah)) {
+			if (!ah->pacal_info.skipcount)
+				ath9k_hw_9271_pa_cal(ah, false);
+			else
+				ah->pacal_info.skipcount--;
+		} else if (AR_SREV_9285_11_OR_LATER(ah)) {
 			if (!ah->pacal_info.skipcount)
 			if (!ah->pacal_info.skipcount)
 				ath9k_hw_9285_pa_cal(ah, false);
 				ath9k_hw_9285_pa_cal(ah, false);
 			else
 			else

+ 421 - 0
drivers/net/wireless/ath/ath9k/common.c

@@ -286,6 +286,427 @@ int ath9k_cmn_padpos(__le16 frame_control)
 }
 }
 EXPORT_SYMBOL(ath9k_cmn_padpos);
 EXPORT_SYMBOL(ath9k_cmn_padpos);
 
 
+int ath9k_cmn_get_hw_crypto_keytype(struct sk_buff *skb)
+{
+	struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
+
+	if (tx_info->control.hw_key) {
+		if (tx_info->control.hw_key->alg == ALG_WEP)
+			return ATH9K_KEY_TYPE_WEP;
+		else if (tx_info->control.hw_key->alg == ALG_TKIP)
+			return ATH9K_KEY_TYPE_TKIP;
+		else if (tx_info->control.hw_key->alg == ALG_CCMP)
+			return ATH9K_KEY_TYPE_AES;
+	}
+
+	return ATH9K_KEY_TYPE_CLEAR;
+}
+EXPORT_SYMBOL(ath9k_cmn_get_hw_crypto_keytype);
+
+/*
+ * Calculate the RX filter to be set in the HW.
+ */
+u32 ath9k_cmn_calcrxfilter(struct ieee80211_hw *hw, struct ath_hw *ah,
+			   unsigned int rxfilter)
+{
+#define	RX_FILTER_PRESERVE (ATH9K_RX_FILTER_PHYERR | ATH9K_RX_FILTER_PHYRADAR)
+
+	u32 rfilt;
+
+	rfilt = (ath9k_hw_getrxfilter(ah) & RX_FILTER_PRESERVE)
+		| ATH9K_RX_FILTER_UCAST | ATH9K_RX_FILTER_BCAST
+		| ATH9K_RX_FILTER_MCAST;
+
+	/* If not a STA, enable processing of Probe Requests */
+	if (ah->opmode != NL80211_IFTYPE_STATION)
+		rfilt |= ATH9K_RX_FILTER_PROBEREQ;
+
+	/*
+	 * Set promiscuous mode when FIF_PROMISC_IN_BSS is enabled for station
+	 * mode interface or when in monitor mode. AP mode does not need this
+	 * since it receives all in-BSS frames anyway.
+	 */
+	if (((ah->opmode != NL80211_IFTYPE_AP) &&
+	     (rxfilter & FIF_PROMISC_IN_BSS)) ||
+	    (ah->opmode == NL80211_IFTYPE_MONITOR))
+		rfilt |= ATH9K_RX_FILTER_PROM;
+
+	if (rxfilter & FIF_CONTROL)
+		rfilt |= ATH9K_RX_FILTER_CONTROL;
+
+	if ((ah->opmode == NL80211_IFTYPE_STATION) &&
+	    !(rxfilter & FIF_BCN_PRBRESP_PROMISC))
+		rfilt |= ATH9K_RX_FILTER_MYBEACON;
+	else
+		rfilt |= ATH9K_RX_FILTER_BEACON;
+
+	if ((AR_SREV_9280_10_OR_LATER(ah) ||
+	    AR_SREV_9285_10_OR_LATER(ah)) &&
+	    (ah->opmode == NL80211_IFTYPE_AP) &&
+	    (rxfilter & FIF_PSPOLL))
+		rfilt |= ATH9K_RX_FILTER_PSPOLL;
+
+	if (conf_is_ht(&hw->conf))
+		rfilt |= ATH9K_RX_FILTER_COMP_BAR;
+
+	return rfilt;
+
+#undef RX_FILTER_PRESERVE
+}
+EXPORT_SYMBOL(ath9k_cmn_calcrxfilter);
+
+/*
+ * Recv initialization for opmode change.
+ */
+void ath9k_cmn_opmode_init(struct ieee80211_hw *hw, struct ath_hw *ah,
+			   unsigned int rxfilter)
+{
+	struct ath_common *common = ath9k_hw_common(ah);
+
+	u32 rfilt, mfilt[2];
+
+	/* configure rx filter */
+	rfilt = ath9k_cmn_calcrxfilter(hw, ah, rxfilter);
+	ath9k_hw_setrxfilter(ah, rfilt);
+
+	/* configure bssid mask */
+	if (ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK)
+		ath_hw_setbssidmask(common);
+
+	/* configure operational mode */
+	ath9k_hw_setopmode(ah);
+
+	/* Handle any link-level address change. */
+	ath9k_hw_setmac(ah, common->macaddr);
+
+	/* calculate and install multicast filter */
+	mfilt[0] = mfilt[1] = ~0;
+	ath9k_hw_setmcastfilter(ah, mfilt[0], mfilt[1]);
+}
+EXPORT_SYMBOL(ath9k_cmn_opmode_init);
+
+static u32 ath9k_get_extchanmode(struct ieee80211_channel *chan,
+				 enum nl80211_channel_type channel_type)
+{
+	u32 chanmode = 0;
+
+	switch (chan->band) {
+	case IEEE80211_BAND_2GHZ:
+		switch (channel_type) {
+		case NL80211_CHAN_NO_HT:
+		case NL80211_CHAN_HT20:
+			chanmode = CHANNEL_G_HT20;
+			break;
+		case NL80211_CHAN_HT40PLUS:
+			chanmode = CHANNEL_G_HT40PLUS;
+			break;
+		case NL80211_CHAN_HT40MINUS:
+			chanmode = CHANNEL_G_HT40MINUS;
+			break;
+		}
+		break;
+	case IEEE80211_BAND_5GHZ:
+		switch (channel_type) {
+		case NL80211_CHAN_NO_HT:
+		case NL80211_CHAN_HT20:
+			chanmode = CHANNEL_A_HT20;
+			break;
+		case NL80211_CHAN_HT40PLUS:
+			chanmode = CHANNEL_A_HT40PLUS;
+			break;
+		case NL80211_CHAN_HT40MINUS:
+			chanmode = CHANNEL_A_HT40MINUS;
+			break;
+		}
+		break;
+	default:
+		break;
+	}
+
+	return chanmode;
+}
+
+/*
+ * Update internal channel flags.
+ */
+void ath9k_cmn_update_ichannel(struct ieee80211_hw *hw,
+			       struct ath9k_channel *ichan)
+{
+	struct ieee80211_channel *chan = hw->conf.channel;
+	struct ieee80211_conf *conf = &hw->conf;
+
+	ichan->channel = chan->center_freq;
+	ichan->chan = chan;
+
+	if (chan->band == IEEE80211_BAND_2GHZ) {
+		ichan->chanmode = CHANNEL_G;
+		ichan->channelFlags = CHANNEL_2GHZ | CHANNEL_OFDM | CHANNEL_G;
+	} else {
+		ichan->chanmode = CHANNEL_A;
+		ichan->channelFlags = CHANNEL_5GHZ | CHANNEL_OFDM;
+	}
+
+	if (conf_is_ht(conf))
+		ichan->chanmode = ath9k_get_extchanmode(chan,
+							conf->channel_type);
+}
+EXPORT_SYMBOL(ath9k_cmn_update_ichannel);
+
+/*
+ * Get the internal channel reference.
+ */
+struct ath9k_channel *ath9k_cmn_get_curchannel(struct ieee80211_hw *hw,
+					       struct ath_hw *ah)
+{
+	struct ieee80211_channel *curchan = hw->conf.channel;
+	struct ath9k_channel *channel;
+	u8 chan_idx;
+
+	chan_idx = curchan->hw_value;
+	channel = &ah->channels[chan_idx];
+	ath9k_cmn_update_ichannel(hw, channel);
+
+	return channel;
+}
+EXPORT_SYMBOL(ath9k_cmn_get_curchannel);
+
+static int ath_setkey_tkip(struct ath_common *common, u16 keyix, const u8 *key,
+			   struct ath9k_keyval *hk, const u8 *addr,
+			   bool authenticator)
+{
+	struct ath_hw *ah = common->ah;
+	const u8 *key_rxmic;
+	const u8 *key_txmic;
+
+	key_txmic = key + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY;
+	key_rxmic = key + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY;
+
+	if (addr == NULL) {
+		/*
+		 * Group key installation - only two key cache entries are used
+		 * regardless of splitmic capability since group key is only
+		 * used either for TX or RX.
+		 */
+		if (authenticator) {
+			memcpy(hk->kv_mic, key_txmic, sizeof(hk->kv_mic));
+			memcpy(hk->kv_txmic, key_txmic, sizeof(hk->kv_mic));
+		} else {
+			memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic));
+			memcpy(hk->kv_txmic, key_rxmic, sizeof(hk->kv_mic));
+		}
+		return ath9k_hw_set_keycache_entry(ah, keyix, hk, addr);
+	}
+	if (!common->splitmic) {
+		/* TX and RX keys share the same key cache entry. */
+		memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic));
+		memcpy(hk->kv_txmic, key_txmic, sizeof(hk->kv_txmic));
+		return ath9k_hw_set_keycache_entry(ah, keyix, hk, addr);
+	}
+
+	/* Separate key cache entries for TX and RX */
+
+	/* TX key goes at first index, RX key at +32. */
+	memcpy(hk->kv_mic, key_txmic, sizeof(hk->kv_mic));
+	if (!ath9k_hw_set_keycache_entry(ah, keyix, hk, NULL)) {
+		/* TX MIC entry failed. No need to proceed further */
+		ath_print(common, ATH_DBG_FATAL,
+			  "Setting TX MIC Key Failed\n");
+		return 0;
+	}
+
+	memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic));
+	/* XXX delete tx key on failure? */
+	return ath9k_hw_set_keycache_entry(ah, keyix + 32, hk, addr);
+}
+
+static int ath_reserve_key_cache_slot_tkip(struct ath_common *common)
+{
+	int i;
+
+	for (i = IEEE80211_WEP_NKID; i < common->keymax / 2; i++) {
+		if (test_bit(i, common->keymap) ||
+		    test_bit(i + 64, common->keymap))
+			continue; /* At least one part of TKIP key allocated */
+		if (common->splitmic &&
+		    (test_bit(i + 32, common->keymap) ||
+		     test_bit(i + 64 + 32, common->keymap)))
+			continue; /* At least one part of TKIP key allocated */
+
+		/* Found a free slot for a TKIP key */
+		return i;
+	}
+	return -1;
+}
+
+static int ath_reserve_key_cache_slot(struct ath_common *common)
+{
+	int i;
+
+	/* First, try to find slots that would not be available for TKIP. */
+	if (common->splitmic) {
+		for (i = IEEE80211_WEP_NKID; i < common->keymax / 4; i++) {
+			if (!test_bit(i, common->keymap) &&
+			    (test_bit(i + 32, common->keymap) ||
+			     test_bit(i + 64, common->keymap) ||
+			     test_bit(i + 64 + 32, common->keymap)))
+				return i;
+			if (!test_bit(i + 32, common->keymap) &&
+			    (test_bit(i, common->keymap) ||
+			     test_bit(i + 64, common->keymap) ||
+			     test_bit(i + 64 + 32, common->keymap)))
+				return i + 32;
+			if (!test_bit(i + 64, common->keymap) &&
+			    (test_bit(i , common->keymap) ||
+			     test_bit(i + 32, common->keymap) ||
+			     test_bit(i + 64 + 32, common->keymap)))
+				return i + 64;
+			if (!test_bit(i + 64 + 32, common->keymap) &&
+			    (test_bit(i, common->keymap) ||
+			     test_bit(i + 32, common->keymap) ||
+			     test_bit(i + 64, common->keymap)))
+				return i + 64 + 32;
+		}
+	} else {
+		for (i = IEEE80211_WEP_NKID; i < common->keymax / 2; i++) {
+			if (!test_bit(i, common->keymap) &&
+			    test_bit(i + 64, common->keymap))
+				return i;
+			if (test_bit(i, common->keymap) &&
+			    !test_bit(i + 64, common->keymap))
+				return i + 64;
+		}
+	}
+
+	/* No partially used TKIP slots, pick any available slot */
+	for (i = IEEE80211_WEP_NKID; i < common->keymax; i++) {
+		/* Do not allow slots that could be needed for TKIP group keys
+		 * to be used. This limitation could be removed if we know that
+		 * TKIP will not be used. */
+		if (i >= 64 && i < 64 + IEEE80211_WEP_NKID)
+			continue;
+		if (common->splitmic) {
+			if (i >= 32 && i < 32 + IEEE80211_WEP_NKID)
+				continue;
+			if (i >= 64 + 32 && i < 64 + 32 + IEEE80211_WEP_NKID)
+				continue;
+		}
+
+		if (!test_bit(i, common->keymap))
+			return i; /* Found a free slot for a key */
+	}
+
+	/* No free slot found */
+	return -1;
+}
+
+/*
+ * Configure encryption in the HW.
+ */
+int ath9k_cmn_key_config(struct ath_common *common,
+			 struct ieee80211_vif *vif,
+			 struct ieee80211_sta *sta,
+			 struct ieee80211_key_conf *key)
+{
+	struct ath_hw *ah = common->ah;
+	struct ath9k_keyval hk;
+	const u8 *mac = NULL;
+	int ret = 0;
+	int idx;
+
+	memset(&hk, 0, sizeof(hk));
+
+	switch (key->alg) {
+	case ALG_WEP:
+		hk.kv_type = ATH9K_CIPHER_WEP;
+		break;
+	case ALG_TKIP:
+		hk.kv_type = ATH9K_CIPHER_TKIP;
+		break;
+	case ALG_CCMP:
+		hk.kv_type = ATH9K_CIPHER_AES_CCM;
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	hk.kv_len = key->keylen;
+	memcpy(hk.kv_val, key->key, key->keylen);
+
+	if (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) {
+		/* For now, use the default keys for broadcast keys. This may
+		 * need to change with virtual interfaces. */
+		idx = key->keyidx;
+	} else if (key->keyidx) {
+		if (WARN_ON(!sta))
+			return -EOPNOTSUPP;
+		mac = sta->addr;
+
+		if (vif->type != NL80211_IFTYPE_AP) {
+			/* Only keyidx 0 should be used with unicast key, but
+			 * allow this for client mode for now. */
+			idx = key->keyidx;
+		} else
+			return -EIO;
+	} else {
+		if (WARN_ON(!sta))
+			return -EOPNOTSUPP;
+		mac = sta->addr;
+
+		if (key->alg == ALG_TKIP)
+			idx = ath_reserve_key_cache_slot_tkip(common);
+		else
+			idx = ath_reserve_key_cache_slot(common);
+		if (idx < 0)
+			return -ENOSPC; /* no free key cache entries */
+	}
+
+	if (key->alg == ALG_TKIP)
+		ret = ath_setkey_tkip(common, idx, key->key, &hk, mac,
+				      vif->type == NL80211_IFTYPE_AP);
+	else
+		ret = ath9k_hw_set_keycache_entry(ah, idx, &hk, mac);
+
+	if (!ret)
+		return -EIO;
+
+	set_bit(idx, common->keymap);
+	if (key->alg == ALG_TKIP) {
+		set_bit(idx + 64, common->keymap);
+		if (common->splitmic) {
+			set_bit(idx + 32, common->keymap);
+			set_bit(idx + 64 + 32, common->keymap);
+		}
+	}
+
+	return idx;
+}
+EXPORT_SYMBOL(ath9k_cmn_key_config);
+
+/*
+ * Delete Key.
+ */
+void ath9k_cmn_key_delete(struct ath_common *common,
+			  struct ieee80211_key_conf *key)
+{
+	struct ath_hw *ah = common->ah;
+
+	ath9k_hw_keyreset(ah, key->hw_key_idx);
+	if (key->hw_key_idx < IEEE80211_WEP_NKID)
+		return;
+
+	clear_bit(key->hw_key_idx, common->keymap);
+	if (key->alg != ALG_TKIP)
+		return;
+
+	clear_bit(key->hw_key_idx + 64, common->keymap);
+	if (common->splitmic) {
+		ath9k_hw_keyreset(ah, key->hw_key_idx + 32);
+		clear_bit(key->hw_key_idx + 32, common->keymap);
+		clear_bit(key->hw_key_idx + 64 + 32, common->keymap);
+	}
+}
+EXPORT_SYMBOL(ath9k_cmn_key_delete);
+
 static int __init ath9k_cmn_init(void)
 static int __init ath9k_cmn_init(void)
 {
 {
 	return 0;
 	return 0;

+ 17 - 0
drivers/net/wireless/ath/ath9k/common.h

@@ -23,6 +23,8 @@
 
 
 /* Common header for Atheros 802.11n base driver cores */
 /* Common header for Atheros 802.11n base driver cores */
 
 
+#define IEEE80211_WEP_NKID 4
+
 #define WME_NUM_TID             16
 #define WME_NUM_TID             16
 #define WME_BA_BMP_SIZE         64
 #define WME_BA_BMP_SIZE         64
 #define WME_MAX_BA              WME_BA_BMP_SIZE
 #define WME_MAX_BA              WME_BA_BMP_SIZE
@@ -125,3 +127,18 @@ void ath9k_cmn_rx_skb_postprocess(struct ath_common *common,
 				  bool decrypt_error);
 				  bool decrypt_error);
 
 
 int ath9k_cmn_padpos(__le16 frame_control);
 int ath9k_cmn_padpos(__le16 frame_control);
+int ath9k_cmn_get_hw_crypto_keytype(struct sk_buff *skb);
+u32 ath9k_cmn_calcrxfilter(struct ieee80211_hw *hw, struct ath_hw *ah,
+			   unsigned int rxfilter);
+void ath9k_cmn_opmode_init(struct ieee80211_hw *hw, struct ath_hw *ah,
+			   unsigned int rxfilter);
+void ath9k_cmn_update_ichannel(struct ieee80211_hw *hw,
+			       struct ath9k_channel *ichan);
+struct ath9k_channel *ath9k_cmn_get_curchannel(struct ieee80211_hw *hw,
+					       struct ath_hw *ah);
+int ath9k_cmn_key_config(struct ath_common *common,
+			 struct ieee80211_vif *vif,
+			 struct ieee80211_sta *sta,
+			 struct ieee80211_key_conf *key);
+void ath9k_cmn_key_delete(struct ath_common *common,
+			  struct ieee80211_key_conf *key);

+ 993 - 0
drivers/net/wireless/ath/ath9k/hif_usb.c

@@ -0,0 +1,993 @@
+/*
+ * Copyright (c) 2010 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "htc.h"
+
+#define ATH9K_FW_USB_DEV(devid, fw)					\
+	{ USB_DEVICE(0x0cf3, devid), .driver_info = (unsigned long) fw }
+
+static struct usb_device_id ath9k_hif_usb_ids[] = {
+	ATH9K_FW_USB_DEV(0x9271, "ar9271.fw"),
+	{ },
+};
+
+MODULE_DEVICE_TABLE(usb, ath9k_hif_usb_ids);
+
+static int __hif_usb_tx(struct hif_device_usb *hif_dev);
+
+static void hif_usb_regout_cb(struct urb *urb)
+{
+	struct cmd_buf *cmd = (struct cmd_buf *)urb->context;
+	struct hif_device_usb *hif_dev = cmd->hif_dev;
+
+	if (!hif_dev) {
+		usb_free_urb(urb);
+		if (cmd) {
+			if (cmd->skb)
+				dev_kfree_skb_any(cmd->skb);
+			kfree(cmd);
+		}
+		return;
+	}
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ENOENT:
+	case -ECONNRESET:
+		break;
+	case -ENODEV:
+	case -ESHUTDOWN:
+		return;
+	default:
+		break;
+	}
+
+	if (cmd) {
+		ath9k_htc_txcompletion_cb(cmd->hif_dev->htc_handle,
+					  cmd->skb, 1);
+		kfree(cmd);
+		usb_free_urb(urb);
+	}
+}
+
+static int hif_usb_send_regout(struct hif_device_usb *hif_dev,
+			       struct sk_buff *skb)
+{
+	struct urb *urb;
+	struct cmd_buf *cmd;
+	int ret = 0;
+
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (urb == NULL)
+		return -ENOMEM;
+
+	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+	if (cmd == NULL) {
+		usb_free_urb(urb);
+		return -ENOMEM;
+	}
+
+	cmd->skb = skb;
+	cmd->hif_dev = hif_dev;
+
+	usb_fill_int_urb(urb, hif_dev->udev,
+			 usb_sndintpipe(hif_dev->udev, USB_REG_OUT_PIPE),
+			 skb->data, skb->len,
+			 hif_usb_regout_cb, cmd, 1);
+
+	ret = usb_submit_urb(urb, GFP_KERNEL);
+	if (ret) {
+		usb_free_urb(urb);
+		kfree(cmd);
+	}
+
+	return ret;
+}
+
+static void hif_usb_tx_cb(struct urb *urb)
+{
+	struct tx_buf *tx_buf = (struct tx_buf *) urb->context;
+	struct hif_device_usb *hif_dev = tx_buf->hif_dev;
+	struct sk_buff *skb;
+	bool drop, flush;
+
+	if (!hif_dev)
+		return;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ENOENT:
+	case -ECONNRESET:
+		break;
+	case -ENODEV:
+	case -ESHUTDOWN:
+		return;
+	default:
+		break;
+	}
+
+	if (tx_buf) {
+		spin_lock(&hif_dev->tx.tx_lock);
+		drop = !!(hif_dev->tx.flags & HIF_USB_TX_STOP);
+		flush = !!(hif_dev->tx.flags & HIF_USB_TX_FLUSH);
+		spin_unlock(&hif_dev->tx.tx_lock);
+
+		while ((skb = __skb_dequeue(&tx_buf->skb_queue)) != NULL) {
+			if (!drop && !flush) {
+				ath9k_htc_txcompletion_cb(hif_dev->htc_handle,
+							  skb, 1);
+				TX_STAT_INC(skb_completed);
+			} else {
+				dev_kfree_skb_any(skb);
+			}
+		}
+
+		if (flush)
+			return;
+
+		tx_buf->len = tx_buf->offset = 0;
+		__skb_queue_head_init(&tx_buf->skb_queue);
+
+		spin_lock(&hif_dev->tx.tx_lock);
+		list_del(&tx_buf->list);
+		list_add_tail(&tx_buf->list, &hif_dev->tx.tx_buf);
+		hif_dev->tx.tx_buf_cnt++;
+		if (!drop)
+			__hif_usb_tx(hif_dev); /* Check for pending SKBs */
+		TX_STAT_INC(buf_completed);
+		spin_unlock(&hif_dev->tx.tx_lock);
+	}
+}
+
+/* TX lock has to be taken */
+static int __hif_usb_tx(struct hif_device_usb *hif_dev)
+{
+	struct tx_buf *tx_buf = NULL;
+	struct sk_buff *nskb = NULL;
+	int ret = 0, i;
+	u16 *hdr, tx_skb_cnt = 0;
+	u8 *buf;
+
+	if (hif_dev->tx.tx_skb_cnt == 0)
+		return 0;
+
+	/* Check if a free TX buffer is available */
+	if (list_empty(&hif_dev->tx.tx_buf))
+		return 0;
+
+	tx_buf = list_first_entry(&hif_dev->tx.tx_buf, struct tx_buf, list);
+	list_del(&tx_buf->list);
+	list_add_tail(&tx_buf->list, &hif_dev->tx.tx_pending);
+	hif_dev->tx.tx_buf_cnt--;
+
+	tx_skb_cnt = min_t(u16, hif_dev->tx.tx_skb_cnt, MAX_TX_AGGR_NUM);
+
+	for (i = 0; i < tx_skb_cnt; i++) {
+		nskb = __skb_dequeue(&hif_dev->tx.tx_skb_queue);
+
+		/* Should never be NULL */
+		BUG_ON(!nskb);
+
+		hif_dev->tx.tx_skb_cnt--;
+
+		buf = tx_buf->buf;
+		buf += tx_buf->offset;
+		hdr = (u16 *)buf;
+		*hdr++ = nskb->len;
+		*hdr++ = ATH_USB_TX_STREAM_MODE_TAG;
+		buf += 4;
+		memcpy(buf, nskb->data, nskb->len);
+		tx_buf->len = nskb->len + 4;
+
+		if (i < (tx_skb_cnt - 1))
+			tx_buf->offset += (((tx_buf->len - 1) / 4) + 1) * 4;
+
+		if (i == (tx_skb_cnt - 1))
+			tx_buf->len += tx_buf->offset;
+
+		__skb_queue_tail(&tx_buf->skb_queue, nskb);
+		TX_STAT_INC(skb_queued);
+	}
+
+	usb_fill_bulk_urb(tx_buf->urb, hif_dev->udev,
+			  usb_sndbulkpipe(hif_dev->udev, USB_WLAN_TX_PIPE),
+			  tx_buf->buf, tx_buf->len,
+			  hif_usb_tx_cb, tx_buf);
+
+	ret = usb_submit_urb(tx_buf->urb, GFP_ATOMIC);
+	if (ret) {
+		tx_buf->len = tx_buf->offset = 0;
+		__skb_queue_purge(&tx_buf->skb_queue);
+		__skb_queue_head_init(&tx_buf->skb_queue);
+		list_move_tail(&tx_buf->list, &hif_dev->tx.tx_buf);
+		hif_dev->tx.tx_buf_cnt++;
+	}
+
+	if (!ret)
+		TX_STAT_INC(buf_queued);
+
+	return ret;
+}
+
+static int hif_usb_send_tx(struct hif_device_usb *hif_dev, struct sk_buff *skb,
+			   struct ath9k_htc_tx_ctl *tx_ctl)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&hif_dev->tx.tx_lock, flags);
+
+	if (hif_dev->tx.flags & HIF_USB_TX_STOP) {
+		spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags);
+		return -ENODEV;
+	}
+
+	/* Check if the max queue count has been reached */
+	if (hif_dev->tx.tx_skb_cnt > MAX_TX_BUF_NUM) {
+		spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags);
+		return -ENOMEM;
+	}
+
+	__skb_queue_tail(&hif_dev->tx.tx_skb_queue, skb);
+	hif_dev->tx.tx_skb_cnt++;
+
+	/* Send normal frames immediately */
+	if (!tx_ctl || (tx_ctl && (tx_ctl->type == ATH9K_HTC_NORMAL)))
+		__hif_usb_tx(hif_dev);
+
+	/* Check if AMPDUs have to be sent immediately */
+	if (tx_ctl && (tx_ctl->type == ATH9K_HTC_AMPDU) &&
+	    (hif_dev->tx.tx_buf_cnt == MAX_TX_URB_NUM) &&
+	    (hif_dev->tx.tx_skb_cnt < 2)) {
+		__hif_usb_tx(hif_dev);
+	}
+
+	spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags);
+
+	return 0;
+}
+
+static void hif_usb_start(void *hif_handle, u8 pipe_id)
+{
+	struct hif_device_usb *hif_dev = (struct hif_device_usb *)hif_handle;
+	unsigned long flags;
+
+	hif_dev->flags |= HIF_USB_START;
+
+	spin_lock_irqsave(&hif_dev->tx.tx_lock, flags);
+	hif_dev->tx.flags &= ~HIF_USB_TX_STOP;
+	spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags);
+}
+
+static void hif_usb_stop(void *hif_handle, u8 pipe_id)
+{
+	struct hif_device_usb *hif_dev = (struct hif_device_usb *)hif_handle;
+	unsigned long flags;
+
+	spin_lock_irqsave(&hif_dev->tx.tx_lock, flags);
+	__skb_queue_purge(&hif_dev->tx.tx_skb_queue);
+	hif_dev->tx.tx_skb_cnt = 0;
+	hif_dev->tx.flags |= HIF_USB_TX_STOP;
+	spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags);
+}
+
+static int hif_usb_send(void *hif_handle, u8 pipe_id, struct sk_buff *skb,
+			struct ath9k_htc_tx_ctl *tx_ctl)
+{
+	struct hif_device_usb *hif_dev = (struct hif_device_usb *)hif_handle;
+	int ret = 0;
+
+	switch (pipe_id) {
+	case USB_WLAN_TX_PIPE:
+		ret = hif_usb_send_tx(hif_dev, skb, tx_ctl);
+		break;
+	case USB_REG_OUT_PIPE:
+		ret = hif_usb_send_regout(hif_dev, skb);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static struct ath9k_htc_hif hif_usb = {
+	.transport = ATH9K_HIF_USB,
+	.name = "ath9k_hif_usb",
+
+	.control_ul_pipe = USB_REG_OUT_PIPE,
+	.control_dl_pipe = USB_REG_IN_PIPE,
+
+	.start = hif_usb_start,
+	.stop = hif_usb_stop,
+	.send = hif_usb_send,
+};
+
+static void ath9k_hif_usb_rx_stream(struct hif_device_usb *hif_dev,
+				    struct sk_buff *skb)
+{
+	struct sk_buff *nskb, *skb_pool[8];
+	int index = 0, i = 0, chk_idx, len = skb->len;
+	int rx_remain_len = 0, rx_pkt_len = 0;
+	u16 pkt_len, pkt_tag, pool_index = 0;
+	u8 *ptr;
+
+	rx_remain_len = hif_dev->rx_remain_len;
+	rx_pkt_len = hif_dev->rx_transfer_len;
+
+	if (rx_remain_len != 0) {
+		struct sk_buff *remain_skb = hif_dev->remain_skb;
+
+		if (remain_skb) {
+			ptr = (u8 *) remain_skb->data;
+
+			index = rx_remain_len;
+			rx_remain_len -= hif_dev->rx_pad_len;
+			ptr += rx_pkt_len;
+
+			memcpy(ptr, skb->data, rx_remain_len);
+
+			rx_pkt_len += rx_remain_len;
+			hif_dev->rx_remain_len = 0;
+			skb_put(remain_skb, rx_pkt_len);
+
+			skb_pool[pool_index++] = remain_skb;
+
+		} else {
+			index = rx_remain_len;
+		}
+	}
+
+	while (index < len) {
+		ptr = (u8 *) skb->data;
+
+		pkt_len = ptr[index] + (ptr[index+1] << 8);
+		pkt_tag = ptr[index+2] + (ptr[index+3] << 8);
+
+		if (pkt_tag == ATH_USB_RX_STREAM_MODE_TAG) {
+			u16 pad_len;
+
+			pad_len = 4 - (pkt_len & 0x3);
+			if (pad_len == 4)
+				pad_len = 0;
+
+			chk_idx = index;
+			index = index + 4 + pkt_len + pad_len;
+
+			if (index > MAX_RX_BUF_SIZE) {
+				hif_dev->rx_remain_len = index - MAX_RX_BUF_SIZE;
+				hif_dev->rx_transfer_len =
+					MAX_RX_BUF_SIZE - chk_idx - 4;
+				hif_dev->rx_pad_len = pad_len;
+
+				nskb = __dev_alloc_skb(pkt_len + 32,
+						       GFP_ATOMIC);
+				if (!nskb) {
+					dev_err(&hif_dev->udev->dev,
+					"ath9k_htc: RX memory allocation"
+					" error\n");
+					goto err;
+				}
+				skb_reserve(nskb, 32);
+				RX_STAT_INC(skb_allocated);
+
+				memcpy(nskb->data, &(skb->data[chk_idx+4]),
+				       hif_dev->rx_transfer_len);
+
+				/* Record the buffer pointer */
+				hif_dev->remain_skb = nskb;
+			} else {
+				nskb = __dev_alloc_skb(pkt_len + 32, GFP_ATOMIC);
+				if (!nskb) {
+					dev_err(&hif_dev->udev->dev,
+					"ath9k_htc: RX memory allocation"
+					" error\n");
+					goto err;
+				}
+				skb_reserve(nskb, 32);
+				RX_STAT_INC(skb_allocated);
+
+				memcpy(nskb->data, &(skb->data[chk_idx+4]), pkt_len);
+				skb_put(nskb, pkt_len);
+				skb_pool[pool_index++] = nskb;
+			}
+		} else {
+			RX_STAT_INC(skb_dropped);
+			dev_kfree_skb_any(skb);
+			return;
+		}
+	}
+
+err:
+	dev_kfree_skb_any(skb);
+
+	for (i = 0; i < pool_index; i++) {
+		ath9k_htc_rx_msg(hif_dev->htc_handle, skb_pool[i],
+				 skb_pool[i]->len, USB_WLAN_RX_PIPE);
+		RX_STAT_INC(skb_completed);
+	}
+}
+
+static void ath9k_hif_usb_rx_cb(struct urb *urb)
+{
+	struct sk_buff *skb = (struct sk_buff *) urb->context;
+	struct sk_buff *nskb;
+	struct hif_device_usb *hif_dev = (struct hif_device_usb *)
+		usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0));
+	int ret;
+
+	if (!hif_dev)
+		goto free;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ENOENT:
+	case -ECONNRESET:
+	case -ENODEV:
+	case -ESHUTDOWN:
+		goto free;
+	default:
+		goto resubmit;
+	}
+
+	if (likely(urb->actual_length != 0)) {
+		skb_put(skb, urb->actual_length);
+
+		nskb = __dev_alloc_skb(MAX_RX_BUF_SIZE, GFP_ATOMIC);
+		if (!nskb)
+			goto resubmit;
+
+		usb_fill_bulk_urb(urb, hif_dev->udev,
+				  usb_rcvbulkpipe(hif_dev->udev,
+						  USB_WLAN_RX_PIPE),
+				  nskb->data, MAX_RX_BUF_SIZE,
+				  ath9k_hif_usb_rx_cb, nskb);
+
+		ret = usb_submit_urb(urb, GFP_ATOMIC);
+		if (ret) {
+			dev_kfree_skb_any(nskb);
+			goto free;
+		}
+
+		ath9k_hif_usb_rx_stream(hif_dev, skb);
+		return;
+	}
+
+resubmit:
+	skb_reset_tail_pointer(skb);
+	skb_trim(skb, 0);
+
+	ret = usb_submit_urb(urb, GFP_ATOMIC);
+	if (ret)
+		goto free;
+
+	return;
+free:
+	dev_kfree_skb_any(skb);
+}
+
+static void ath9k_hif_usb_reg_in_cb(struct urb *urb)
+{
+	struct sk_buff *skb = (struct sk_buff *) urb->context;
+	struct sk_buff *nskb;
+	struct hif_device_usb *hif_dev = (struct hif_device_usb *)
+		usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0));
+	int ret;
+
+	if (!hif_dev)
+		goto free;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ENOENT:
+	case -ECONNRESET:
+	case -ENODEV:
+	case -ESHUTDOWN:
+		goto free;
+	default:
+		goto resubmit;
+	}
+
+	if (likely(urb->actual_length != 0)) {
+		skb_put(skb, urb->actual_length);
+
+		nskb = __dev_alloc_skb(MAX_REG_IN_BUF_SIZE, GFP_ATOMIC);
+		if (!nskb)
+			goto resubmit;
+
+		usb_fill_int_urb(urb, hif_dev->udev,
+				 usb_rcvintpipe(hif_dev->udev, USB_REG_IN_PIPE),
+				 nskb->data, MAX_REG_IN_BUF_SIZE,
+				 ath9k_hif_usb_reg_in_cb, nskb, 1);
+
+		ret = usb_submit_urb(urb, GFP_ATOMIC);
+		if (ret) {
+			dev_kfree_skb_any(nskb);
+			goto free;
+		}
+
+		ath9k_htc_rx_msg(hif_dev->htc_handle, skb,
+				 skb->len, USB_REG_IN_PIPE);
+
+		return;
+	}
+
+resubmit:
+	skb_reset_tail_pointer(skb);
+	skb_trim(skb, 0);
+
+	ret = usb_submit_urb(urb, GFP_ATOMIC);
+	if (ret)
+		goto free;
+
+	return;
+free:
+	dev_kfree_skb_any(skb);
+}
+
+static void ath9k_hif_usb_dealloc_tx_urbs(struct hif_device_usb *hif_dev)
+{
+	unsigned long flags;
+	struct tx_buf *tx_buf = NULL, *tx_buf_tmp = NULL;
+
+	list_for_each_entry_safe(tx_buf, tx_buf_tmp, &hif_dev->tx.tx_buf, list) {
+		list_del(&tx_buf->list);
+		usb_free_urb(tx_buf->urb);
+		kfree(tx_buf->buf);
+		kfree(tx_buf);
+	}
+
+	spin_lock_irqsave(&hif_dev->tx.tx_lock, flags);
+	hif_dev->tx.flags |= HIF_USB_TX_FLUSH;
+	spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags);
+
+	list_for_each_entry_safe(tx_buf, tx_buf_tmp,
+				 &hif_dev->tx.tx_pending, list) {
+		usb_kill_urb(tx_buf->urb);
+		list_del(&tx_buf->list);
+		usb_free_urb(tx_buf->urb);
+		kfree(tx_buf->buf);
+		kfree(tx_buf);
+	}
+
+	spin_lock_irqsave(&hif_dev->tx.tx_lock, flags);
+	hif_dev->tx.flags &= ~HIF_USB_TX_FLUSH;
+	spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags);
+}
+
+static int ath9k_hif_usb_alloc_tx_urbs(struct hif_device_usb *hif_dev)
+{
+	struct tx_buf *tx_buf;
+	int i;
+
+	INIT_LIST_HEAD(&hif_dev->tx.tx_buf);
+	INIT_LIST_HEAD(&hif_dev->tx.tx_pending);
+	spin_lock_init(&hif_dev->tx.tx_lock);
+	__skb_queue_head_init(&hif_dev->tx.tx_skb_queue);
+
+	for (i = 0; i < MAX_TX_URB_NUM; i++) {
+		tx_buf = kzalloc(sizeof(struct tx_buf), GFP_KERNEL);
+		if (!tx_buf)
+			goto err;
+
+		tx_buf->buf = kzalloc(MAX_TX_BUF_SIZE, GFP_KERNEL);
+		if (!tx_buf->buf)
+			goto err;
+
+		tx_buf->urb = usb_alloc_urb(0, GFP_KERNEL);
+		if (!tx_buf->urb)
+			goto err;
+
+		tx_buf->hif_dev = hif_dev;
+		__skb_queue_head_init(&tx_buf->skb_queue);
+
+		list_add_tail(&tx_buf->list, &hif_dev->tx.tx_buf);
+	}
+
+	hif_dev->tx.tx_buf_cnt = MAX_TX_URB_NUM;
+
+	return 0;
+err:
+	ath9k_hif_usb_dealloc_tx_urbs(hif_dev);
+	return -ENOMEM;
+}
+
+static void ath9k_hif_usb_dealloc_rx_skbs(struct hif_device_usb *hif_dev)
+{
+	int i;
+
+	for (i = 0; i < MAX_RX_URB_NUM; i++) {
+		if (hif_dev->wlan_rx_data_urb[i]) {
+			if (hif_dev->wlan_rx_data_urb[i]->transfer_buffer)
+				dev_kfree_skb_any((void *)
+					  hif_dev->wlan_rx_data_urb[i]->context);
+		}
+	}
+}
+
+static void ath9k_hif_usb_dealloc_rx_urbs(struct hif_device_usb *hif_dev)
+{
+	int i;
+
+	for (i = 0; i < MAX_RX_URB_NUM; i++) {
+		if (hif_dev->wlan_rx_data_urb[i]) {
+			usb_kill_urb(hif_dev->wlan_rx_data_urb[i]);
+			usb_free_urb(hif_dev->wlan_rx_data_urb[i]);
+			hif_dev->wlan_rx_data_urb[i] = NULL;
+		}
+	}
+}
+
+static int ath9k_hif_usb_prep_rx_urb(struct hif_device_usb *hif_dev,
+				     struct urb *urb)
+{
+	struct sk_buff *skb;
+
+	skb = __dev_alloc_skb(MAX_RX_BUF_SIZE, GFP_KERNEL);
+	if (!skb)
+		return -ENOMEM;
+
+	usb_fill_bulk_urb(urb, hif_dev->udev,
+			  usb_rcvbulkpipe(hif_dev->udev, USB_WLAN_RX_PIPE),
+			  skb->data, MAX_RX_BUF_SIZE,
+			  ath9k_hif_usb_rx_cb, skb);
+	return 0;
+}
+
+static int ath9k_hif_usb_alloc_rx_urbs(struct hif_device_usb *hif_dev)
+{
+	int i, ret;
+
+	for (i = 0; i < MAX_RX_URB_NUM; i++) {
+
+		/* Allocate URB */
+		hif_dev->wlan_rx_data_urb[i] = usb_alloc_urb(0, GFP_KERNEL);
+		if (hif_dev->wlan_rx_data_urb[i] == NULL) {
+			ret = -ENOMEM;
+			goto err_rx_urb;
+		}
+
+		/* Allocate buffer */
+		ret = ath9k_hif_usb_prep_rx_urb(hif_dev,
+						hif_dev->wlan_rx_data_urb[i]);
+		if (ret)
+			goto err_rx_urb;
+
+		/* Submit URB */
+		ret = usb_submit_urb(hif_dev->wlan_rx_data_urb[i], GFP_KERNEL);
+		if (ret)
+			goto err_rx_urb;
+
+	}
+
+	return 0;
+
+err_rx_urb:
+	ath9k_hif_usb_dealloc_rx_skbs(hif_dev);
+	ath9k_hif_usb_dealloc_rx_urbs(hif_dev);
+	return ret;
+}
+
+static void ath9k_hif_usb_dealloc_reg_in_urb(struct hif_device_usb *hif_dev)
+{
+	if (hif_dev->reg_in_urb) {
+		usb_kill_urb(hif_dev->reg_in_urb);
+		usb_free_urb(hif_dev->reg_in_urb);
+		hif_dev->reg_in_urb = NULL;
+	}
+}
+
+static int ath9k_hif_usb_alloc_reg_in_urb(struct hif_device_usb *hif_dev)
+{
+	struct sk_buff *skb;
+
+	hif_dev->reg_in_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (hif_dev->reg_in_urb == NULL)
+		return -ENOMEM;
+
+	skb = __dev_alloc_skb(MAX_REG_IN_BUF_SIZE, GFP_KERNEL);
+	if (!skb)
+		goto err;
+
+	usb_fill_int_urb(hif_dev->reg_in_urb, hif_dev->udev,
+			 usb_rcvintpipe(hif_dev->udev, USB_REG_IN_PIPE),
+			 skb->data, MAX_REG_IN_BUF_SIZE,
+			 ath9k_hif_usb_reg_in_cb, skb, 1);
+
+	if (usb_submit_urb(hif_dev->reg_in_urb, GFP_KERNEL) != 0)
+		goto err_skb;
+
+	return 0;
+
+err_skb:
+	dev_kfree_skb_any(skb);
+err:
+	ath9k_hif_usb_dealloc_reg_in_urb(hif_dev);
+	return -ENOMEM;
+}
+
+static int ath9k_hif_usb_alloc_urbs(struct hif_device_usb *hif_dev)
+{
+	/* TX */
+	if (ath9k_hif_usb_alloc_tx_urbs(hif_dev) < 0)
+		goto err;
+
+	/* RX */
+	if (ath9k_hif_usb_alloc_rx_urbs(hif_dev) < 0)
+		goto err;
+
+	/* Register Read/Write */
+	if (ath9k_hif_usb_alloc_reg_in_urb(hif_dev) < 0)
+		goto err;
+
+	return 0;
+err:
+	return -ENOMEM;
+}
+
+static int ath9k_hif_usb_download_fw(struct hif_device_usb *hif_dev)
+{
+	int transfer, err;
+	const void *data = hif_dev->firmware->data;
+	size_t len = hif_dev->firmware->size;
+	u32 addr = AR9271_FIRMWARE;
+	u8 *buf = kzalloc(4096, GFP_KERNEL);
+
+	if (!buf)
+		return -ENOMEM;
+
+	while (len) {
+		transfer = min_t(int, len, 4096);
+		memcpy(buf, data, transfer);
+
+		err = usb_control_msg(hif_dev->udev,
+				      usb_sndctrlpipe(hif_dev->udev, 0),
+				      FIRMWARE_DOWNLOAD, 0x40 | USB_DIR_OUT,
+				      addr >> 8, 0, buf, transfer, HZ);
+		if (err < 0) {
+			kfree(buf);
+			return err;
+		}
+
+		len -= transfer;
+		data += transfer;
+		addr += transfer;
+	}
+	kfree(buf);
+
+	/*
+	 * Issue FW download complete command to firmware.
+	 */
+	err = usb_control_msg(hif_dev->udev, usb_sndctrlpipe(hif_dev->udev, 0),
+			      FIRMWARE_DOWNLOAD_COMP,
+			      0x40 | USB_DIR_OUT,
+			      AR9271_FIRMWARE_TEXT >> 8, 0, NULL, 0, HZ);
+	if (err)
+		return -EIO;
+
+	dev_info(&hif_dev->udev->dev, "ath9k_htc: Transferred FW: %s, size: %ld\n",
+		 "ar9271.fw", (unsigned long) hif_dev->firmware->size);
+
+	return 0;
+}
+
+static int ath9k_hif_usb_dev_init(struct hif_device_usb *hif_dev,
+				  const char *fw_name)
+{
+	int ret;
+
+	/* Request firmware */
+	ret = request_firmware(&hif_dev->firmware, fw_name, &hif_dev->udev->dev);
+	if (ret) {
+		dev_err(&hif_dev->udev->dev,
+			"ath9k_htc: Firmware - %s not found\n", fw_name);
+		goto err_fw_req;
+	}
+
+	/* Download firmware */
+	ret = ath9k_hif_usb_download_fw(hif_dev);
+	if (ret) {
+		dev_err(&hif_dev->udev->dev,
+			"ath9k_htc: Firmware - %s download failed\n", fw_name);
+		goto err_fw_download;
+	}
+
+	/* Alloc URBs */
+	ret = ath9k_hif_usb_alloc_urbs(hif_dev);
+	if (ret) {
+		dev_err(&hif_dev->udev->dev,
+			"ath9k_htc: Unable to allocate URBs\n");
+		goto err_urb;
+	}
+
+	return 0;
+
+err_urb:
+	/* Nothing */
+err_fw_download:
+	release_firmware(hif_dev->firmware);
+err_fw_req:
+	hif_dev->firmware = NULL;
+	return ret;
+}
+
+static void ath9k_hif_usb_dealloc_urbs(struct hif_device_usb *hif_dev)
+{
+	ath9k_hif_usb_dealloc_reg_in_urb(hif_dev);
+	ath9k_hif_usb_dealloc_tx_urbs(hif_dev);
+	ath9k_hif_usb_dealloc_rx_urbs(hif_dev);
+}
+
+static void ath9k_hif_usb_dev_deinit(struct hif_device_usb *hif_dev)
+{
+	ath9k_hif_usb_dealloc_urbs(hif_dev);
+	if (hif_dev->firmware)
+		release_firmware(hif_dev->firmware);
+}
+
+static int ath9k_hif_usb_probe(struct usb_interface *interface,
+			       const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(interface);
+	struct hif_device_usb *hif_dev;
+	const char *fw_name = (const char *) id->driver_info;
+	int ret = 0;
+
+	hif_dev = kzalloc(sizeof(struct hif_device_usb), GFP_KERNEL);
+	if (!hif_dev) {
+		ret = -ENOMEM;
+		goto err_alloc;
+	}
+
+	usb_get_dev(udev);
+	hif_dev->udev = udev;
+	hif_dev->interface = interface;
+	hif_dev->device_id = id->idProduct;
+#ifdef CONFIG_PM
+	udev->reset_resume = 1;
+#endif
+	usb_set_intfdata(interface, hif_dev);
+
+	ret = ath9k_hif_usb_dev_init(hif_dev, fw_name);
+	if (ret) {
+		ret = -EINVAL;
+		goto err_hif_init_usb;
+	}
+
+	hif_dev->htc_handle = ath9k_htc_hw_alloc(hif_dev);
+	if (hif_dev->htc_handle == NULL) {
+		ret = -ENOMEM;
+		goto err_htc_hw_alloc;
+	}
+
+	ret = ath9k_htc_hw_init(&hif_usb, hif_dev->htc_handle, hif_dev,
+				&hif_dev->udev->dev, hif_dev->device_id,
+				ATH9K_HIF_USB);
+	if (ret) {
+		ret = -EINVAL;
+		goto err_htc_hw_init;
+	}
+
+	dev_info(&hif_dev->udev->dev, "ath9k_htc: USB layer initialized\n");
+
+	return 0;
+
+err_htc_hw_init:
+	ath9k_htc_hw_free(hif_dev->htc_handle);
+err_htc_hw_alloc:
+	ath9k_hif_usb_dev_deinit(hif_dev);
+err_hif_init_usb:
+	usb_set_intfdata(interface, NULL);
+	kfree(hif_dev);
+	usb_put_dev(udev);
+err_alloc:
+	return ret;
+}
+
+static void ath9k_hif_usb_disconnect(struct usb_interface *interface)
+{
+	struct usb_device *udev = interface_to_usbdev(interface);
+	struct hif_device_usb *hif_dev =
+		(struct hif_device_usb *) usb_get_intfdata(interface);
+
+	if (hif_dev) {
+		ath9k_htc_hw_deinit(hif_dev->htc_handle, true);
+		ath9k_htc_hw_free(hif_dev->htc_handle);
+		ath9k_hif_usb_dev_deinit(hif_dev);
+		usb_set_intfdata(interface, NULL);
+	}
+
+	if (hif_dev->flags & HIF_USB_START)
+		usb_reset_device(udev);
+
+	kfree(hif_dev);
+	dev_info(&udev->dev, "ath9k_htc: USB layer deinitialized\n");
+	usb_put_dev(udev);
+}
+
+#ifdef CONFIG_PM
+static int ath9k_hif_usb_suspend(struct usb_interface *interface,
+				 pm_message_t message)
+{
+	struct hif_device_usb *hif_dev =
+		(struct hif_device_usb *) usb_get_intfdata(interface);
+
+	ath9k_hif_usb_dealloc_urbs(hif_dev);
+
+	return 0;
+}
+
+static int ath9k_hif_usb_resume(struct usb_interface *interface)
+{
+	struct hif_device_usb *hif_dev =
+		(struct hif_device_usb *) usb_get_intfdata(interface);
+	int ret;
+
+	ret = ath9k_hif_usb_alloc_urbs(hif_dev);
+	if (ret)
+		return ret;
+
+	if (hif_dev->firmware) {
+		ret = ath9k_hif_usb_download_fw(hif_dev);
+		if (ret)
+			goto fail_resume;
+	} else {
+		ath9k_hif_usb_dealloc_urbs(hif_dev);
+		return -EIO;
+	}
+
+	mdelay(100);
+
+	ret = ath9k_htc_resume(hif_dev->htc_handle);
+
+	if (ret)
+		goto fail_resume;
+
+	return 0;
+
+fail_resume:
+	ath9k_hif_usb_dealloc_urbs(hif_dev);
+
+	return ret;
+}
+#endif
+
+static struct usb_driver ath9k_hif_usb_driver = {
+	.name = "ath9k_hif_usb",
+	.probe = ath9k_hif_usb_probe,
+	.disconnect = ath9k_hif_usb_disconnect,
+#ifdef CONFIG_PM
+	.suspend = ath9k_hif_usb_suspend,
+	.resume = ath9k_hif_usb_resume,
+	.reset_resume = ath9k_hif_usb_resume,
+#endif
+	.id_table = ath9k_hif_usb_ids,
+	.soft_unbind = 1,
+};
+
+int ath9k_hif_usb_init(void)
+{
+	return usb_register(&ath9k_hif_usb_driver);
+}
+
+void ath9k_hif_usb_exit(void)
+{
+	usb_deregister(&ath9k_hif_usb_driver);
+}

+ 105 - 0
drivers/net/wireless/ath/ath9k/hif_usb.h

@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2010 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef HTC_USB_H
+#define HTC_USB_H
+
+#define AR9271_FIRMWARE       0x501000
+#define AR9271_FIRMWARE_TEXT  0x903000
+
+#define FIRMWARE_DOWNLOAD       0x30
+#define FIRMWARE_DOWNLOAD_COMP  0x31
+
+#define ATH_USB_RX_STREAM_MODE_TAG 0x4e00
+#define ATH_USB_TX_STREAM_MODE_TAG 0x697e
+
+/* FIXME: Verify these numbers (with Windows) */
+#define MAX_TX_URB_NUM  8
+#define MAX_TX_BUF_NUM  1024
+#define MAX_TX_BUF_SIZE 32768
+#define MAX_TX_AGGR_NUM 20
+
+#define MAX_RX_URB_NUM  8
+#define MAX_RX_BUF_SIZE 16384
+
+#define MAX_REG_OUT_URB_NUM  1
+#define MAX_REG_OUT_BUF_NUM  8
+
+#define MAX_REG_IN_BUF_SIZE 64
+
+/* USB Endpoint definition */
+#define USB_WLAN_TX_PIPE  1
+#define USB_WLAN_RX_PIPE  2
+#define USB_REG_IN_PIPE   3
+#define USB_REG_OUT_PIPE  4
+
+#define HIF_USB_MAX_RXPIPES 2
+#define HIF_USB_MAX_TXPIPES 4
+
+struct tx_buf {
+	u8 *buf;
+	u16 len;
+	u16 offset;
+	struct urb *urb;
+	struct sk_buff_head skb_queue;
+	struct hif_device_usb *hif_dev;
+	struct list_head list;
+};
+
+#define HIF_USB_TX_STOP  BIT(0)
+#define HIF_USB_TX_FLUSH BIT(1)
+
+struct hif_usb_tx {
+	u8 flags;
+	u8 tx_buf_cnt;
+	u16 tx_skb_cnt;
+	struct sk_buff_head tx_skb_queue;
+	struct list_head tx_buf;
+	struct list_head tx_pending;
+	spinlock_t tx_lock;
+};
+
+struct cmd_buf {
+	struct sk_buff *skb;
+	struct hif_device_usb *hif_dev;
+};
+
+#define HIF_USB_START BIT(0)
+
+struct hif_device_usb {
+	u16 device_id;
+	struct usb_device *udev;
+	struct usb_interface *interface;
+	const struct firmware *firmware;
+	struct htc_target *htc_handle;
+	u8 flags;
+
+	struct hif_usb_tx tx;
+
+	struct urb *wlan_rx_data_urb[MAX_RX_URB_NUM];
+	struct urb *reg_in_urb;
+
+	struct sk_buff *remain_skb;
+	int rx_remain_len;
+	int rx_pkt_len;
+	int rx_transfer_len;
+	int rx_pad_len;
+};
+
+int ath9k_hif_usb_init(void);
+void ath9k_hif_usb_exit(void);
+
+#endif /* HTC_USB_H */

+ 441 - 0
drivers/net/wireless/ath/ath9k/htc.h

@@ -0,0 +1,441 @@
+/*
+ * Copyright (c) 2010 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef HTC_H
+#define HTC_H
+
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/firmware.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/leds.h>
+#include <net/mac80211.h>
+
+#include "common.h"
+#include "htc_hst.h"
+#include "hif_usb.h"
+#include "wmi.h"
+
+#define ATH_STA_SHORT_CALINTERVAL 1000    /* 1 second */
+#define ATH_ANI_POLLINTERVAL      100     /* 100 ms */
+#define ATH_LONG_CALINTERVAL      30000   /* 30 seconds */
+#define ATH_RESTART_CALINTERVAL   1200000 /* 20 minutes */
+
+#define ATH_DEFAULT_BMISS_LIMIT 10
+#define IEEE80211_MS_TO_TU(x)   (((x) * 1000) / 1024)
+#define TSF_TO_TU(_h, _l) \
+	((((u32)(_h)) << 22) | (((u32)(_l)) >> 10))
+
+extern struct ieee80211_ops ath9k_htc_ops;
+extern int htc_modparam_nohwcrypt;
+
+enum htc_phymode {
+	HTC_MODE_AUTO		= 0,
+	HTC_MODE_11A		= 1,
+	HTC_MODE_11B		= 2,
+	HTC_MODE_11G		= 3,
+	HTC_MODE_FH		= 4,
+	HTC_MODE_TURBO_A	= 5,
+	HTC_MODE_TURBO_G	= 6,
+	HTC_MODE_11NA		= 7,
+	HTC_MODE_11NG		= 8
+};
+
+enum htc_opmode {
+	HTC_M_STA	= 1,
+	HTC_M_IBSS	= 0,
+	HTC_M_AHDEMO	= 3,
+	HTC_M_HOSTAP	= 6,
+	HTC_M_MONITOR	= 8,
+	HTC_M_WDS	= 2
+};
+
+#define ATH9K_HTC_HDRSPACE sizeof(struct htc_frame_hdr)
+#define ATH9K_HTC_AMPDU	1
+#define ATH9K_HTC_NORMAL 2
+
+#define ATH9K_HTC_TX_CTSONLY      0x1
+#define ATH9K_HTC_TX_RTSCTS       0x2
+#define ATH9K_HTC_TX_USE_MIN_RATE 0x100
+
+struct tx_frame_hdr {
+	u8 data_type;
+	u8 node_idx;
+	u8 vif_idx;
+	u8 tidno;
+	u32 flags; /* ATH9K_HTC_TX_* */
+	u8 key_type;
+	u8 keyix;
+	u8 reserved[26];
+} __packed;
+
+struct tx_mgmt_hdr {
+	u8 node_idx;
+	u8 vif_idx;
+	u8 tidno;
+	u8 flags;
+	u8 key_type;
+	u8 keyix;
+	u16 reserved;
+} __packed;
+
+struct tx_beacon_header {
+	u8 len_changed;
+	u8 vif_index;
+	u16 rev;
+} __packed;
+
+struct ath9k_htc_target_hw {
+	u32 flags;
+	u32 flags_ext;
+	u32 ampdu_limit;
+	u8 ampdu_subframes;
+	u8 tx_chainmask;
+	u8 tx_chainmask_legacy;
+	u8 rtscts_ratecode;
+	u8 protmode;
+} __packed;
+
+struct ath9k_htc_cap_target {
+	u32 flags;
+	u32 flags_ext;
+	u32 ampdu_limit;
+	u8 ampdu_subframes;
+	u8 tx_chainmask;
+	u8 tx_chainmask_legacy;
+	u8 rtscts_ratecode;
+	u8 protmode;
+} __packed;
+
+struct ath9k_htc_target_vif {
+	u8 index;
+	u8 des_bssid[ETH_ALEN];
+	enum htc_opmode opmode;
+	u8 myaddr[ETH_ALEN];
+	u8 bssid[ETH_ALEN];
+	u32 flags;
+	u32 flags_ext;
+	u16 ps_sta;
+	u16 rtsthreshold;
+	u8 ath_cap;
+	u8 node;
+	s8 mcast_rate;
+} __packed;
+
+#define ATH_HTC_STA_AUTH  0x0001
+#define ATH_HTC_STA_QOS   0x0002
+#define ATH_HTC_STA_ERP   0x0004
+#define ATH_HTC_STA_HT    0x0008
+
+/* FIXME: UAPSD variables */
+struct ath9k_htc_target_sta {
+	u16 associd;
+	u16 txpower;
+	u32 ucastkey;
+	u8 macaddr[ETH_ALEN];
+	u8 bssid[ETH_ALEN];
+	u8 sta_index;
+	u8 vif_index;
+	u8 vif_sta;
+	u16 flags; /* ATH_HTC_STA_* */
+	u16 htcap;
+	u8 valid;
+	u16 capinfo;
+	struct ath9k_htc_target_hw *hw;
+	struct ath9k_htc_target_vif *vif;
+	u16 txseqmgmt;
+	u8 is_vif_sta;
+	u16 maxampdu;
+	u16 iv16;
+	u32 iv32;
+} __packed;
+
+struct ath9k_htc_target_aggr {
+	u8 sta_index;
+	u8 tidno;
+	u8 aggr_enable;
+	u8 padding;
+} __packed;
+
+#define ATH_HTC_RATE_MAX 30
+
+#define WLAN_RC_DS_FLAG  0x01
+#define WLAN_RC_40_FLAG  0x02
+#define WLAN_RC_SGI_FLAG 0x04
+#define WLAN_RC_HT_FLAG  0x08
+
+struct ath9k_htc_rateset {
+	u8 rs_nrates;
+	u8 rs_rates[ATH_HTC_RATE_MAX];
+};
+
+struct ath9k_htc_rate {
+	struct ath9k_htc_rateset legacy_rates;
+	struct ath9k_htc_rateset ht_rates;
+} __packed;
+
+struct ath9k_htc_target_rate {
+	u8 sta_index;
+	u8 isnew;
+	u32 capflags;
+	struct ath9k_htc_rate rates;
+};
+
+struct ath9k_htc_target_stats {
+	u32 tx_shortretry;
+	u32 tx_longretry;
+	u32 tx_xretries;
+	u32 ht_txunaggr_xretry;
+	u32 ht_tx_xretries;
+} __packed;
+
+struct ath9k_htc_vif {
+	u8 index;
+};
+
+#define ATH9K_HTC_MAX_STA 8
+#define ATH9K_HTC_MAX_TID 8
+
+enum tid_aggr_state {
+	AGGR_STOP = 0,
+	AGGR_PROGRESS,
+	AGGR_START,
+	AGGR_OPERATIONAL
+};
+
+struct ath9k_htc_sta {
+	u8 index;
+	enum tid_aggr_state tid_state[ATH9K_HTC_MAX_TID];
+};
+
+struct ath9k_htc_aggr_work {
+	u16 tid;
+	u8 sta_addr[ETH_ALEN];
+	struct ieee80211_hw *hw;
+	struct ieee80211_vif *vif;
+	enum ieee80211_ampdu_mlme_action action;
+	struct mutex mutex;
+};
+
+#define ATH9K_HTC_RXBUF 256
+#define HTC_RX_FRAME_HEADER_SIZE 40
+
+struct ath9k_htc_rxbuf {
+	bool in_process;
+	struct sk_buff *skb;
+	struct ath_htc_rx_status rxstatus;
+	struct list_head list;
+};
+
+struct ath9k_htc_rx {
+	int last_rssi; /* FIXME: per-STA */
+	struct list_head rxbuf;
+	spinlock_t rxbuflock;
+};
+
+struct ath9k_htc_tx_ctl {
+	u8 type; /* ATH9K_HTC_* */
+};
+
+#ifdef CONFIG_ATH9K_HTC_DEBUGFS
+
+#define TX_STAT_INC(c) (hif_dev->htc_handle->drv_priv->debug.tx_stats.c++)
+#define RX_STAT_INC(c) (hif_dev->htc_handle->drv_priv->debug.rx_stats.c++)
+
+struct ath_tx_stats {
+	u32 buf_queued;
+	u32 buf_completed;
+	u32 skb_queued;
+	u32 skb_completed;
+};
+
+struct ath_rx_stats {
+	u32 skb_allocated;
+	u32 skb_completed;
+	u32 skb_dropped;
+};
+
+struct ath9k_debug {
+	struct dentry *debugfs_phy;
+	struct dentry *debugfs_tgt_stats;
+	struct dentry *debugfs_xmit;
+	struct dentry *debugfs_recv;
+	struct ath_tx_stats tx_stats;
+	struct ath_rx_stats rx_stats;
+	u32 txrate;
+};
+
+#else
+
+#define TX_STAT_INC(c) do { } while (0)
+#define RX_STAT_INC(c) do { } while (0)
+
+#endif /* CONFIG_ATH9K_HTC_DEBUGFS */
+
+#define ATH_LED_PIN_DEF             1
+#define ATH_LED_PIN_9287            8
+#define ATH_LED_PIN_9271            15
+#define ATH_LED_ON_DURATION_IDLE    350	/* in msecs */
+#define ATH_LED_OFF_DURATION_IDLE   250	/* in msecs */
+
+enum ath_led_type {
+	ATH_LED_RADIO,
+	ATH_LED_ASSOC,
+	ATH_LED_TX,
+	ATH_LED_RX
+};
+
+struct ath_led {
+	struct ath9k_htc_priv *priv;
+	struct led_classdev led_cdev;
+	enum ath_led_type led_type;
+	struct delayed_work brightness_work;
+	char name[32];
+	bool registered;
+	int brightness;
+};
+
+#define OP_INVALID        BIT(0)
+#define OP_SCANNING       BIT(1)
+#define OP_FULL_RESET     BIT(2)
+#define OP_LED_ASSOCIATED BIT(3)
+#define OP_LED_ON         BIT(4)
+#define OP_PREAMBLE_SHORT BIT(5)
+#define OP_PROTECT_ENABLE BIT(6)
+#define OP_TXAGGR         BIT(7)
+#define OP_ASSOCIATED     BIT(8)
+#define OP_ENABLE_BEACON  BIT(9)
+#define OP_LED_DEINIT     BIT(10)
+
+struct ath9k_htc_priv {
+	struct device *dev;
+	struct ieee80211_hw *hw;
+	struct ath_hw *ah;
+	struct htc_target *htc;
+	struct wmi *wmi;
+
+	enum htc_endpoint_id wmi_cmd_ep;
+	enum htc_endpoint_id beacon_ep;
+	enum htc_endpoint_id cab_ep;
+	enum htc_endpoint_id uapsd_ep;
+	enum htc_endpoint_id mgmt_ep;
+	enum htc_endpoint_id data_be_ep;
+	enum htc_endpoint_id data_bk_ep;
+	enum htc_endpoint_id data_vi_ep;
+	enum htc_endpoint_id data_vo_ep;
+
+	u16 op_flags;
+	u16 curtxpow;
+	u16 txpowlimit;
+	u16 nvifs;
+	u16 nstations;
+	u16 seq_no;
+	u32 bmiss_cnt;
+
+	struct sk_buff *beacon;
+	spinlock_t beacon_lock;
+
+	struct ieee80211_vif *vif;
+	unsigned int rxfilter;
+	struct tasklet_struct wmi_tasklet;
+	struct tasklet_struct rx_tasklet;
+	struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS];
+	struct ath9k_htc_rx rx;
+	struct tasklet_struct tx_tasklet;
+	struct sk_buff_head tx_queue;
+	struct ath9k_htc_aggr_work aggr_work;
+	struct delayed_work ath9k_aggr_work;
+	struct delayed_work ath9k_ani_work;
+
+	struct ath_led radio_led;
+	struct ath_led assoc_led;
+	struct ath_led tx_led;
+	struct ath_led rx_led;
+	struct delayed_work ath9k_led_blink_work;
+	int led_on_duration;
+	int led_off_duration;
+	int led_on_cnt;
+	int led_off_cnt;
+	int hwq_map[ATH9K_WME_AC_VO+1];
+
+#ifdef CONFIG_ATH9K_HTC_DEBUGFS
+	struct ath9k_debug debug;
+#endif
+	struct ath9k_htc_target_rate tgt_rate;
+
+	struct mutex mutex;
+};
+
+static inline void ath_read_cachesize(struct ath_common *common, int *csz)
+{
+	common->bus_ops->read_cachesize(common, csz);
+}
+
+void ath9k_htc_beacon_config(struct ath9k_htc_priv *priv,
+			     struct ieee80211_vif *vif,
+			     struct ieee80211_bss_conf *bss_conf);
+void ath9k_htc_swba(struct ath9k_htc_priv *priv, u8 beacon_pending);
+void ath9k_htc_beacon_update(struct ath9k_htc_priv *priv,
+			     struct ieee80211_vif *vif);
+
+void ath9k_htc_rxep(void *priv, struct sk_buff *skb,
+		    enum htc_endpoint_id ep_id);
+void ath9k_htc_txep(void *priv, struct sk_buff *skb, enum htc_endpoint_id ep_id,
+		    bool txok);
+
+void ath9k_htc_station_work(struct work_struct *work);
+void ath9k_htc_aggr_work(struct work_struct *work);
+void ath9k_ani_work(struct work_struct *work);;
+
+int ath9k_tx_init(struct ath9k_htc_priv *priv);
+void ath9k_tx_tasklet(unsigned long data);
+int ath9k_htc_tx_start(struct ath9k_htc_priv *priv, struct sk_buff *skb);
+void ath9k_tx_cleanup(struct ath9k_htc_priv *priv);
+bool ath9k_htc_txq_setup(struct ath9k_htc_priv *priv,
+			 enum ath9k_tx_queue_subtype qtype);
+int get_hw_qnum(u16 queue, int *hwq_map);
+int ath_htc_txq_update(struct ath9k_htc_priv *priv, int qnum,
+		       struct ath9k_tx_queue_info *qinfo);
+
+int ath9k_rx_init(struct ath9k_htc_priv *priv);
+void ath9k_rx_cleanup(struct ath9k_htc_priv *priv);
+void ath9k_host_rx_init(struct ath9k_htc_priv *priv);
+void ath9k_rx_tasklet(unsigned long data);
+
+void ath9k_start_rfkill_poll(struct ath9k_htc_priv *priv);
+void ath9k_init_leds(struct ath9k_htc_priv *priv);
+void ath9k_deinit_leds(struct ath9k_htc_priv *priv);
+
+int ath9k_htc_probe_device(struct htc_target *htc_handle, struct device *dev,
+			   u16 devid);
+void ath9k_htc_disconnect_device(struct htc_target *htc_handle, bool hotunplug);
+#ifdef CONFIG_PM
+int ath9k_htc_resume(struct htc_target *htc_handle);
+#endif
+#ifdef CONFIG_ATH9K_HTC_DEBUGFS
+int ath9k_htc_debug_create_root(void);
+void ath9k_htc_debug_remove_root(void);
+int ath9k_htc_init_debug(struct ath_hw *ah);
+void ath9k_htc_exit_debug(struct ath_hw *ah);
+#else
+static inline int ath9k_htc_debug_create_root(void) { return 0; };
+static inline void ath9k_htc_debug_remove_root(void) {};
+static inline int ath9k_htc_init_debug(struct ath_hw *ah) { return 0; };
+static inline void ath9k_htc_exit_debug(struct ath_hw *ah) {};
+#endif /* CONFIG_ATH9K_HTC_DEBUGFS */
+
+#endif /* HTC_H */

+ 260 - 0
drivers/net/wireless/ath/ath9k/htc_drv_beacon.c

@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 2010 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "htc.h"
+
+#define FUDGE 2
+
+static void ath9k_htc_beacon_config_sta(struct ath9k_htc_priv *priv,
+					struct ieee80211_bss_conf *bss_conf)
+{
+	struct ath_common *common = ath9k_hw_common(priv->ah);
+	struct ath9k_beacon_state bs;
+	enum ath9k_int imask = 0;
+	int dtimperiod, dtimcount, sleepduration;
+	int cfpperiod, cfpcount, bmiss_timeout;
+	u32 nexttbtt = 0, intval, tsftu, htc_imask = 0;
+	u64 tsf;
+	int num_beacons, offset, dtim_dec_count, cfp_dec_count;
+	int ret;
+	u8 cmd_rsp;
+
+	memset(&bs, 0, sizeof(bs));
+
+	intval = bss_conf->beacon_int & ATH9K_BEACON_PERIOD;
+	bmiss_timeout = (ATH_DEFAULT_BMISS_LIMIT * bss_conf->beacon_int);
+
+	/*
+	 * Setup dtim and cfp parameters according to
+	 * last beacon we received (which may be none).
+	 */
+	dtimperiod = bss_conf->dtim_period;
+	if (dtimperiod <= 0)		/* NB: 0 if not known */
+		dtimperiod = 1;
+	dtimcount = 1;
+	if (dtimcount >= dtimperiod)	/* NB: sanity check */
+		dtimcount = 0;
+	cfpperiod = 1;			/* NB: no PCF support yet */
+	cfpcount = 0;
+
+	sleepduration = intval;
+	if (sleepduration <= 0)
+		sleepduration = intval;
+
+	/*
+	 * Pull nexttbtt forward to reflect the current
+	 * TSF and calculate dtim+cfp state for the result.
+	 */
+	tsf = ath9k_hw_gettsf64(priv->ah);
+	tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE;
+
+	num_beacons = tsftu / intval + 1;
+	offset = tsftu % intval;
+	nexttbtt = tsftu - offset;
+	if (offset)
+		nexttbtt += intval;
+
+	/* DTIM Beacon every dtimperiod Beacon */
+	dtim_dec_count = num_beacons % dtimperiod;
+	/* CFP every cfpperiod DTIM Beacon */
+	cfp_dec_count = (num_beacons / dtimperiod) % cfpperiod;
+	if (dtim_dec_count)
+		cfp_dec_count++;
+
+	dtimcount -= dtim_dec_count;
+	if (dtimcount < 0)
+		dtimcount += dtimperiod;
+
+	cfpcount -= cfp_dec_count;
+	if (cfpcount < 0)
+		cfpcount += cfpperiod;
+
+	bs.bs_intval = intval;
+	bs.bs_nexttbtt = nexttbtt;
+	bs.bs_dtimperiod = dtimperiod*intval;
+	bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount*intval;
+	bs.bs_cfpperiod = cfpperiod*bs.bs_dtimperiod;
+	bs.bs_cfpnext = bs.bs_nextdtim + cfpcount*bs.bs_dtimperiod;
+	bs.bs_cfpmaxduration = 0;
+
+	/*
+	 * Calculate the number of consecutive beacons to miss* before taking
+	 * a BMISS interrupt. The configuration is specified in TU so we only
+	 * need calculate based	on the beacon interval.  Note that we clamp the
+	 * result to at most 15 beacons.
+	 */
+	if (sleepduration > intval) {
+		bs.bs_bmissthreshold = ATH_DEFAULT_BMISS_LIMIT / 2;
+	} else {
+		bs.bs_bmissthreshold = DIV_ROUND_UP(bmiss_timeout, intval);
+		if (bs.bs_bmissthreshold > 15)
+			bs.bs_bmissthreshold = 15;
+		else if (bs.bs_bmissthreshold <= 0)
+			bs.bs_bmissthreshold = 1;
+	}
+
+	/*
+	 * Calculate sleep duration. The configuration is given in ms.
+	 * We ensure a multiple of the beacon period is used. Also, if the sleep
+	 * duration is greater than the DTIM period then it makes senses
+	 * to make it a multiple of that.
+	 *
+	 * XXX fixed at 100ms
+	 */
+
+	bs.bs_sleepduration = roundup(IEEE80211_MS_TO_TU(100), sleepduration);
+	if (bs.bs_sleepduration > bs.bs_dtimperiod)
+		bs.bs_sleepduration = bs.bs_dtimperiod;
+
+	/* TSF out of range threshold fixed at 1 second */
+	bs.bs_tsfoor_threshold = ATH9K_TSFOOR_THRESHOLD;
+
+	ath_print(common, ATH_DBG_BEACON, "tsf: %llu tsftu: %u\n", tsf, tsftu);
+	ath_print(common, ATH_DBG_BEACON,
+		  "bmiss: %u sleep: %u cfp-period: %u maxdur: %u next: %u\n",
+		  bs.bs_bmissthreshold, bs.bs_sleepduration,
+		  bs.bs_cfpperiod, bs.bs_cfpmaxduration, bs.bs_cfpnext);
+
+	/* Set the computed STA beacon timers */
+
+	WMI_CMD(WMI_DISABLE_INTR_CMDID);
+	ath9k_hw_set_sta_beacon_timers(priv->ah, &bs);
+	imask |= ATH9K_INT_BMISS;
+	htc_imask = cpu_to_be32(imask);
+	WMI_CMD_BUF(WMI_ENABLE_INTR_CMDID, &htc_imask);
+}
+
+static void ath9k_htc_beacon_config_adhoc(struct ath9k_htc_priv *priv,
+					  struct ieee80211_bss_conf *bss_conf)
+{
+	struct ath_common *common = ath9k_hw_common(priv->ah);
+	enum ath9k_int imask = 0;
+	u32 nexttbtt, intval, htc_imask = 0;
+	int ret;
+	u8 cmd_rsp;
+
+	intval = bss_conf->beacon_int & ATH9K_BEACON_PERIOD;
+	nexttbtt = intval;
+	intval |= ATH9K_BEACON_ENA;
+	if (priv->op_flags & OP_ENABLE_BEACON)
+		imask |= ATH9K_INT_SWBA;
+
+	ath_print(common, ATH_DBG_BEACON,
+		  "IBSS Beacon config, intval: %d, imask: 0x%x\n",
+		  bss_conf->beacon_int, imask);
+
+	WMI_CMD(WMI_DISABLE_INTR_CMDID);
+	ath9k_hw_beaconinit(priv->ah, nexttbtt, intval);
+	priv->bmiss_cnt = 0;
+	htc_imask = cpu_to_be32(imask);
+	WMI_CMD_BUF(WMI_ENABLE_INTR_CMDID, &htc_imask);
+}
+
+void ath9k_htc_beacon_update(struct ath9k_htc_priv *priv,
+			     struct ieee80211_vif *vif)
+{
+	struct ath_common *common = ath9k_hw_common(priv->ah);
+
+	spin_lock_bh(&priv->beacon_lock);
+
+	if (priv->beacon)
+		dev_kfree_skb_any(priv->beacon);
+
+	priv->beacon = ieee80211_beacon_get(priv->hw, vif);
+	if (!priv->beacon)
+		ath_print(common, ATH_DBG_BEACON,
+			  "Unable to allocate beacon\n");
+
+	spin_unlock_bh(&priv->beacon_lock);
+}
+
+void ath9k_htc_swba(struct ath9k_htc_priv *priv, u8 beacon_pending)
+{
+	struct ath9k_htc_vif *avp = (void *)priv->vif->drv_priv;
+	struct tx_beacon_header beacon_hdr;
+	struct ath9k_htc_tx_ctl tx_ctl;
+	struct ieee80211_tx_info *info;
+	u8 *tx_fhdr;
+
+	memset(&beacon_hdr, 0, sizeof(struct tx_beacon_header));
+	memset(&tx_ctl, 0, sizeof(struct ath9k_htc_tx_ctl));
+
+	/* FIXME: Handle BMISS */
+	if (beacon_pending != 0) {
+		priv->bmiss_cnt++;
+		return;
+	}
+
+	spin_lock_bh(&priv->beacon_lock);
+
+	if (unlikely(priv->op_flags & OP_SCANNING)) {
+		spin_unlock_bh(&priv->beacon_lock);
+		return;
+	}
+
+	if (unlikely(priv->beacon == NULL)) {
+		spin_unlock_bh(&priv->beacon_lock);
+		return;
+	}
+
+	/* Free the old SKB first */
+	dev_kfree_skb_any(priv->beacon);
+
+	/* Get a new beacon */
+	priv->beacon = ieee80211_beacon_get(priv->hw, priv->vif);
+	if (!priv->beacon) {
+		spin_unlock_bh(&priv->beacon_lock);
+		return;
+	}
+
+	info = IEEE80211_SKB_CB(priv->beacon);
+	if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
+		struct ieee80211_hdr *hdr =
+			(struct ieee80211_hdr *) priv->beacon->data;
+		priv->seq_no += 0x10;
+		hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
+		hdr->seq_ctrl |= cpu_to_le16(priv->seq_no);
+	}
+
+	tx_ctl.type = ATH9K_HTC_NORMAL;
+	beacon_hdr.vif_index = avp->index;
+	tx_fhdr = skb_push(priv->beacon, sizeof(beacon_hdr));
+	memcpy(tx_fhdr, (u8 *) &beacon_hdr, sizeof(beacon_hdr));
+
+	htc_send(priv->htc, priv->beacon, priv->beacon_ep, &tx_ctl);
+
+	spin_unlock_bh(&priv->beacon_lock);
+}
+
+void ath9k_htc_beacon_config(struct ath9k_htc_priv *priv,
+			     struct ieee80211_vif *vif,
+			     struct ieee80211_bss_conf *bss_conf)
+{
+	struct ath_common *common = ath9k_hw_common(priv->ah);
+
+	switch (vif->type) {
+	case NL80211_IFTYPE_STATION:
+		ath9k_htc_beacon_config_sta(priv, bss_conf);
+		break;
+	case NL80211_IFTYPE_ADHOC:
+		ath9k_htc_beacon_config_adhoc(priv, bss_conf);
+		break;
+	default:
+		ath_print(common, ATH_DBG_CONFIG,
+			  "Unsupported beaconing mode\n");
+		return;
+	}
+}

+ 713 - 0
drivers/net/wireless/ath/ath9k/htc_drv_init.c

@@ -0,0 +1,713 @@
+/*
+ * Copyright (c) 2010 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "htc.h"
+
+MODULE_AUTHOR("Atheros Communications");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("Atheros driver 802.11n HTC based wireless devices");
+
+static unsigned int ath9k_debug = ATH_DBG_DEFAULT;
+module_param_named(debug, ath9k_debug, uint, 0);
+MODULE_PARM_DESC(debug, "Debugging mask");
+
+int htc_modparam_nohwcrypt;
+module_param_named(nohwcrypt, htc_modparam_nohwcrypt, int, 0444);
+MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption");
+
+#define CHAN2G(_freq, _idx)  { \
+	.center_freq = (_freq), \
+	.hw_value = (_idx), \
+	.max_power = 20, \
+}
+
+static struct ieee80211_channel ath9k_2ghz_channels[] = {
+	CHAN2G(2412, 0), /* Channel 1 */
+	CHAN2G(2417, 1), /* Channel 2 */
+	CHAN2G(2422, 2), /* Channel 3 */
+	CHAN2G(2427, 3), /* Channel 4 */
+	CHAN2G(2432, 4), /* Channel 5 */
+	CHAN2G(2437, 5), /* Channel 6 */
+	CHAN2G(2442, 6), /* Channel 7 */
+	CHAN2G(2447, 7), /* Channel 8 */
+	CHAN2G(2452, 8), /* Channel 9 */
+	CHAN2G(2457, 9), /* Channel 10 */
+	CHAN2G(2462, 10), /* Channel 11 */
+	CHAN2G(2467, 11), /* Channel 12 */
+	CHAN2G(2472, 12), /* Channel 13 */
+	CHAN2G(2484, 13), /* Channel 14 */
+};
+
+/* Atheros hardware rate code addition for short premble */
+#define SHPCHECK(__hw_rate, __flags) \
+	((__flags & IEEE80211_RATE_SHORT_PREAMBLE) ? (__hw_rate | 0x04) : 0)
+
+#define RATE(_bitrate, _hw_rate, _flags) {		\
+	.bitrate	= (_bitrate),			\
+	.flags		= (_flags),			\
+	.hw_value	= (_hw_rate),			\
+	.hw_value_short = (SHPCHECK(_hw_rate, _flags))	\
+}
+
+static struct ieee80211_rate ath9k_legacy_rates[] = {
+	RATE(10, 0x1b, 0),
+	RATE(20, 0x1a, IEEE80211_RATE_SHORT_PREAMBLE), /* shortp : 0x1e */
+	RATE(55, 0x19, IEEE80211_RATE_SHORT_PREAMBLE), /* shortp: 0x1d */
+	RATE(110, 0x18, IEEE80211_RATE_SHORT_PREAMBLE), /* short: 0x1c */
+	RATE(60, 0x0b, 0),
+	RATE(90, 0x0f, 0),
+	RATE(120, 0x0a, 0),
+	RATE(180, 0x0e, 0),
+	RATE(240, 0x09, 0),
+	RATE(360, 0x0d, 0),
+	RATE(480, 0x08, 0),
+	RATE(540, 0x0c, 0),
+};
+
+static int ath9k_htc_wait_for_target(struct ath9k_htc_priv *priv)
+{
+	int time_left;
+
+	/* Firmware can take up to 50ms to get ready, to be safe use 1 second */
+	time_left = wait_for_completion_timeout(&priv->htc->target_wait, HZ);
+	if (!time_left) {
+		dev_err(priv->dev, "ath9k_htc: Target is unresponsive\n");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static void ath9k_deinit_priv(struct ath9k_htc_priv *priv)
+{
+	ath9k_htc_exit_debug(priv->ah);
+	ath9k_hw_deinit(priv->ah);
+	tasklet_kill(&priv->wmi_tasklet);
+	tasklet_kill(&priv->rx_tasklet);
+	tasklet_kill(&priv->tx_tasklet);
+	kfree(priv->ah);
+	priv->ah = NULL;
+}
+
+static void ath9k_deinit_device(struct ath9k_htc_priv *priv)
+{
+	struct ieee80211_hw *hw = priv->hw;
+
+	wiphy_rfkill_stop_polling(hw->wiphy);
+	ath9k_deinit_leds(priv);
+	ieee80211_unregister_hw(hw);
+	ath9k_rx_cleanup(priv);
+	ath9k_tx_cleanup(priv);
+	ath9k_deinit_priv(priv);
+}
+
+static inline int ath9k_htc_connect_svc(struct ath9k_htc_priv *priv,
+					u16 service_id,
+					void (*tx) (void *,
+						    struct sk_buff *,
+						    enum htc_endpoint_id,
+						    bool txok),
+					enum htc_endpoint_id *ep_id)
+{
+	struct htc_service_connreq req;
+
+	memset(&req, 0, sizeof(struct htc_service_connreq));
+
+	req.service_id = service_id;
+	req.ep_callbacks.priv = priv;
+	req.ep_callbacks.rx = ath9k_htc_rxep;
+	req.ep_callbacks.tx = tx;
+
+	return htc_connect_service(priv->htc, &req, ep_id);
+}
+
+static int ath9k_init_htc_services(struct ath9k_htc_priv *priv)
+{
+	int ret;
+
+	/* WMI CMD*/
+	ret = ath9k_wmi_connect(priv->htc, priv->wmi, &priv->wmi_cmd_ep);
+	if (ret)
+		goto err;
+
+	/* Beacon */
+	ret = ath9k_htc_connect_svc(priv, WMI_BEACON_SVC, NULL,
+				    &priv->beacon_ep);
+	if (ret)
+		goto err;
+
+	/* CAB */
+	ret = ath9k_htc_connect_svc(priv, WMI_CAB_SVC, ath9k_htc_txep,
+				    &priv->cab_ep);
+	if (ret)
+		goto err;
+
+
+	/* UAPSD */
+	ret = ath9k_htc_connect_svc(priv, WMI_UAPSD_SVC, ath9k_htc_txep,
+				    &priv->uapsd_ep);
+	if (ret)
+		goto err;
+
+	/* MGMT */
+	ret = ath9k_htc_connect_svc(priv, WMI_MGMT_SVC, ath9k_htc_txep,
+				    &priv->mgmt_ep);
+	if (ret)
+		goto err;
+
+	/* DATA BE */
+	ret = ath9k_htc_connect_svc(priv, WMI_DATA_BE_SVC, ath9k_htc_txep,
+				    &priv->data_be_ep);
+	if (ret)
+		goto err;
+
+	/* DATA BK */
+	ret = ath9k_htc_connect_svc(priv, WMI_DATA_BK_SVC, ath9k_htc_txep,
+				    &priv->data_bk_ep);
+	if (ret)
+		goto err;
+
+	/* DATA VI */
+	ret = ath9k_htc_connect_svc(priv, WMI_DATA_VI_SVC, ath9k_htc_txep,
+				    &priv->data_vi_ep);
+	if (ret)
+		goto err;
+
+	/* DATA VO */
+	ret = ath9k_htc_connect_svc(priv, WMI_DATA_VO_SVC, ath9k_htc_txep,
+				    &priv->data_vo_ep);
+	if (ret)
+		goto err;
+
+	ret = htc_init(priv->htc);
+	if (ret)
+		goto err;
+
+	return 0;
+
+err:
+	dev_err(priv->dev, "ath9k_htc: Unable to initialize HTC services\n");
+	return ret;
+}
+
+static int ath9k_reg_notifier(struct wiphy *wiphy,
+			      struct regulatory_request *request)
+{
+	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+	struct ath9k_htc_priv *priv = hw->priv;
+
+	return ath_reg_notifier_apply(wiphy, request,
+				      ath9k_hw_regulatory(priv->ah));
+}
+
+static unsigned int ath9k_ioread32(void *hw_priv, u32 reg_offset)
+{
+	struct ath_hw *ah = (struct ath_hw *) hw_priv;
+	struct ath_common *common = ath9k_hw_common(ah);
+	struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
+	__be32 val, reg = cpu_to_be32(reg_offset);
+	int r;
+
+	r = ath9k_wmi_cmd(priv->wmi, WMI_REG_READ_CMDID,
+			  (u8 *) &reg, sizeof(reg),
+			  (u8 *) &val, sizeof(val),
+			  100);
+	if (unlikely(r)) {
+		ath_print(common, ATH_DBG_WMI,
+			  "REGISTER READ FAILED: (0x%04x, %d)\n",
+			   reg_offset, r);
+		return -EIO;
+	}
+
+	return be32_to_cpu(val);
+}
+
+static void ath9k_iowrite32(void *hw_priv, u32 val, u32 reg_offset)
+{
+	struct ath_hw *ah = (struct ath_hw *) hw_priv;
+	struct ath_common *common = ath9k_hw_common(ah);
+	struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
+	__be32 buf[2] = {
+		cpu_to_be32(reg_offset),
+		cpu_to_be32(val),
+	};
+	int r;
+
+	r = ath9k_wmi_cmd(priv->wmi, WMI_REG_WRITE_CMDID,
+			  (u8 *) &buf, sizeof(buf),
+			  (u8 *) &val, sizeof(val),
+			  100);
+	if (unlikely(r)) {
+		ath_print(common, ATH_DBG_WMI,
+			  "REGISTER WRITE FAILED:(0x%04x, %d)\n",
+			  reg_offset, r);
+	}
+}
+
+static const struct ath_ops ath9k_common_ops = {
+	.read = ath9k_ioread32,
+	.write = ath9k_iowrite32,
+};
+
+static void ath_usb_read_cachesize(struct ath_common *common, int *csz)
+{
+	*csz = L1_CACHE_BYTES >> 2;
+}
+
+static bool ath_usb_eeprom_read(struct ath_common *common, u32 off, u16 *data)
+{
+	struct ath_hw *ah = (struct ath_hw *) common->ah;
+
+	(void)REG_READ(ah, AR5416_EEPROM_OFFSET + (off << AR5416_EEPROM_S));
+
+	if (!ath9k_hw_wait(ah,
+			   AR_EEPROM_STATUS_DATA,
+			   AR_EEPROM_STATUS_DATA_BUSY |
+			   AR_EEPROM_STATUS_DATA_PROT_ACCESS, 0,
+			   AH_WAIT_TIMEOUT))
+		return false;
+
+	*data = MS(REG_READ(ah, AR_EEPROM_STATUS_DATA),
+		   AR_EEPROM_STATUS_DATA_VAL);
+
+	return true;
+}
+
+static const struct ath_bus_ops ath9k_usb_bus_ops = {
+	.read_cachesize = ath_usb_read_cachesize,
+	.eeprom_read = ath_usb_eeprom_read,
+};
+
+static void setup_ht_cap(struct ath9k_htc_priv *priv,
+			 struct ieee80211_sta_ht_cap *ht_info)
+{
+	ht_info->ht_supported = true;
+	ht_info->cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
+		       IEEE80211_HT_CAP_SM_PS |
+		       IEEE80211_HT_CAP_SGI_40 |
+		       IEEE80211_HT_CAP_DSSSCCK40;
+
+	ht_info->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
+	ht_info->ampdu_density = IEEE80211_HT_MPDU_DENSITY_8;
+
+	memset(&ht_info->mcs, 0, sizeof(ht_info->mcs));
+	ht_info->mcs.rx_mask[0] = 0xff;
+	ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_DEFINED;
+}
+
+static int ath9k_init_queues(struct ath9k_htc_priv *priv)
+{
+	struct ath_common *common = ath9k_hw_common(priv->ah);
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(priv->hwq_map); i++)
+		priv->hwq_map[i] = -1;
+
+	if (!ath9k_htc_txq_setup(priv, ATH9K_WME_AC_BE)) {
+		ath_print(common, ATH_DBG_FATAL,
+			  "Unable to setup xmit queue for BE traffic\n");
+		goto err;
+	}
+
+	if (!ath9k_htc_txq_setup(priv, ATH9K_WME_AC_BK)) {
+		ath_print(common, ATH_DBG_FATAL,
+			  "Unable to setup xmit queue for BK traffic\n");
+		goto err;
+	}
+	if (!ath9k_htc_txq_setup(priv, ATH9K_WME_AC_VI)) {
+		ath_print(common, ATH_DBG_FATAL,
+			  "Unable to setup xmit queue for VI traffic\n");
+		goto err;
+	}
+	if (!ath9k_htc_txq_setup(priv, ATH9K_WME_AC_VO)) {
+		ath_print(common, ATH_DBG_FATAL,
+			  "Unable to setup xmit queue for VO traffic\n");
+		goto err;
+	}
+
+	return 0;
+
+err:
+	return -EINVAL;
+}
+
+static void ath9k_init_crypto(struct ath9k_htc_priv *priv)
+{
+	struct ath_common *common = ath9k_hw_common(priv->ah);
+	int i = 0;
+
+	/* Get the hardware key cache size. */
+	common->keymax = priv->ah->caps.keycache_size;
+	if (common->keymax > ATH_KEYMAX) {
+		ath_print(common, ATH_DBG_ANY,
+			  "Warning, using only %u entries in %u key cache\n",
+			  ATH_KEYMAX, common->keymax);
+		common->keymax = ATH_KEYMAX;
+	}
+
+	/*
+	 * Reset the key cache since some parts do not
+	 * reset the contents on initial power up.
+	 */
+	for (i = 0; i < common->keymax; i++)
+		ath9k_hw_keyreset(priv->ah, (u16) i);
+
+	if (ath9k_hw_getcapability(priv->ah, ATH9K_CAP_CIPHER,
+				   ATH9K_CIPHER_TKIP, NULL)) {
+		/*
+		 * Whether we should enable h/w TKIP MIC.
+		 * XXX: if we don't support WME TKIP MIC, then we wouldn't
+		 * report WMM capable, so it's always safe to turn on
+		 * TKIP MIC in this case.
+		 */
+		ath9k_hw_setcapability(priv->ah, ATH9K_CAP_TKIP_MIC, 0, 1, NULL);
+	}
+
+	/*
+	 * Check whether the separate key cache entries
+	 * are required to handle both tx+rx MIC keys.
+	 * With split mic keys the number of stations is limited
+	 * to 27 otherwise 59.
+	 */
+	if (ath9k_hw_getcapability(priv->ah, ATH9K_CAP_CIPHER,
+				   ATH9K_CIPHER_TKIP, NULL)
+	    && ath9k_hw_getcapability(priv->ah, ATH9K_CAP_CIPHER,
+				      ATH9K_CIPHER_MIC, NULL)
+	    && ath9k_hw_getcapability(priv->ah, ATH9K_CAP_TKIP_SPLIT,
+				      0, NULL))
+		common->splitmic = 1;
+
+	/* turn on mcast key search if possible */
+	if (!ath9k_hw_getcapability(priv->ah, ATH9K_CAP_MCAST_KEYSRCH, 0, NULL))
+		(void)ath9k_hw_setcapability(priv->ah, ATH9K_CAP_MCAST_KEYSRCH,
+					     1, 1, NULL);
+}
+
+static void ath9k_init_channels_rates(struct ath9k_htc_priv *priv)
+{
+	if (test_bit(ATH9K_MODE_11G, priv->ah->caps.wireless_modes)) {
+		priv->sbands[IEEE80211_BAND_2GHZ].channels =
+			ath9k_2ghz_channels;
+		priv->sbands[IEEE80211_BAND_2GHZ].band = IEEE80211_BAND_2GHZ;
+		priv->sbands[IEEE80211_BAND_2GHZ].n_channels =
+			ARRAY_SIZE(ath9k_2ghz_channels);
+		priv->sbands[IEEE80211_BAND_2GHZ].bitrates = ath9k_legacy_rates;
+		priv->sbands[IEEE80211_BAND_2GHZ].n_bitrates =
+			ARRAY_SIZE(ath9k_legacy_rates);
+	}
+}
+
+static void ath9k_init_misc(struct ath9k_htc_priv *priv)
+{
+	struct ath_common *common = ath9k_hw_common(priv->ah);
+
+	common->tx_chainmask = priv->ah->caps.tx_chainmask;
+	common->rx_chainmask = priv->ah->caps.rx_chainmask;
+
+	if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK)
+		memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN);
+
+	priv->op_flags |= OP_TXAGGR;
+}
+
+static int ath9k_init_priv(struct ath9k_htc_priv *priv, u16 devid)
+{
+	struct ath_hw *ah = NULL;
+	struct ath_common *common;
+	int ret = 0, csz = 0;
+
+	priv->op_flags |= OP_INVALID;
+
+	ah = kzalloc(sizeof(struct ath_hw), GFP_KERNEL);
+	if (!ah)
+		return -ENOMEM;
+
+	ah->hw_version.devid = devid;
+	ah->hw_version.subsysid = 0; /* FIXME */
+	priv->ah = ah;
+
+	common = ath9k_hw_common(ah);
+	common->ops = &ath9k_common_ops;
+	common->bus_ops = &ath9k_usb_bus_ops;
+	common->ah = ah;
+	common->hw = priv->hw;
+	common->priv = priv;
+	common->debug_mask = ath9k_debug;
+
+	spin_lock_init(&priv->wmi->wmi_lock);
+	spin_lock_init(&priv->beacon_lock);
+	mutex_init(&priv->mutex);
+	mutex_init(&priv->aggr_work.mutex);
+	tasklet_init(&priv->wmi_tasklet, ath9k_wmi_tasklet,
+		     (unsigned long)priv);
+	tasklet_init(&priv->rx_tasklet, ath9k_rx_tasklet,
+		     (unsigned long)priv);
+	tasklet_init(&priv->tx_tasklet, ath9k_tx_tasklet, (unsigned long)priv);
+	INIT_DELAYED_WORK(&priv->ath9k_aggr_work, ath9k_htc_aggr_work);
+	INIT_DELAYED_WORK(&priv->ath9k_ani_work, ath9k_ani_work);
+
+	/*
+	 * Cache line size is used to size and align various
+	 * structures used to communicate with the hardware.
+	 */
+	ath_read_cachesize(common, &csz);
+	common->cachelsz = csz << 2; /* convert to bytes */
+
+	ret = ath9k_hw_init(ah);
+	if (ret) {
+		ath_print(common, ATH_DBG_FATAL,
+			  "Unable to initialize hardware; "
+			  "initialization status: %d\n", ret);
+		goto err_hw;
+	}
+
+	ret = ath9k_htc_init_debug(ah);
+	if (ret) {
+		ath_print(common, ATH_DBG_FATAL,
+			  "Unable to create debugfs files\n");
+		goto err_debug;
+	}
+
+	ret = ath9k_init_queues(priv);
+	if (ret)
+		goto err_queues;
+
+	ath9k_init_crypto(priv);
+	ath9k_init_channels_rates(priv);
+	ath9k_init_misc(priv);
+
+	return 0;
+
+err_queues:
+	ath9k_htc_exit_debug(ah);
+err_debug:
+	ath9k_hw_deinit(ah);
+err_hw:
+
+	kfree(ah);
+	priv->ah = NULL;
+
+	return ret;
+}
+
+static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv,
+			       struct ieee80211_hw *hw)
+{
+	struct ath_common *common = ath9k_hw_common(priv->ah);
+
+	hw->flags = IEEE80211_HW_SIGNAL_DBM |
+		IEEE80211_HW_AMPDU_AGGREGATION |
+		IEEE80211_HW_SPECTRUM_MGMT |
+		IEEE80211_HW_HAS_RATE_CONTROL;
+
+	hw->wiphy->interface_modes =
+		BIT(NL80211_IFTYPE_STATION) |
+		BIT(NL80211_IFTYPE_ADHOC);
+
+	hw->queues = 4;
+	hw->channel_change_time = 5000;
+	hw->max_listen_interval = 10;
+	hw->vif_data_size = sizeof(struct ath9k_htc_vif);
+	hw->sta_data_size = sizeof(struct ath9k_htc_sta);
+
+	/* tx_frame_hdr is larger than tx_mgmt_hdr anyway */
+	hw->extra_tx_headroom = sizeof(struct tx_frame_hdr) +
+		sizeof(struct htc_frame_hdr) + 4;
+
+	if (test_bit(ATH9K_MODE_11G, priv->ah->caps.wireless_modes))
+		hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
+			&priv->sbands[IEEE80211_BAND_2GHZ];
+
+	if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_HT) {
+		if (test_bit(ATH9K_MODE_11G, priv->ah->caps.wireless_modes))
+			setup_ht_cap(priv,
+				     &priv->sbands[IEEE80211_BAND_2GHZ].ht_cap);
+	}
+
+	SET_IEEE80211_PERM_ADDR(hw, common->macaddr);
+}
+
+static int ath9k_init_device(struct ath9k_htc_priv *priv, u16 devid)
+{
+	struct ieee80211_hw *hw = priv->hw;
+	struct ath_common *common;
+	struct ath_hw *ah;
+	int error = 0;
+	struct ath_regulatory *reg;
+
+	/* Bring up device */
+	error = ath9k_init_priv(priv, devid);
+	if (error != 0)
+		goto err_init;
+
+	ah = priv->ah;
+	common = ath9k_hw_common(ah);
+	ath9k_set_hw_capab(priv, hw);
+
+	/* Initialize regulatory */
+	error = ath_regd_init(&common->regulatory, priv->hw->wiphy,
+			      ath9k_reg_notifier);
+	if (error)
+		goto err_regd;
+
+	reg = &common->regulatory;
+
+	/* Setup TX */
+	error = ath9k_tx_init(priv);
+	if (error != 0)
+		goto err_tx;
+
+	/* Setup RX */
+	error = ath9k_rx_init(priv);
+	if (error != 0)
+		goto err_rx;
+
+	/* Register with mac80211 */
+	error = ieee80211_register_hw(hw);
+	if (error)
+		goto err_register;
+
+	/* Handle world regulatory */
+	if (!ath_is_world_regd(reg)) {
+		error = regulatory_hint(hw->wiphy, reg->alpha2);
+		if (error)
+			goto err_world;
+	}
+
+	ath9k_init_leds(priv);
+	ath9k_start_rfkill_poll(priv);
+
+	return 0;
+
+err_world:
+	ieee80211_unregister_hw(hw);
+err_register:
+	ath9k_rx_cleanup(priv);
+err_rx:
+	ath9k_tx_cleanup(priv);
+err_tx:
+	/* Nothing */
+err_regd:
+	ath9k_deinit_priv(priv);
+err_init:
+	return error;
+}
+
+int ath9k_htc_probe_device(struct htc_target *htc_handle, struct device *dev,
+			   u16 devid)
+{
+	struct ieee80211_hw *hw;
+	struct ath9k_htc_priv *priv;
+	int ret;
+
+	hw = ieee80211_alloc_hw(sizeof(struct ath9k_htc_priv), &ath9k_htc_ops);
+	if (!hw)
+		return -ENOMEM;
+
+	priv = hw->priv;
+	priv->hw = hw;
+	priv->htc = htc_handle;
+	priv->dev = dev;
+	htc_handle->drv_priv = priv;
+	SET_IEEE80211_DEV(hw, priv->dev);
+
+	ret = ath9k_htc_wait_for_target(priv);
+	if (ret)
+		goto err_free;
+
+	priv->wmi = ath9k_init_wmi(priv);
+	if (!priv->wmi) {
+		ret = -EINVAL;
+		goto err_free;
+	}
+
+	ret = ath9k_init_htc_services(priv);
+	if (ret)
+		goto err_init;
+
+	ret = ath9k_init_device(priv, devid);
+	if (ret)
+		goto err_init;
+
+	return 0;
+
+err_init:
+	ath9k_deinit_wmi(priv);
+err_free:
+	ieee80211_free_hw(hw);
+	return ret;
+}
+
+void ath9k_htc_disconnect_device(struct htc_target *htc_handle, bool hotunplug)
+{
+	if (htc_handle->drv_priv) {
+		ath9k_deinit_device(htc_handle->drv_priv);
+		ath9k_deinit_wmi(htc_handle->drv_priv);
+		ieee80211_free_hw(htc_handle->drv_priv->hw);
+	}
+}
+
+#ifdef CONFIG_PM
+int ath9k_htc_resume(struct htc_target *htc_handle)
+{
+	int ret;
+
+	ret = ath9k_htc_wait_for_target(htc_handle->drv_priv);
+	if (ret)
+		return ret;
+
+	ret = ath9k_init_htc_services(htc_handle->drv_priv);
+	return ret;
+}
+#endif
+
+static int __init ath9k_htc_init(void)
+{
+	int error;
+
+	error = ath9k_htc_debug_create_root();
+	if (error < 0) {
+		printk(KERN_ERR
+			"ath9k_htc: Unable to create debugfs root: %d\n",
+			error);
+		goto err_dbg;
+	}
+
+	error = ath9k_hif_usb_init();
+	if (error < 0) {
+		printk(KERN_ERR
+			"ath9k_htc: No USB devices found,"
+			" driver not installed.\n");
+		error = -ENODEV;
+		goto err_usb;
+	}
+
+	return 0;
+
+err_usb:
+	ath9k_htc_debug_remove_root();
+err_dbg:
+	return error;
+}
+module_init(ath9k_htc_init);
+
+static void __exit ath9k_htc_exit(void)
+{
+	ath9k_hif_usb_exit();
+	ath9k_htc_debug_remove_root();
+	printk(KERN_INFO "ath9k_htc: Driver unloaded\n");
+}
+module_exit(ath9k_htc_exit);

+ 1626 - 0
drivers/net/wireless/ath/ath9k/htc_drv_main.c

@@ -0,0 +1,1626 @@
+/*
+ * Copyright (c) 2010 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "htc.h"
+
+#ifdef CONFIG_ATH9K_HTC_DEBUGFS
+static struct dentry *ath9k_debugfs_root;
+#endif
+
+/*************/
+/* Utilities */
+/*************/
+
+static void ath_update_txpow(struct ath9k_htc_priv *priv)
+{
+	struct ath_hw *ah = priv->ah;
+	u32 txpow;
+
+	if (priv->curtxpow != priv->txpowlimit) {
+		ath9k_hw_set_txpowerlimit(ah, priv->txpowlimit);
+		/* read back in case value is clamped */
+		ath9k_hw_getcapability(ah, ATH9K_CAP_TXPOW, 1, &txpow);
+		priv->curtxpow = txpow;
+	}
+}
+
+/* HACK Alert: Use 11NG for 2.4, use 11NA for 5 */
+static enum htc_phymode ath9k_htc_get_curmode(struct ath9k_htc_priv *priv,
+					      struct ath9k_channel *ichan)
+{
+	enum htc_phymode mode;
+
+	mode = HTC_MODE_AUTO;
+
+	switch (ichan->chanmode) {
+	case CHANNEL_G:
+	case CHANNEL_G_HT20:
+	case CHANNEL_G_HT40PLUS:
+	case CHANNEL_G_HT40MINUS:
+		mode = HTC_MODE_11NG;
+		break;
+	case CHANNEL_A:
+	case CHANNEL_A_HT20:
+	case CHANNEL_A_HT40PLUS:
+	case CHANNEL_A_HT40MINUS:
+		mode = HTC_MODE_11NA;
+		break;
+	default:
+		break;
+	}
+
+	return mode;
+}
+
+static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv,
+				 struct ieee80211_hw *hw,
+				 struct ath9k_channel *hchan)
+{
+	struct ath_hw *ah = priv->ah;
+	struct ath_common *common = ath9k_hw_common(ah);
+	struct ieee80211_conf *conf = &common->hw->conf;
+	bool fastcc = true;
+	struct ieee80211_channel *channel = hw->conf.channel;
+	enum htc_phymode mode;
+	u16 htc_mode;
+	u8 cmd_rsp;
+	int ret;
+
+	if (priv->op_flags & OP_INVALID)
+		return -EIO;
+
+	if (priv->op_flags & OP_FULL_RESET)
+		fastcc = false;
+
+	/* Fiddle around with fastcc later on, for now just use full reset */
+	fastcc = false;
+
+	htc_stop(priv->htc);
+	WMI_CMD(WMI_DISABLE_INTR_CMDID);
+	WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
+	WMI_CMD(WMI_STOP_RECV_CMDID);
+
+	ath_print(common, ATH_DBG_CONFIG,
+		  "(%u MHz) -> (%u MHz), HT: %d, HT40: %d\n",
+		  priv->ah->curchan->channel,
+		  channel->center_freq, conf_is_ht(conf), conf_is_ht40(conf));
+
+	ret = ath9k_hw_reset(ah, hchan, fastcc);
+	if (ret) {
+		ath_print(common, ATH_DBG_FATAL,
+			  "Unable to reset channel (%u Mhz) "
+			  "reset status %d\n", channel->center_freq, ret);
+		goto err;
+	}
+
+	ath_update_txpow(priv);
+
+	WMI_CMD(WMI_START_RECV_CMDID);
+	if (ret)
+		goto err;
+
+	ath9k_host_rx_init(priv);
+
+	mode = ath9k_htc_get_curmode(priv, hchan);
+	htc_mode = cpu_to_be16(mode);
+	WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
+	if (ret)
+		goto err;
+
+	WMI_CMD(WMI_ENABLE_INTR_CMDID);
+	if (ret)
+		goto err;
+
+	htc_start(priv->htc);
+
+	priv->op_flags &= ~OP_FULL_RESET;
+err:
+	return ret;
+}
+
+static int ath9k_htc_add_monitor_interface(struct ath9k_htc_priv *priv)
+{
+	struct ath_common *common = ath9k_hw_common(priv->ah);
+	struct ath9k_htc_target_vif hvif;
+	int ret = 0;
+	u8 cmd_rsp;
+
+	if (priv->nvifs > 0)
+		return -ENOBUFS;
+
+	memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
+	memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN);
+
+	hvif.opmode = cpu_to_be32(HTC_M_MONITOR);
+	priv->ah->opmode = NL80211_IFTYPE_MONITOR;
+	hvif.index = priv->nvifs;
+
+	WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif);
+	if (ret)
+		return ret;
+
+	priv->nvifs++;
+	return 0;
+}
+
+static int ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv)
+{
+	struct ath_common *common = ath9k_hw_common(priv->ah);
+	struct ath9k_htc_target_vif hvif;
+	int ret = 0;
+	u8 cmd_rsp;
+
+	memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
+	memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN);
+	hvif.index = 0; /* Should do for now */
+	WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
+	priv->nvifs--;
+
+	return ret;
+}
+
+static int ath9k_htc_add_station(struct ath9k_htc_priv *priv,
+				 struct ieee80211_vif *vif,
+				 struct ieee80211_sta *sta)
+{
+	struct ath_common *common = ath9k_hw_common(priv->ah);
+	struct ath9k_htc_target_sta tsta;
+	struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv;
+	struct ath9k_htc_sta *ista;
+	int ret;
+	u8 cmd_rsp;
+
+	if (priv->nstations >= ATH9K_HTC_MAX_STA)
+		return -ENOBUFS;
+
+	memset(&tsta, 0, sizeof(struct ath9k_htc_target_sta));
+
+	if (sta) {
+		ista = (struct ath9k_htc_sta *) sta->drv_priv;
+		memcpy(&tsta.macaddr, sta->addr, ETH_ALEN);
+		memcpy(&tsta.bssid, common->curbssid, ETH_ALEN);
+		tsta.associd = common->curaid;
+		tsta.is_vif_sta = 0;
+		tsta.valid = true;
+		ista->index = priv->nstations;
+	} else {
+		memcpy(&tsta.macaddr, vif->addr, ETH_ALEN);
+		tsta.is_vif_sta = 1;
+	}
+
+	tsta.sta_index = priv->nstations;
+	tsta.vif_index = avp->index;
+	tsta.maxampdu = 0xffff;
+	if (sta && sta->ht_cap.ht_supported)
+		tsta.flags = cpu_to_be16(ATH_HTC_STA_HT);
+
+	WMI_CMD_BUF(WMI_NODE_CREATE_CMDID, &tsta);
+	if (ret) {
+		if (sta)
+			ath_print(common, ATH_DBG_FATAL,
+			  "Unable to add station entry for: %pM\n", sta->addr);
+		return ret;
+	}
+
+	if (sta)
+		ath_print(common, ATH_DBG_CONFIG,
+			  "Added a station entry for: %pM (idx: %d)\n",
+			  sta->addr, tsta.sta_index);
+
+	priv->nstations++;
+	return 0;
+}
+
+static int ath9k_htc_remove_station(struct ath9k_htc_priv *priv,
+				    struct ieee80211_vif *vif,
+				    struct ieee80211_sta *sta)
+{
+	struct ath_common *common = ath9k_hw_common(priv->ah);
+	struct ath9k_htc_sta *ista;
+	int ret;
+	u8 cmd_rsp, sta_idx;
+
+	if (sta) {
+		ista = (struct ath9k_htc_sta *) sta->drv_priv;
+		sta_idx = ista->index;
+	} else {
+		sta_idx = 0;
+	}
+
+	WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx);
+	if (ret) {
+		if (sta)
+			ath_print(common, ATH_DBG_FATAL,
+			  "Unable to remove station entry for: %pM\n",
+			  sta->addr);
+		return ret;
+	}
+
+	if (sta)
+		ath_print(common, ATH_DBG_CONFIG,
+			  "Removed a station entry for: %pM (idx: %d)\n",
+			  sta->addr, sta_idx);
+
+	priv->nstations--;
+	return 0;
+}
+
+static int ath9k_htc_update_cap_target(struct ath9k_htc_priv *priv)
+{
+	struct ath9k_htc_cap_target tcap;
+	int ret;
+	u8 cmd_rsp;
+
+	memset(&tcap, 0, sizeof(struct ath9k_htc_cap_target));
+
+	/* FIXME: Values are hardcoded */
+	tcap.flags = 0x240c40;
+	tcap.flags_ext = 0x80601000;
+	tcap.ampdu_limit = 0xffff0000;
+	tcap.ampdu_subframes = 20;
+	tcap.tx_chainmask_legacy = 1;
+	tcap.protmode = 1;
+	tcap.tx_chainmask = 1;
+
+	WMI_CMD_BUF(WMI_TARGET_IC_UPDATE_CMDID, &tcap);
+
+	return ret;
+}
+
+static int ath9k_htc_init_rate(struct ath9k_htc_priv *priv,
+				 struct ieee80211_vif *vif,
+				 struct ieee80211_sta *sta)
+{
+	struct ath_common *common = ath9k_hw_common(priv->ah);
+	struct ath9k_htc_sta *ista = (struct ath9k_htc_sta *) sta->drv_priv;
+	struct ieee80211_supported_band *sband;
+	struct ath9k_htc_target_rate trate;
+	u32 caps = 0;
+	u8 cmd_rsp;
+	int i, j, ret;
+
+	memset(&trate, 0, sizeof(trate));
+
+	/* Only 2GHz is supported */
+	sband = priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ];
+
+	for (i = 0, j = 0; i < sband->n_bitrates; i++) {
+		if (sta->supp_rates[sband->band] & BIT(i)) {
+			priv->tgt_rate.rates.legacy_rates.rs_rates[j]
+				= (sband->bitrates[i].bitrate * 2) / 10;
+			j++;
+		}
+	}
+	priv->tgt_rate.rates.legacy_rates.rs_nrates = j;
+
+	if (sta->ht_cap.ht_supported) {
+		for (i = 0, j = 0; i < 77; i++) {
+			if (sta->ht_cap.mcs.rx_mask[i/8] & (1<<(i%8)))
+				priv->tgt_rate.rates.ht_rates.rs_rates[j++] = i;
+			if (j == ATH_HTC_RATE_MAX)
+				break;
+		}
+		priv->tgt_rate.rates.ht_rates.rs_nrates = j;
+
+		caps = WLAN_RC_HT_FLAG;
+		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)
+			caps |= WLAN_RC_SGI_FLAG;
+
+	}
+
+	priv->tgt_rate.sta_index = ista->index;
+	priv->tgt_rate.isnew = 1;
+	trate = priv->tgt_rate;
+	priv->tgt_rate.capflags = caps;
+	trate.capflags = cpu_to_be32(caps);
+
+	WMI_CMD_BUF(WMI_RC_RATE_UPDATE_CMDID, &trate);
+	if (ret) {
+		ath_print(common, ATH_DBG_FATAL,
+			  "Unable to initialize Rate information on target\n");
+		return ret;
+	}
+
+	ath_print(common, ATH_DBG_CONFIG,
+		  "Updated target STA: %pM (caps: 0x%x)\n", sta->addr, caps);
+	return 0;
+}
+
+static bool check_rc_update(struct ieee80211_hw *hw, bool *cw40)
+{
+	struct ath9k_htc_priv *priv = hw->priv;
+	struct ieee80211_conf *conf = &hw->conf;
+
+	if (!conf_is_ht(conf))
+		return false;
+
+	if (!(priv->op_flags & OP_ASSOCIATED) ||
+	    (priv->op_flags & OP_SCANNING))
+		return false;
+
+	if (conf_is_ht40(conf)) {
+		if (priv->ah->curchan->chanmode &
+			(CHANNEL_HT40PLUS | CHANNEL_HT40MINUS)) {
+			return false;
+		} else {
+			*cw40 = true;
+			return true;
+		}
+	} else {  /* ht20 */
+		if (priv->ah->curchan->chanmode & CHANNEL_HT20)
+			return false;
+		else
+			return true;
+	}
+}
+
+static void ath9k_htc_rc_update(struct ath9k_htc_priv *priv, bool is_cw40)
+{
+	struct ath9k_htc_target_rate trate;
+	struct ath_common *common = ath9k_hw_common(priv->ah);
+	int ret;
+	u8 cmd_rsp;
+
+	memset(&trate, 0, sizeof(trate));
+
+	trate = priv->tgt_rate;
+
+	if (is_cw40)
+		priv->tgt_rate.capflags |= WLAN_RC_40_FLAG;
+	else
+		priv->tgt_rate.capflags &= ~WLAN_RC_40_FLAG;
+
+	trate.capflags = cpu_to_be32(priv->tgt_rate.capflags);
+
+	WMI_CMD_BUF(WMI_RC_RATE_UPDATE_CMDID, &trate);
+	if (ret) {
+		ath_print(common, ATH_DBG_FATAL,
+			  "Unable to update Rate information on target\n");
+		return;
+	}
+
+	ath_print(common, ATH_DBG_CONFIG, "Rate control updated with "
+		  "caps:0x%x on target\n", priv->tgt_rate.capflags);
+}
+
+static int ath9k_htc_aggr_oper(struct ath9k_htc_priv *priv,
+			       struct ieee80211_vif *vif,
+			       u8 *sta_addr, u8 tid, bool oper)
+{
+	struct ath_common *common = ath9k_hw_common(priv->ah);
+	struct ath9k_htc_target_aggr aggr;
+	struct ieee80211_sta *sta = NULL;
+	struct ath9k_htc_sta *ista = (struct ath9k_htc_sta *) sta->drv_priv;
+	int ret = 0;
+	u8 cmd_rsp;
+
+	if (tid > ATH9K_HTC_MAX_TID)
+		return -EINVAL;
+
+	rcu_read_lock();
+	sta = ieee80211_find_sta(vif, sta_addr);
+	if (sta) {
+		ista = (struct ath9k_htc_sta *) sta->drv_priv;
+	} else {
+		rcu_read_unlock();
+		return -EINVAL;
+	}
+
+	if (!ista) {
+		rcu_read_unlock();
+		return -EINVAL;
+	}
+
+	memset(&aggr, 0, sizeof(struct ath9k_htc_target_aggr));
+
+	aggr.sta_index = ista->index;
+	rcu_read_unlock();
+	aggr.tidno = tid;
+	aggr.aggr_enable = oper;
+
+	if (oper)
+		ista->tid_state[tid] = AGGR_START;
+	else
+		ista->tid_state[tid] = AGGR_STOP;
+
+	WMI_CMD_BUF(WMI_TX_AGGR_ENABLE_CMDID, &aggr);
+	if (ret)
+		ath_print(common, ATH_DBG_CONFIG,
+			  "Unable to %s TX aggregation for (%pM, %d)\n",
+			  (oper) ? "start" : "stop", sta->addr, tid);
+	else
+		ath_print(common, ATH_DBG_CONFIG,
+			  "%s aggregation for (%pM, %d)\n",
+			  (oper) ? "Starting" : "Stopping", sta->addr, tid);
+
+	return ret;
+}
+
+void ath9k_htc_aggr_work(struct work_struct *work)
+{
+	int ret = 0;
+	struct ath9k_htc_priv *priv =
+		container_of(work, struct ath9k_htc_priv,
+			     ath9k_aggr_work.work);
+	struct ath9k_htc_aggr_work *wk = &priv->aggr_work;
+
+	mutex_lock(&wk->mutex);
+
+	switch (wk->action) {
+	case IEEE80211_AMPDU_TX_START:
+		ret = ath9k_htc_aggr_oper(priv, wk->vif, wk->sta_addr,
+					  wk->tid, true);
+		if (!ret)
+			ieee80211_start_tx_ba_cb(wk->vif, wk->sta_addr,
+						 wk->tid);
+		break;
+	case IEEE80211_AMPDU_TX_STOP:
+		ath9k_htc_aggr_oper(priv, wk->vif, wk->sta_addr,
+				    wk->tid, false);
+		ieee80211_stop_tx_ba_cb(wk->vif, wk->sta_addr, wk->tid);
+		break;
+	default:
+		ath_print(ath9k_hw_common(priv->ah), ATH_DBG_FATAL,
+			  "Unknown AMPDU action\n");
+	}
+
+	mutex_unlock(&wk->mutex);
+}
+
+/*********/
+/* DEBUG */
+/*********/
+
+#ifdef CONFIG_ATH9K_HTC_DEBUGFS
+
+static int ath9k_debugfs_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static ssize_t read_file_tgt_stats(struct file *file, char __user *user_buf,
+				   size_t count, loff_t *ppos)
+{
+	struct ath9k_htc_priv *priv =
+		(struct ath9k_htc_priv *) file->private_data;
+	struct ath9k_htc_target_stats cmd_rsp;
+	char buf[512];
+	unsigned int len = 0;
+	int ret = 0;
+
+	memset(&cmd_rsp, 0, sizeof(cmd_rsp));
+
+	WMI_CMD(WMI_TGT_STATS_CMDID);
+	if (ret)
+		return -EINVAL;
+
+
+	len += snprintf(buf + len, sizeof(buf) - len,
+			"%19s : %10u\n", "TX Short Retries",
+			be32_to_cpu(cmd_rsp.tx_shortretry));
+	len += snprintf(buf + len, sizeof(buf) - len,
+			"%19s : %10u\n", "TX Long Retries",
+			be32_to_cpu(cmd_rsp.tx_longretry));
+	len += snprintf(buf + len, sizeof(buf) - len,
+			"%19s : %10u\n", "TX Xretries",
+			be32_to_cpu(cmd_rsp.tx_xretries));
+	len += snprintf(buf + len, sizeof(buf) - len,
+			"%19s : %10u\n", "TX Unaggr. Xretries",
+			be32_to_cpu(cmd_rsp.ht_txunaggr_xretry));
+	len += snprintf(buf + len, sizeof(buf) - len,
+			"%19s : %10u\n", "TX Xretries (HT)",
+			be32_to_cpu(cmd_rsp.ht_tx_xretries));
+	len += snprintf(buf + len, sizeof(buf) - len,
+			"%19s : %10u\n", "TX Rate", priv->debug.txrate);
+
+	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static const struct file_operations fops_tgt_stats = {
+	.read = read_file_tgt_stats,
+	.open = ath9k_debugfs_open,
+	.owner = THIS_MODULE
+};
+
+static ssize_t read_file_xmit(struct file *file, char __user *user_buf,
+			      size_t count, loff_t *ppos)
+{
+	struct ath9k_htc_priv *priv =
+		(struct ath9k_htc_priv *) file->private_data;
+	char buf[512];
+	unsigned int len = 0;
+
+	len += snprintf(buf + len, sizeof(buf) - len,
+			"%20s : %10u\n", "Buffers queued",
+			priv->debug.tx_stats.buf_queued);
+	len += snprintf(buf + len, sizeof(buf) - len,
+			"%20s : %10u\n", "Buffers completed",
+			priv->debug.tx_stats.buf_completed);
+	len += snprintf(buf + len, sizeof(buf) - len,
+			"%20s : %10u\n", "SKBs queued",
+			priv->debug.tx_stats.skb_queued);
+	len += snprintf(buf + len, sizeof(buf) - len,
+			"%20s : %10u\n", "SKBs completed",
+			priv->debug.tx_stats.skb_completed);
+
+	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static const struct file_operations fops_xmit = {
+	.read = read_file_xmit,
+	.open = ath9k_debugfs_open,
+	.owner = THIS_MODULE
+};
+
+static ssize_t read_file_recv(struct file *file, char __user *user_buf,
+			      size_t count, loff_t *ppos)
+{
+	struct ath9k_htc_priv *priv =
+		(struct ath9k_htc_priv *) file->private_data;
+	char buf[512];
+	unsigned int len = 0;
+
+	len += snprintf(buf + len, sizeof(buf) - len,
+			"%20s : %10u\n", "SKBs allocated",
+			priv->debug.rx_stats.skb_allocated);
+	len += snprintf(buf + len, sizeof(buf) - len,
+			"%20s : %10u\n", "SKBs completed",
+			priv->debug.rx_stats.skb_completed);
+	len += snprintf(buf + len, sizeof(buf) - len,
+			"%20s : %10u\n", "SKBs Dropped",
+			priv->debug.rx_stats.skb_dropped);
+
+	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static const struct file_operations fops_recv = {
+	.read = read_file_recv,
+	.open = ath9k_debugfs_open,
+	.owner = THIS_MODULE
+};
+
+int ath9k_htc_init_debug(struct ath_hw *ah)
+{
+	struct ath_common *common = ath9k_hw_common(ah);
+	struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
+
+	if (!ath9k_debugfs_root)
+		return -ENOENT;
+
+	priv->debug.debugfs_phy = debugfs_create_dir(wiphy_name(priv->hw->wiphy),
+						     ath9k_debugfs_root);
+	if (!priv->debug.debugfs_phy)
+		goto err;
+
+	priv->debug.debugfs_tgt_stats = debugfs_create_file("tgt_stats", S_IRUSR,
+						    priv->debug.debugfs_phy,
+						    priv, &fops_tgt_stats);
+	if (!priv->debug.debugfs_tgt_stats)
+		goto err;
+
+
+	priv->debug.debugfs_xmit = debugfs_create_file("xmit", S_IRUSR,
+						       priv->debug.debugfs_phy,
+						       priv, &fops_xmit);
+	if (!priv->debug.debugfs_xmit)
+		goto err;
+
+	priv->debug.debugfs_recv = debugfs_create_file("recv", S_IRUSR,
+						       priv->debug.debugfs_phy,
+						       priv, &fops_recv);
+	if (!priv->debug.debugfs_recv)
+		goto err;
+
+	return 0;
+
+err:
+	ath9k_htc_exit_debug(ah);
+	return -ENOMEM;
+}
+
+void ath9k_htc_exit_debug(struct ath_hw *ah)
+{
+	struct ath_common *common = ath9k_hw_common(ah);
+	struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
+
+	debugfs_remove(priv->debug.debugfs_recv);
+	debugfs_remove(priv->debug.debugfs_xmit);
+	debugfs_remove(priv->debug.debugfs_tgt_stats);
+	debugfs_remove(priv->debug.debugfs_phy);
+}
+
+int ath9k_htc_debug_create_root(void)
+{
+	ath9k_debugfs_root = debugfs_create_dir(KBUILD_MODNAME, NULL);
+	if (!ath9k_debugfs_root)
+		return -ENOENT;
+
+	return 0;
+}
+
+void ath9k_htc_debug_remove_root(void)
+{
+	debugfs_remove(ath9k_debugfs_root);
+	ath9k_debugfs_root = NULL;
+}
+
+#endif /* CONFIG_ATH9K_HTC_DEBUGFS */
+
+/*******/
+/* ANI */
+/*******/
+
+static void ath_start_ani(struct ath9k_htc_priv *priv)
+{
+	struct ath_common *common = ath9k_hw_common(priv->ah);
+	unsigned long timestamp = jiffies_to_msecs(jiffies);
+
+	common->ani.longcal_timer = timestamp;
+	common->ani.shortcal_timer = timestamp;
+	common->ani.checkani_timer = timestamp;
+
+	ieee80211_queue_delayed_work(common->hw, &priv->ath9k_ani_work,
+				     msecs_to_jiffies(ATH_ANI_POLLINTERVAL));
+}
+
+void ath9k_ani_work(struct work_struct *work)
+{
+	struct ath9k_htc_priv *priv =
+		container_of(work, struct ath9k_htc_priv,
+			     ath9k_ani_work.work);
+	struct ath_hw *ah = priv->ah;
+	struct ath_common *common = ath9k_hw_common(ah);
+	bool longcal = false;
+	bool shortcal = false;
+	bool aniflag = false;
+	unsigned int timestamp = jiffies_to_msecs(jiffies);
+	u32 cal_interval, short_cal_interval;
+
+	short_cal_interval = ATH_STA_SHORT_CALINTERVAL;
+
+	/* Long calibration runs independently of short calibration. */
+	if ((timestamp - common->ani.longcal_timer) >= ATH_LONG_CALINTERVAL) {
+		longcal = true;
+		ath_print(common, ATH_DBG_ANI, "longcal @%lu\n", jiffies);
+		common->ani.longcal_timer = timestamp;
+	}
+
+	/* Short calibration applies only while caldone is false */
+	if (!common->ani.caldone) {
+		if ((timestamp - common->ani.shortcal_timer) >=
+		    short_cal_interval) {
+			shortcal = true;
+			ath_print(common, ATH_DBG_ANI,
+				  "shortcal @%lu\n", jiffies);
+			common->ani.shortcal_timer = timestamp;
+			common->ani.resetcal_timer = timestamp;
+		}
+	} else {
+		if ((timestamp - common->ani.resetcal_timer) >=
+		    ATH_RESTART_CALINTERVAL) {
+			common->ani.caldone = ath9k_hw_reset_calvalid(ah);
+			if (common->ani.caldone)
+				common->ani.resetcal_timer = timestamp;
+		}
+	}
+
+	/* Verify whether we must check ANI */
+	if ((timestamp - common->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) {
+		aniflag = true;
+		common->ani.checkani_timer = timestamp;
+	}
+
+	/* Skip all processing if there's nothing to do. */
+	if (longcal || shortcal || aniflag) {
+		/* Call ANI routine if necessary */
+		if (aniflag)
+			ath9k_hw_ani_monitor(ah, ah->curchan);
+
+		/* Perform calibration if necessary */
+		if (longcal || shortcal) {
+			common->ani.caldone =
+				ath9k_hw_calibrate(ah, ah->curchan,
+						   common->rx_chainmask,
+						   longcal);
+
+			if (longcal)
+				common->ani.noise_floor =
+					ath9k_hw_getchan_noise(ah, ah->curchan);
+
+			ath_print(common, ATH_DBG_ANI,
+				  " calibrate chan %u/%x nf: %d\n",
+				  ah->curchan->channel,
+				  ah->curchan->channelFlags,
+				  common->ani.noise_floor);
+		}
+	}
+
+	/*
+	* Set timer interval based on previous results.
+	* The interval must be the shortest necessary to satisfy ANI,
+	* short calibration and long calibration.
+	*/
+	cal_interval = ATH_LONG_CALINTERVAL;
+	if (priv->ah->config.enable_ani)
+		cal_interval = min(cal_interval, (u32)ATH_ANI_POLLINTERVAL);
+	if (!common->ani.caldone)
+		cal_interval = min(cal_interval, (u32)short_cal_interval);
+
+	ieee80211_queue_delayed_work(common->hw, &priv->ath9k_ani_work,
+				     msecs_to_jiffies(cal_interval));
+}
+
+/*******/
+/* LED */
+/*******/
+
+static void ath9k_led_blink_work(struct work_struct *work)
+{
+	struct ath9k_htc_priv *priv = container_of(work, struct ath9k_htc_priv,
+						   ath9k_led_blink_work.work);
+
+	if (!(priv->op_flags & OP_LED_ASSOCIATED))
+		return;
+
+	if ((priv->led_on_duration == ATH_LED_ON_DURATION_IDLE) ||
+	    (priv->led_off_duration == ATH_LED_OFF_DURATION_IDLE))
+		ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, 0);
+	else
+		ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin,
+				  (priv->op_flags & OP_LED_ON) ? 1 : 0);
+
+	ieee80211_queue_delayed_work(priv->hw,
+				     &priv->ath9k_led_blink_work,
+				     (priv->op_flags & OP_LED_ON) ?
+				     msecs_to_jiffies(priv->led_off_duration) :
+				     msecs_to_jiffies(priv->led_on_duration));
+
+	priv->led_on_duration = priv->led_on_cnt ?
+		max((ATH_LED_ON_DURATION_IDLE - priv->led_on_cnt), 25) :
+		ATH_LED_ON_DURATION_IDLE;
+	priv->led_off_duration = priv->led_off_cnt ?
+		max((ATH_LED_OFF_DURATION_IDLE - priv->led_off_cnt), 10) :
+		ATH_LED_OFF_DURATION_IDLE;
+	priv->led_on_cnt = priv->led_off_cnt = 0;
+
+	if (priv->op_flags & OP_LED_ON)
+		priv->op_flags &= ~OP_LED_ON;
+	else
+		priv->op_flags |= OP_LED_ON;
+}
+
+static void ath9k_led_brightness_work(struct work_struct *work)
+{
+	struct ath_led *led = container_of(work, struct ath_led,
+					   brightness_work.work);
+	struct ath9k_htc_priv *priv = led->priv;
+
+	switch (led->brightness) {
+	case LED_OFF:
+		if (led->led_type == ATH_LED_ASSOC ||
+		    led->led_type == ATH_LED_RADIO) {
+			ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin,
+					  (led->led_type == ATH_LED_RADIO));
+			priv->op_flags &= ~OP_LED_ASSOCIATED;
+			if (led->led_type == ATH_LED_RADIO)
+				priv->op_flags &= ~OP_LED_ON;
+		} else {
+			priv->led_off_cnt++;
+		}
+		break;
+	case LED_FULL:
+		if (led->led_type == ATH_LED_ASSOC) {
+			priv->op_flags |= OP_LED_ASSOCIATED;
+			ieee80211_queue_delayed_work(priv->hw,
+					     &priv->ath9k_led_blink_work, 0);
+		} else if (led->led_type == ATH_LED_RADIO) {
+			ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, 0);
+			priv->op_flags |= OP_LED_ON;
+		} else {
+			priv->led_on_cnt++;
+		}
+		break;
+	default:
+		break;
+	}
+}
+
+static void ath9k_led_brightness(struct led_classdev *led_cdev,
+				 enum led_brightness brightness)
+{
+	struct ath_led *led = container_of(led_cdev, struct ath_led, led_cdev);
+	struct ath9k_htc_priv *priv = led->priv;
+
+	led->brightness = brightness;
+	if (!(priv->op_flags & OP_LED_DEINIT))
+		ieee80211_queue_delayed_work(priv->hw,
+					     &led->brightness_work, 0);
+}
+
+static void ath9k_led_stop_brightness(struct ath9k_htc_priv *priv)
+{
+	cancel_delayed_work_sync(&priv->radio_led.brightness_work);
+	cancel_delayed_work_sync(&priv->assoc_led.brightness_work);
+	cancel_delayed_work_sync(&priv->tx_led.brightness_work);
+	cancel_delayed_work_sync(&priv->rx_led.brightness_work);
+}
+
+static int ath9k_register_led(struct ath9k_htc_priv *priv, struct ath_led *led,
+			      char *trigger)
+{
+	int ret;
+
+	led->priv = priv;
+	led->led_cdev.name = led->name;
+	led->led_cdev.default_trigger = trigger;
+	led->led_cdev.brightness_set = ath9k_led_brightness;
+
+	ret = led_classdev_register(wiphy_dev(priv->hw->wiphy), &led->led_cdev);
+	if (ret)
+		ath_print(ath9k_hw_common(priv->ah), ATH_DBG_FATAL,
+			  "Failed to register led:%s", led->name);
+	else
+		led->registered = 1;
+
+	INIT_DELAYED_WORK(&led->brightness_work, ath9k_led_brightness_work);
+
+	return ret;
+}
+
+static void ath9k_unregister_led(struct ath_led *led)
+{
+	if (led->registered) {
+		led_classdev_unregister(&led->led_cdev);
+		led->registered = 0;
+	}
+}
+
+void ath9k_deinit_leds(struct ath9k_htc_priv *priv)
+{
+	priv->op_flags |= OP_LED_DEINIT;
+	ath9k_unregister_led(&priv->assoc_led);
+	priv->op_flags &= ~OP_LED_ASSOCIATED;
+	ath9k_unregister_led(&priv->tx_led);
+	ath9k_unregister_led(&priv->rx_led);
+	ath9k_unregister_led(&priv->radio_led);
+	ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, 1);
+}
+
+void ath9k_init_leds(struct ath9k_htc_priv *priv)
+{
+	char *trigger;
+	int ret;
+
+	if (AR_SREV_9287(priv->ah))
+		priv->ah->led_pin = ATH_LED_PIN_9287;
+	else if (AR_SREV_9271(priv->ah))
+		priv->ah->led_pin = ATH_LED_PIN_9271;
+	else
+		priv->ah->led_pin = ATH_LED_PIN_DEF;
+
+	/* Configure gpio 1 for output */
+	ath9k_hw_cfg_output(priv->ah, priv->ah->led_pin,
+			    AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
+	/* LED off, active low */
+	ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, 1);
+
+	INIT_DELAYED_WORK(&priv->ath9k_led_blink_work, ath9k_led_blink_work);
+
+	trigger = ieee80211_get_radio_led_name(priv->hw);
+	snprintf(priv->radio_led.name, sizeof(priv->radio_led.name),
+		"ath9k-%s::radio", wiphy_name(priv->hw->wiphy));
+	ret = ath9k_register_led(priv, &priv->radio_led, trigger);
+	priv->radio_led.led_type = ATH_LED_RADIO;
+	if (ret)
+		goto fail;
+
+	trigger = ieee80211_get_assoc_led_name(priv->hw);
+	snprintf(priv->assoc_led.name, sizeof(priv->assoc_led.name),
+		"ath9k-%s::assoc", wiphy_name(priv->hw->wiphy));
+	ret = ath9k_register_led(priv, &priv->assoc_led, trigger);
+	priv->assoc_led.led_type = ATH_LED_ASSOC;
+	if (ret)
+		goto fail;
+
+	trigger = ieee80211_get_tx_led_name(priv->hw);
+	snprintf(priv->tx_led.name, sizeof(priv->tx_led.name),
+		"ath9k-%s::tx", wiphy_name(priv->hw->wiphy));
+	ret = ath9k_register_led(priv, &priv->tx_led, trigger);
+	priv->tx_led.led_type = ATH_LED_TX;
+	if (ret)
+		goto fail;
+
+	trigger = ieee80211_get_rx_led_name(priv->hw);
+	snprintf(priv->rx_led.name, sizeof(priv->rx_led.name),
+		"ath9k-%s::rx", wiphy_name(priv->hw->wiphy));
+	ret = ath9k_register_led(priv, &priv->rx_led, trigger);
+	priv->rx_led.led_type = ATH_LED_RX;
+	if (ret)
+		goto fail;
+
+	priv->op_flags &= ~OP_LED_DEINIT;
+
+	return;
+
+fail:
+	cancel_delayed_work_sync(&priv->ath9k_led_blink_work);
+	ath9k_deinit_leds(priv);
+}
+
+/*******************/
+/*	Rfkill	   */
+/*******************/
+
+static bool ath_is_rfkill_set(struct ath9k_htc_priv *priv)
+{
+	return ath9k_hw_gpio_get(priv->ah, priv->ah->rfkill_gpio) ==
+		priv->ah->rfkill_polarity;
+}
+
+static void ath9k_htc_rfkill_poll_state(struct ieee80211_hw *hw)
+{
+	struct ath9k_htc_priv *priv = hw->priv;
+	bool blocked = !!ath_is_rfkill_set(priv);
+
+	wiphy_rfkill_set_hw_state(hw->wiphy, blocked);
+}
+
+void ath9k_start_rfkill_poll(struct ath9k_htc_priv *priv)
+{
+	if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
+		wiphy_rfkill_start_polling(priv->hw->wiphy);
+}
+
+/**********************/
+/* mac80211 Callbacks */
+/**********************/
+
+static int ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
+{
+	struct ieee80211_hdr *hdr;
+	struct ath9k_htc_priv *priv = hw->priv;
+	int padpos, padsize;
+
+	hdr = (struct ieee80211_hdr *) skb->data;
+
+	/* Add the padding after the header if this is not already done */
+	padpos = ath9k_cmn_padpos(hdr->frame_control);
+	padsize = padpos & 3;
+	if (padsize && skb->len > padpos) {
+		if (skb_headroom(skb) < padsize)
+			return -1;
+		skb_push(skb, padsize);
+		memmove(skb->data, skb->data + padsize, padpos);
+	}
+
+	if (ath9k_htc_tx_start(priv, skb) != 0) {
+		ath_print(ath9k_hw_common(priv->ah), ATH_DBG_XMIT, "Tx failed");
+		goto fail_tx;
+	}
+
+	return 0;
+
+fail_tx:
+	dev_kfree_skb_any(skb);
+	return 0;
+}
+
+static int ath9k_htc_start(struct ieee80211_hw *hw)
+{
+	struct ath9k_htc_priv *priv = hw->priv;
+	struct ath_hw *ah = priv->ah;
+	struct ath_common *common = ath9k_hw_common(ah);
+	struct ieee80211_channel *curchan = hw->conf.channel;
+	struct ath9k_channel *init_channel;
+	int ret = 0;
+	enum htc_phymode mode;
+	u16 htc_mode;
+	u8 cmd_rsp;
+
+	ath_print(common, ATH_DBG_CONFIG,
+		  "Starting driver with initial channel: %d MHz\n",
+		  curchan->center_freq);
+
+	mutex_lock(&priv->mutex);
+
+	/* setup initial channel */
+	init_channel = ath9k_cmn_get_curchannel(hw, ah);
+
+	/* Reset SERDES registers */
+	ath9k_hw_configpcipowersave(ah, 0, 0);
+
+	ath9k_hw_htc_resetinit(ah);
+	ret = ath9k_hw_reset(ah, init_channel, false);
+	if (ret) {
+		ath_print(common, ATH_DBG_FATAL,
+			  "Unable to reset hardware; reset status %d "
+			  "(freq %u MHz)\n", ret, curchan->center_freq);
+		goto mutex_unlock;
+	}
+
+	ath_update_txpow(priv);
+
+	mode = ath9k_htc_get_curmode(priv, init_channel);
+	htc_mode = cpu_to_be16(mode);
+	WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
+	if (ret)
+		goto mutex_unlock;
+
+	WMI_CMD(WMI_ATH_INIT_CMDID);
+	if (ret)
+		goto mutex_unlock;
+
+	WMI_CMD(WMI_START_RECV_CMDID);
+	if (ret)
+		goto mutex_unlock;
+
+	ath9k_host_rx_init(priv);
+
+	priv->op_flags &= ~OP_INVALID;
+	htc_start(priv->htc);
+
+mutex_unlock:
+	mutex_unlock(&priv->mutex);
+	return ret;
+}
+
+static void ath9k_htc_stop(struct ieee80211_hw *hw)
+{
+	struct ath9k_htc_priv *priv = hw->priv;
+	struct ath_hw *ah = priv->ah;
+	struct ath_common *common = ath9k_hw_common(ah);
+	int ret = 0;
+	u8 cmd_rsp;
+
+	mutex_lock(&priv->mutex);
+
+	if (priv->op_flags & OP_INVALID) {
+		ath_print(common, ATH_DBG_ANY, "Device not present\n");
+		mutex_unlock(&priv->mutex);
+		return;
+	}
+
+	htc_stop(priv->htc);
+	WMI_CMD(WMI_DISABLE_INTR_CMDID);
+	WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
+	WMI_CMD(WMI_STOP_RECV_CMDID);
+	ath9k_hw_phy_disable(ah);
+	ath9k_hw_disable(ah);
+	ath9k_hw_configpcipowersave(ah, 1, 1);
+	ath9k_hw_setpower(ah, ATH9K_PM_FULL_SLEEP);
+
+	cancel_delayed_work_sync(&priv->ath9k_ani_work);
+	cancel_delayed_work_sync(&priv->ath9k_aggr_work);
+	cancel_delayed_work_sync(&priv->ath9k_led_blink_work);
+	ath9k_led_stop_brightness(priv);
+	skb_queue_purge(&priv->tx_queue);
+
+	/* Remove monitor interface here */
+	if (ah->opmode == NL80211_IFTYPE_MONITOR) {
+		if (ath9k_htc_remove_monitor_interface(priv))
+			ath_print(common, ATH_DBG_FATAL,
+				  "Unable to remove monitor interface\n");
+		else
+			ath_print(common, ATH_DBG_CONFIG,
+				  "Monitor interface removed\n");
+	}
+
+	priv->op_flags |= OP_INVALID;
+	mutex_unlock(&priv->mutex);
+
+	ath_print(common, ATH_DBG_CONFIG, "Driver halt\n");
+}
+
+static int ath9k_htc_add_interface(struct ieee80211_hw *hw,
+				   struct ieee80211_vif *vif)
+{
+	struct ath9k_htc_priv *priv = hw->priv;
+	struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
+	struct ath_common *common = ath9k_hw_common(priv->ah);
+	struct ath9k_htc_target_vif hvif;
+	int ret = 0;
+	u8 cmd_rsp;
+
+	mutex_lock(&priv->mutex);
+
+	/* Only one interface for now */
+	if (priv->nvifs > 0) {
+		ret = -ENOBUFS;
+		goto out;
+	}
+
+	memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
+	memcpy(&hvif.myaddr, vif->addr, ETH_ALEN);
+
+	switch (vif->type) {
+	case NL80211_IFTYPE_STATION:
+		hvif.opmode = cpu_to_be32(HTC_M_STA);
+		break;
+	case NL80211_IFTYPE_ADHOC:
+		hvif.opmode = cpu_to_be32(HTC_M_IBSS);
+		break;
+	default:
+		ath_print(common, ATH_DBG_FATAL,
+			"Interface type %d not yet supported\n", vif->type);
+		ret = -EOPNOTSUPP;
+		goto out;
+	}
+
+	ath_print(common, ATH_DBG_CONFIG,
+		  "Attach a VIF of type: %d\n", vif->type);
+
+	priv->ah->opmode = vif->type;
+
+	/* Index starts from zero on the target */
+	avp->index = hvif.index = priv->nvifs;
+	hvif.rtsthreshold = cpu_to_be16(2304);
+	WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif);
+	if (ret)
+		goto out;
+
+	priv->nvifs++;
+
+	/*
+	 * We need a node in target to tx mgmt frames
+	 * before association.
+	 */
+	ret = ath9k_htc_add_station(priv, vif, NULL);
+	if (ret)
+		goto out;
+
+	ret = ath9k_htc_update_cap_target(priv);
+	if (ret)
+		ath_print(common, ATH_DBG_CONFIG, "Failed to update"
+			  " capability in target \n");
+
+	priv->vif = vif;
+out:
+	mutex_unlock(&priv->mutex);
+	return ret;
+}
+
+static void ath9k_htc_remove_interface(struct ieee80211_hw *hw,
+				       struct ieee80211_vif *vif)
+{
+	struct ath9k_htc_priv *priv = hw->priv;
+	struct ath_common *common = ath9k_hw_common(priv->ah);
+	struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
+	struct ath9k_htc_target_vif hvif;
+	int ret = 0;
+	u8 cmd_rsp;
+
+	ath_print(common, ATH_DBG_CONFIG, "Detach Interface\n");
+
+	mutex_lock(&priv->mutex);
+
+	memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
+	memcpy(&hvif.myaddr, vif->addr, ETH_ALEN);
+	hvif.index = avp->index;
+	WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
+	priv->nvifs--;
+
+	ath9k_htc_remove_station(priv, vif, NULL);
+
+	if (vif->type == NL80211_IFTYPE_ADHOC) {
+		spin_lock_bh(&priv->beacon_lock);
+		if (priv->beacon)
+			dev_kfree_skb_any(priv->beacon);
+		priv->beacon = NULL;
+		spin_unlock_bh(&priv->beacon_lock);
+	}
+
+	priv->vif = NULL;
+
+	mutex_unlock(&priv->mutex);
+}
+
+static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed)
+{
+	struct ath9k_htc_priv *priv = hw->priv;
+	struct ath_common *common = ath9k_hw_common(priv->ah);
+	struct ieee80211_conf *conf = &hw->conf;
+
+	mutex_lock(&priv->mutex);
+
+	if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
+		struct ieee80211_channel *curchan = hw->conf.channel;
+		int pos = curchan->hw_value;
+		bool is_cw40 = false;
+
+		ath_print(common, ATH_DBG_CONFIG, "Set channel: %d MHz\n",
+			  curchan->center_freq);
+
+		if (check_rc_update(hw, &is_cw40))
+			ath9k_htc_rc_update(priv, is_cw40);
+
+		ath9k_cmn_update_ichannel(hw, &priv->ah->channels[pos]);
+
+		if (ath9k_htc_set_channel(priv, hw, &priv->ah->channels[pos]) < 0) {
+			ath_print(common, ATH_DBG_FATAL,
+				  "Unable to set channel\n");
+			mutex_unlock(&priv->mutex);
+			return -EINVAL;
+		}
+
+	}
+
+	if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
+		if (conf->flags & IEEE80211_CONF_MONITOR) {
+			if (ath9k_htc_add_monitor_interface(priv))
+				ath_print(common, ATH_DBG_FATAL,
+					  "Failed to set monitor mode\n");
+			else
+				ath_print(common, ATH_DBG_CONFIG,
+					  "HW opmode set to Monitor mode\n");
+		}
+	}
+
+	mutex_unlock(&priv->mutex);
+
+	return 0;
+}
+
+#define SUPPORTED_FILTERS			\
+	(FIF_PROMISC_IN_BSS |			\
+	FIF_ALLMULTI |				\
+	FIF_CONTROL |				\
+	FIF_PSPOLL |				\
+	FIF_OTHER_BSS |				\
+	FIF_BCN_PRBRESP_PROMISC |		\
+	FIF_FCSFAIL)
+
+static void ath9k_htc_configure_filter(struct ieee80211_hw *hw,
+				       unsigned int changed_flags,
+				       unsigned int *total_flags,
+				       u64 multicast)
+{
+	struct ath9k_htc_priv *priv = hw->priv;
+	u32 rfilt;
+
+	mutex_lock(&priv->mutex);
+
+	changed_flags &= SUPPORTED_FILTERS;
+	*total_flags &= SUPPORTED_FILTERS;
+
+	priv->rxfilter = *total_flags;
+	rfilt = ath9k_cmn_calcrxfilter(hw, priv->ah, priv->rxfilter);
+	ath9k_hw_setrxfilter(priv->ah, rfilt);
+
+	ath_print(ath9k_hw_common(priv->ah), ATH_DBG_CONFIG,
+		  "Set HW RX filter: 0x%x\n", rfilt);
+
+	mutex_unlock(&priv->mutex);
+}
+
+static void ath9k_htc_sta_notify(struct ieee80211_hw *hw,
+				 struct ieee80211_vif *vif,
+				 enum sta_notify_cmd cmd,
+				 struct ieee80211_sta *sta)
+{
+	struct ath9k_htc_priv *priv = hw->priv;
+	int ret;
+
+	switch (cmd) {
+	case STA_NOTIFY_ADD:
+		ret = ath9k_htc_add_station(priv, vif, sta);
+		if (!ret)
+			ath9k_htc_init_rate(priv, vif, sta);
+		break;
+	case STA_NOTIFY_REMOVE:
+		ath9k_htc_remove_station(priv, vif, sta);
+		break;
+	default:
+		break;
+	}
+}
+
+static int ath9k_htc_conf_tx(struct ieee80211_hw *hw, u16 queue,
+			     const struct ieee80211_tx_queue_params *params)
+{
+	struct ath9k_htc_priv *priv = hw->priv;
+	struct ath_common *common = ath9k_hw_common(priv->ah);
+	struct ath9k_tx_queue_info qi;
+	int ret = 0, qnum;
+
+	if (queue >= WME_NUM_AC)
+		return 0;
+
+	mutex_lock(&priv->mutex);
+
+	memset(&qi, 0, sizeof(struct ath9k_tx_queue_info));
+
+	qi.tqi_aifs = params->aifs;
+	qi.tqi_cwmin = params->cw_min;
+	qi.tqi_cwmax = params->cw_max;
+	qi.tqi_burstTime = params->txop;
+
+	qnum = get_hw_qnum(queue, priv->hwq_map);
+
+	ath_print(common, ATH_DBG_CONFIG,
+		  "Configure tx [queue/hwq] [%d/%d],  "
+		  "aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n",
+		  queue, qnum, params->aifs, params->cw_min,
+		  params->cw_max, params->txop);
+
+	ret = ath_htc_txq_update(priv, qnum, &qi);
+	if (ret)
+		ath_print(common, ATH_DBG_FATAL, "TXQ Update failed\n");
+
+	mutex_unlock(&priv->mutex);
+
+	return ret;
+}
+
+static int ath9k_htc_set_key(struct ieee80211_hw *hw,
+			     enum set_key_cmd cmd,
+			     struct ieee80211_vif *vif,
+			     struct ieee80211_sta *sta,
+			     struct ieee80211_key_conf *key)
+{
+	struct ath9k_htc_priv *priv = hw->priv;
+	struct ath_common *common = ath9k_hw_common(priv->ah);
+	int ret = 0;
+
+	if (htc_modparam_nohwcrypt)
+		return -ENOSPC;
+
+	mutex_lock(&priv->mutex);
+	ath_print(common, ATH_DBG_CONFIG, "Set HW Key\n");
+
+	switch (cmd) {
+	case SET_KEY:
+		ret = ath9k_cmn_key_config(common, vif, sta, key);
+		if (ret >= 0) {
+			key->hw_key_idx = ret;
+			/* push IV and Michael MIC generation to stack */
+			key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
+			if (key->alg == ALG_TKIP)
+				key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
+			if (priv->ah->sw_mgmt_crypto && key->alg == ALG_CCMP)
+				key->flags |= IEEE80211_KEY_FLAG_SW_MGMT;
+			ret = 0;
+		}
+		break;
+	case DISABLE_KEY:
+		ath9k_cmn_key_delete(common, key);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	mutex_unlock(&priv->mutex);
+
+	return ret;
+}
+
+static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw,
+				       struct ieee80211_vif *vif,
+				       struct ieee80211_bss_conf *bss_conf,
+				       u32 changed)
+{
+	struct ath9k_htc_priv *priv = hw->priv;
+	struct ath_hw *ah = priv->ah;
+	struct ath_common *common = ath9k_hw_common(ah);
+
+	mutex_lock(&priv->mutex);
+
+	if (changed & BSS_CHANGED_ASSOC) {
+		common->curaid = bss_conf->assoc ?
+				 bss_conf->aid : 0;
+		ath_print(common, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n",
+			bss_conf->assoc);
+
+		if (bss_conf->assoc) {
+			priv->op_flags |= OP_ASSOCIATED;
+			ath_start_ani(priv);
+		} else {
+			priv->op_flags &= ~OP_ASSOCIATED;
+			cancel_delayed_work_sync(&priv->ath9k_ani_work);
+		}
+	}
+
+	if (changed & BSS_CHANGED_BSSID) {
+		/* Set BSSID */
+		memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
+		ath9k_hw_write_associd(ah);
+
+		ath_print(common, ATH_DBG_CONFIG,
+			  "BSSID: %pM aid: 0x%x\n",
+			  common->curbssid, common->curaid);
+	}
+
+	if ((changed & BSS_CHANGED_BEACON_INT) ||
+	    (changed & BSS_CHANGED_BEACON) ||
+	    ((changed & BSS_CHANGED_BEACON_ENABLED) &&
+	    bss_conf->enable_beacon)) {
+		priv->op_flags |= OP_ENABLE_BEACON;
+		ath9k_htc_beacon_config(priv, vif, bss_conf);
+	}
+
+	if (changed & BSS_CHANGED_BEACON)
+		ath9k_htc_beacon_update(priv, vif);
+
+	if ((changed & BSS_CHANGED_BEACON_ENABLED) &&
+	    !bss_conf->enable_beacon) {
+		priv->op_flags &= ~OP_ENABLE_BEACON;
+		ath9k_htc_beacon_config(priv, vif, bss_conf);
+	}
+
+	if (changed & BSS_CHANGED_ERP_PREAMBLE) {
+		ath_print(common, ATH_DBG_CONFIG, "BSS Changed PREAMBLE %d\n",
+			  bss_conf->use_short_preamble);
+		if (bss_conf->use_short_preamble)
+			priv->op_flags |= OP_PREAMBLE_SHORT;
+		else
+			priv->op_flags &= ~OP_PREAMBLE_SHORT;
+	}
+
+	if (changed & BSS_CHANGED_ERP_CTS_PROT) {
+		ath_print(common, ATH_DBG_CONFIG, "BSS Changed CTS PROT %d\n",
+			  bss_conf->use_cts_prot);
+		if (bss_conf->use_cts_prot &&
+		    hw->conf.channel->band != IEEE80211_BAND_5GHZ)
+			priv->op_flags |= OP_PROTECT_ENABLE;
+		else
+			priv->op_flags &= ~OP_PROTECT_ENABLE;
+	}
+
+	if (changed & BSS_CHANGED_ERP_SLOT) {
+		if (bss_conf->use_short_slot)
+			ah->slottime = 9;
+		else
+			ah->slottime = 20;
+
+		ath9k_hw_init_global_settings(ah);
+	}
+
+	mutex_unlock(&priv->mutex);
+}
+
+static u64 ath9k_htc_get_tsf(struct ieee80211_hw *hw)
+{
+	struct ath9k_htc_priv *priv = hw->priv;
+	u64 tsf;
+
+	mutex_lock(&priv->mutex);
+	tsf = ath9k_hw_gettsf64(priv->ah);
+	mutex_unlock(&priv->mutex);
+
+	return tsf;
+}
+
+static void ath9k_htc_set_tsf(struct ieee80211_hw *hw, u64 tsf)
+{
+	struct ath9k_htc_priv *priv = hw->priv;
+
+	mutex_lock(&priv->mutex);
+	ath9k_hw_settsf64(priv->ah, tsf);
+	mutex_unlock(&priv->mutex);
+}
+
+static void ath9k_htc_reset_tsf(struct ieee80211_hw *hw)
+{
+	struct ath9k_htc_priv *priv = hw->priv;
+
+	mutex_lock(&priv->mutex);
+	ath9k_hw_reset_tsf(priv->ah);
+	mutex_unlock(&priv->mutex);
+}
+
+static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw,
+				  struct ieee80211_vif *vif,
+				  enum ieee80211_ampdu_mlme_action action,
+				  struct ieee80211_sta *sta,
+				  u16 tid, u16 *ssn)
+{
+	struct ath9k_htc_priv *priv = hw->priv;
+	struct ath9k_htc_aggr_work *work = &priv->aggr_work;
+	struct ath9k_htc_sta *ista;
+
+	switch (action) {
+	case IEEE80211_AMPDU_RX_START:
+		break;
+	case IEEE80211_AMPDU_RX_STOP:
+		break;
+	case IEEE80211_AMPDU_TX_START:
+	case IEEE80211_AMPDU_TX_STOP:
+		if (!(priv->op_flags & OP_TXAGGR))
+			return -ENOTSUPP;
+		memcpy(work->sta_addr, sta->addr, ETH_ALEN);
+		work->hw = hw;
+		work->vif = vif;
+		work->action = action;
+		work->tid = tid;
+		ieee80211_queue_delayed_work(hw, &priv->ath9k_aggr_work, 0);
+		break;
+	case IEEE80211_AMPDU_TX_OPERATIONAL:
+		ista = (struct ath9k_htc_sta *) sta->drv_priv;
+		ista->tid_state[tid] = AGGR_OPERATIONAL;
+		break;
+	default:
+		ath_print(ath9k_hw_common(priv->ah), ATH_DBG_FATAL,
+			  "Unknown AMPDU action\n");
+	}
+
+	return 0;
+}
+
+static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw)
+{
+	struct ath9k_htc_priv *priv = hw->priv;
+
+	mutex_lock(&priv->mutex);
+	spin_lock_bh(&priv->beacon_lock);
+	priv->op_flags |= OP_SCANNING;
+	spin_unlock_bh(&priv->beacon_lock);
+	cancel_delayed_work_sync(&priv->ath9k_ani_work);
+	mutex_unlock(&priv->mutex);
+}
+
+static void ath9k_htc_sw_scan_complete(struct ieee80211_hw *hw)
+{
+	struct ath9k_htc_priv *priv = hw->priv;
+
+	mutex_lock(&priv->mutex);
+	spin_lock_bh(&priv->beacon_lock);
+	priv->op_flags &= ~OP_SCANNING;
+	spin_unlock_bh(&priv->beacon_lock);
+	priv->op_flags |= OP_FULL_RESET;
+	ath_start_ani(priv);
+	mutex_unlock(&priv->mutex);
+}
+
+static int ath9k_htc_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+	return 0;
+}
+
+static void ath9k_htc_set_coverage_class(struct ieee80211_hw *hw,
+					 u8 coverage_class)
+{
+	struct ath9k_htc_priv *priv = hw->priv;
+
+	mutex_lock(&priv->mutex);
+	priv->ah->coverage_class = coverage_class;
+	ath9k_hw_init_global_settings(priv->ah);
+	mutex_unlock(&priv->mutex);
+}
+
+struct ieee80211_ops ath9k_htc_ops = {
+	.tx                 = ath9k_htc_tx,
+	.start              = ath9k_htc_start,
+	.stop               = ath9k_htc_stop,
+	.add_interface      = ath9k_htc_add_interface,
+	.remove_interface   = ath9k_htc_remove_interface,
+	.config             = ath9k_htc_config,
+	.configure_filter   = ath9k_htc_configure_filter,
+	.sta_notify         = ath9k_htc_sta_notify,
+	.conf_tx            = ath9k_htc_conf_tx,
+	.bss_info_changed   = ath9k_htc_bss_info_changed,
+	.set_key            = ath9k_htc_set_key,
+	.get_tsf            = ath9k_htc_get_tsf,
+	.set_tsf            = ath9k_htc_set_tsf,
+	.reset_tsf          = ath9k_htc_reset_tsf,
+	.ampdu_action       = ath9k_htc_ampdu_action,
+	.sw_scan_start      = ath9k_htc_sw_scan_start,
+	.sw_scan_complete   = ath9k_htc_sw_scan_complete,
+	.set_rts_threshold  = ath9k_htc_set_rts_threshold,
+	.rfkill_poll        = ath9k_htc_rfkill_poll_state,
+	.set_coverage_class = ath9k_htc_set_coverage_class,
+};

+ 604 - 0
drivers/net/wireless/ath/ath9k/htc_drv_txrx.c

@@ -0,0 +1,604 @@
+/*
+ * Copyright (c) 2010 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "htc.h"
+
+/******/
+/* TX */
+/******/
+
+int get_hw_qnum(u16 queue, int *hwq_map)
+{
+	switch (queue) {
+	case 0:
+		return hwq_map[ATH9K_WME_AC_VO];
+	case 1:
+		return hwq_map[ATH9K_WME_AC_VI];
+	case 2:
+		return hwq_map[ATH9K_WME_AC_BE];
+	case 3:
+		return hwq_map[ATH9K_WME_AC_BK];
+	default:
+		return hwq_map[ATH9K_WME_AC_BE];
+	}
+}
+
+int ath_htc_txq_update(struct ath9k_htc_priv *priv, int qnum,
+		       struct ath9k_tx_queue_info *qinfo)
+{
+	struct ath_hw *ah = priv->ah;
+	int error = 0;
+	struct ath9k_tx_queue_info qi;
+
+	ath9k_hw_get_txq_props(ah, qnum, &qi);
+
+	qi.tqi_aifs = qinfo->tqi_aifs;
+	qi.tqi_cwmin = qinfo->tqi_cwmin / 2; /* XXX */
+	qi.tqi_cwmax = qinfo->tqi_cwmax;
+	qi.tqi_burstTime = qinfo->tqi_burstTime;
+	qi.tqi_readyTime = qinfo->tqi_readyTime;
+
+	if (!ath9k_hw_set_txq_props(ah, qnum, &qi)) {
+		ath_print(ath9k_hw_common(ah), ATH_DBG_FATAL,
+			  "Unable to update hardware queue %u!\n", qnum);
+		error = -EIO;
+	} else {
+		ath9k_hw_resettxqueue(ah, qnum);
+	}
+
+	return error;
+}
+
+int ath9k_htc_tx_start(struct ath9k_htc_priv *priv, struct sk_buff *skb)
+{
+	struct ieee80211_hdr *hdr;
+	struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
+	struct ieee80211_sta *sta = tx_info->control.sta;
+	struct ath9k_htc_sta *ista;
+	struct ath9k_htc_vif *avp;
+	struct ath9k_htc_tx_ctl tx_ctl;
+	enum htc_endpoint_id epid;
+	u16 qnum, hw_qnum;
+	__le16 fc;
+	u8 *tx_fhdr;
+	u8 sta_idx;
+
+	hdr = (struct ieee80211_hdr *) skb->data;
+	fc = hdr->frame_control;
+
+	avp = (struct ath9k_htc_vif *) tx_info->control.vif->drv_priv;
+	if (sta) {
+		ista = (struct ath9k_htc_sta *) sta->drv_priv;
+		sta_idx = ista->index;
+	} else {
+		sta_idx = 0;
+	}
+
+	memset(&tx_ctl, 0, sizeof(struct ath9k_htc_tx_ctl));
+
+	if (ieee80211_is_data(fc)) {
+		struct tx_frame_hdr tx_hdr;
+		u8 *qc;
+
+		memset(&tx_hdr, 0, sizeof(struct tx_frame_hdr));
+
+		tx_hdr.node_idx = sta_idx;
+		tx_hdr.vif_idx = avp->index;
+
+		if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) {
+			tx_ctl.type = ATH9K_HTC_AMPDU;
+			tx_hdr.data_type = ATH9K_HTC_AMPDU;
+		} else {
+			tx_ctl.type = ATH9K_HTC_NORMAL;
+			tx_hdr.data_type = ATH9K_HTC_NORMAL;
+		}
+
+		if (ieee80211_is_data(fc)) {
+			qc = ieee80211_get_qos_ctl(hdr);
+			tx_hdr.tidno = qc[0] & IEEE80211_QOS_CTL_TID_MASK;
+		}
+
+		/* Check for RTS protection */
+		if (priv->hw->wiphy->rts_threshold != (u32) -1)
+			if (skb->len > priv->hw->wiphy->rts_threshold)
+				tx_hdr.flags |= ATH9K_HTC_TX_RTSCTS;
+
+		/* CTS-to-self */
+		if (!(tx_hdr.flags & ATH9K_HTC_TX_RTSCTS) &&
+		    (priv->op_flags & OP_PROTECT_ENABLE))
+			tx_hdr.flags |= ATH9K_HTC_TX_CTSONLY;
+
+		tx_hdr.key_type = ath9k_cmn_get_hw_crypto_keytype(skb);
+		if (tx_hdr.key_type == ATH9K_KEY_TYPE_CLEAR)
+			tx_hdr.keyix = (u8) ATH9K_TXKEYIX_INVALID;
+		else
+			tx_hdr.keyix = tx_info->control.hw_key->hw_key_idx;
+
+		tx_fhdr = skb_push(skb, sizeof(tx_hdr));
+		memcpy(tx_fhdr, (u8 *) &tx_hdr, sizeof(tx_hdr));
+
+		qnum = skb_get_queue_mapping(skb);
+		hw_qnum = get_hw_qnum(qnum, priv->hwq_map);
+
+		switch (hw_qnum) {
+		case 0:
+			epid = priv->data_be_ep;
+			break;
+		case 2:
+			epid = priv->data_vi_ep;
+			break;
+		case 3:
+			epid = priv->data_vo_ep;
+			break;
+		case 1:
+		default:
+			epid = priv->data_bk_ep;
+			break;
+		}
+	} else {
+		struct tx_mgmt_hdr mgmt_hdr;
+
+		memset(&mgmt_hdr, 0, sizeof(struct tx_mgmt_hdr));
+
+		tx_ctl.type = ATH9K_HTC_NORMAL;
+
+		mgmt_hdr.node_idx = sta_idx;
+		mgmt_hdr.vif_idx = avp->index;
+		mgmt_hdr.tidno = 0;
+		mgmt_hdr.flags = 0;
+
+		mgmt_hdr.key_type = ath9k_cmn_get_hw_crypto_keytype(skb);
+		if (mgmt_hdr.key_type == ATH9K_KEY_TYPE_CLEAR)
+			mgmt_hdr.keyix = (u8) ATH9K_TXKEYIX_INVALID;
+		else
+			mgmt_hdr.keyix = tx_info->control.hw_key->hw_key_idx;
+
+		tx_fhdr = skb_push(skb, sizeof(mgmt_hdr));
+		memcpy(tx_fhdr, (u8 *) &mgmt_hdr, sizeof(mgmt_hdr));
+		epid = priv->mgmt_ep;
+	}
+
+	return htc_send(priv->htc, skb, epid, &tx_ctl);
+}
+
+void ath9k_tx_tasklet(unsigned long data)
+{
+	struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *)data;
+	struct ieee80211_sta *sta;
+	struct ieee80211_hdr *hdr;
+	struct ieee80211_tx_info *tx_info;
+	struct sk_buff *skb = NULL;
+	__le16 fc;
+
+	while ((skb = skb_dequeue(&priv->tx_queue)) != NULL) {
+
+		hdr = (struct ieee80211_hdr *) skb->data;
+		fc = hdr->frame_control;
+		tx_info = IEEE80211_SKB_CB(skb);
+		sta = tx_info->control.sta;
+
+		rcu_read_lock();
+
+		if (sta && conf_is_ht(&priv->hw->conf) &&
+		    (priv->op_flags & OP_TXAGGR)
+		    && !(skb->protocol == cpu_to_be16(ETH_P_PAE))) {
+			if (ieee80211_is_data_qos(fc)) {
+				u8 *qc, tid;
+				struct ath9k_htc_sta *ista;
+
+				qc = ieee80211_get_qos_ctl(hdr);
+				tid = qc[0] & 0xf;
+				ista = (struct ath9k_htc_sta *)sta->drv_priv;
+
+				if ((tid < ATH9K_HTC_MAX_TID) &&
+				    ista->tid_state[tid] == AGGR_STOP) {
+					ieee80211_start_tx_ba_session(sta, tid);
+					ista->tid_state[tid] = AGGR_PROGRESS;
+				}
+			}
+		}
+
+		rcu_read_unlock();
+
+		memset(&tx_info->status, 0, sizeof(tx_info->status));
+		ieee80211_tx_status(priv->hw, skb);
+	}
+}
+
+void ath9k_htc_txep(void *drv_priv, struct sk_buff *skb,
+		    enum htc_endpoint_id ep_id, bool txok)
+{
+	struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) drv_priv;
+	struct ieee80211_tx_info *tx_info;
+
+	if (!skb)
+		return;
+
+	if (ep_id == priv->mgmt_ep)
+		skb_pull(skb, sizeof(struct tx_mgmt_hdr));
+	else
+		/* TODO: Check for cab/uapsd/data */
+		skb_pull(skb, sizeof(struct tx_frame_hdr));
+
+	tx_info = IEEE80211_SKB_CB(skb);
+
+	if (txok)
+		tx_info->flags |= IEEE80211_TX_STAT_ACK;
+
+	skb_queue_tail(&priv->tx_queue, skb);
+	tasklet_schedule(&priv->tx_tasklet);
+}
+
+int ath9k_tx_init(struct ath9k_htc_priv *priv)
+{
+	skb_queue_head_init(&priv->tx_queue);
+	return 0;
+}
+
+void ath9k_tx_cleanup(struct ath9k_htc_priv *priv)
+{
+
+}
+
+bool ath9k_htc_txq_setup(struct ath9k_htc_priv *priv,
+			 enum ath9k_tx_queue_subtype subtype)
+{
+	struct ath_hw *ah = priv->ah;
+	struct ath_common *common = ath9k_hw_common(ah);
+	struct ath9k_tx_queue_info qi;
+	int qnum;
+
+	memset(&qi, 0, sizeof(qi));
+
+	qi.tqi_subtype = subtype;
+	qi.tqi_aifs = ATH9K_TXQ_USEDEFAULT;
+	qi.tqi_cwmin = ATH9K_TXQ_USEDEFAULT;
+	qi.tqi_cwmax = ATH9K_TXQ_USEDEFAULT;
+	qi.tqi_physCompBuf = 0;
+	qi.tqi_qflags = TXQ_FLAG_TXEOLINT_ENABLE | TXQ_FLAG_TXDESCINT_ENABLE;
+
+	qnum = ath9k_hw_setuptxqueue(priv->ah, ATH9K_TX_QUEUE_DATA, &qi);
+	if (qnum == -1)
+		return false;
+
+	if (qnum >= ARRAY_SIZE(priv->hwq_map)) {
+		ath_print(common, ATH_DBG_FATAL,
+			  "qnum %u out of range, max %u!\n",
+			  qnum, (unsigned int)ARRAY_SIZE(priv->hwq_map));
+		ath9k_hw_releasetxqueue(ah, qnum);
+		return false;
+	}
+
+	priv->hwq_map[subtype] = qnum;
+	return true;
+}
+
+/******/
+/* RX */
+/******/
+
+void ath9k_host_rx_init(struct ath9k_htc_priv *priv)
+{
+	ath9k_hw_rxena(priv->ah);
+	ath9k_cmn_opmode_init(priv->hw, priv->ah, priv->rxfilter);
+	ath9k_hw_startpcureceive(priv->ah);
+	priv->rx.last_rssi = ATH_RSSI_DUMMY_MARKER;
+}
+
+static void ath9k_process_rate(struct ieee80211_hw *hw,
+			       struct ieee80211_rx_status *rxs,
+			       u8 rx_rate, u8 rs_flags)
+{
+	struct ieee80211_supported_band *sband;
+	enum ieee80211_band band;
+	unsigned int i = 0;
+
+	if (rx_rate & 0x80) {
+		/* HT rate */
+		rxs->flag |= RX_FLAG_HT;
+		if (rs_flags & ATH9K_RX_2040)
+			rxs->flag |= RX_FLAG_40MHZ;
+		if (rs_flags & ATH9K_RX_GI)
+			rxs->flag |= RX_FLAG_SHORT_GI;
+		rxs->rate_idx = rx_rate & 0x7f;
+		return;
+	}
+
+	band = hw->conf.channel->band;
+	sband = hw->wiphy->bands[band];
+
+	for (i = 0; i < sband->n_bitrates; i++) {
+		if (sband->bitrates[i].hw_value == rx_rate) {
+			rxs->rate_idx = i;
+			return;
+		}
+		if (sband->bitrates[i].hw_value_short == rx_rate) {
+			rxs->rate_idx = i;
+			rxs->flag |= RX_FLAG_SHORTPRE;
+			return;
+		}
+	}
+
+}
+
+static bool ath9k_rx_prepare(struct ath9k_htc_priv *priv,
+			     struct ath9k_htc_rxbuf *rxbuf,
+			     struct ieee80211_rx_status *rx_status)
+
+{
+	struct ieee80211_hdr *hdr;
+	struct ieee80211_hw *hw = priv->hw;
+	struct sk_buff *skb = rxbuf->skb;
+	struct ath_common *common = ath9k_hw_common(priv->ah);
+	int hdrlen, padpos, padsize;
+	int last_rssi = ATH_RSSI_DUMMY_MARKER;
+	__le16 fc;
+
+	hdr = (struct ieee80211_hdr *)skb->data;
+	fc = hdr->frame_control;
+	hdrlen = ieee80211_get_hdrlen_from_skb(skb);
+
+	padpos = ath9k_cmn_padpos(fc);
+
+	padsize = padpos & 3;
+	if (padsize && skb->len >= padpos+padsize) {
+		memmove(skb->data + padsize, skb->data, padpos);
+		skb_pull(skb, padsize);
+	}
+
+	memset(rx_status, 0, sizeof(struct ieee80211_rx_status));
+
+	if (rxbuf->rxstatus.rs_status != 0) {
+		if (rxbuf->rxstatus.rs_status & ATH9K_RXERR_CRC)
+			rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
+		if (rxbuf->rxstatus.rs_status & ATH9K_RXERR_PHY)
+			goto rx_next;
+
+		if (rxbuf->rxstatus.rs_status & ATH9K_RXERR_DECRYPT) {
+			/* FIXME */
+		} else if (rxbuf->rxstatus.rs_status & ATH9K_RXERR_MIC) {
+			if (ieee80211_is_ctl(fc))
+				/*
+				 * Sometimes, we get invalid
+				 * MIC failures on valid control frames.
+				 * Remove these mic errors.
+				 */
+				rxbuf->rxstatus.rs_status &= ~ATH9K_RXERR_MIC;
+			else
+				rx_status->flag |= RX_FLAG_MMIC_ERROR;
+		}
+
+		/*
+		 * Reject error frames with the exception of
+		 * decryption and MIC failures. For monitor mode,
+		 * we also ignore the CRC error.
+		 */
+		if (priv->ah->opmode == NL80211_IFTYPE_MONITOR) {
+			if (rxbuf->rxstatus.rs_status &
+			    ~(ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC |
+			      ATH9K_RXERR_CRC))
+				goto rx_next;
+		} else {
+			if (rxbuf->rxstatus.rs_status &
+			    ~(ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC)) {
+				goto rx_next;
+			}
+		}
+	}
+
+	if (!(rxbuf->rxstatus.rs_status & ATH9K_RXERR_DECRYPT)) {
+		u8 keyix;
+		keyix = rxbuf->rxstatus.rs_keyix;
+		if (keyix != ATH9K_RXKEYIX_INVALID) {
+			rx_status->flag |= RX_FLAG_DECRYPTED;
+		} else if (ieee80211_has_protected(fc) &&
+			   skb->len >= hdrlen + 4) {
+			keyix = skb->data[hdrlen + 3] >> 6;
+			if (test_bit(keyix, common->keymap))
+				rx_status->flag |= RX_FLAG_DECRYPTED;
+		}
+	}
+
+	ath9k_process_rate(hw, rx_status, rxbuf->rxstatus.rs_rate,
+			   rxbuf->rxstatus.rs_flags);
+
+	if (priv->op_flags & OP_ASSOCIATED) {
+		if (rxbuf->rxstatus.rs_rssi != ATH9K_RSSI_BAD &&
+		    !rxbuf->rxstatus.rs_moreaggr)
+			ATH_RSSI_LPF(priv->rx.last_rssi,
+				     rxbuf->rxstatus.rs_rssi);
+
+		last_rssi = priv->rx.last_rssi;
+
+		if (likely(last_rssi != ATH_RSSI_DUMMY_MARKER))
+			rxbuf->rxstatus.rs_rssi = ATH_EP_RND(last_rssi,
+							     ATH_RSSI_EP_MULTIPLIER);
+
+		if (rxbuf->rxstatus.rs_rssi < 0)
+			rxbuf->rxstatus.rs_rssi = 0;
+
+		if (ieee80211_is_beacon(fc))
+			priv->ah->stats.avgbrssi = rxbuf->rxstatus.rs_rssi;
+	}
+
+	rx_status->mactime = rxbuf->rxstatus.rs_tstamp;
+	rx_status->band = hw->conf.channel->band;
+	rx_status->freq = hw->conf.channel->center_freq;
+	rx_status->signal =  rxbuf->rxstatus.rs_rssi + ATH_DEFAULT_NOISE_FLOOR;
+	rx_status->antenna = rxbuf->rxstatus.rs_antenna;
+	rx_status->flag |= RX_FLAG_TSFT;
+
+	return true;
+
+rx_next:
+	return false;
+}
+
+/*
+ * FIXME: Handle FLUSH later on.
+ */
+void ath9k_rx_tasklet(unsigned long data)
+{
+	struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *)data;
+	struct ath9k_htc_rxbuf *rxbuf = NULL, *tmp_buf = NULL;
+	struct ieee80211_rx_status rx_status;
+	struct sk_buff *skb;
+	unsigned long flags;
+
+
+	do {
+		spin_lock_irqsave(&priv->rx.rxbuflock, flags);
+		list_for_each_entry(tmp_buf, &priv->rx.rxbuf, list) {
+			if (tmp_buf->in_process) {
+				rxbuf = tmp_buf;
+				break;
+			}
+		}
+
+		if (rxbuf == NULL) {
+			spin_unlock_irqrestore(&priv->rx.rxbuflock, flags);
+			break;
+		}
+
+		if (!rxbuf->skb)
+			goto requeue;
+
+		if (!ath9k_rx_prepare(priv, rxbuf, &rx_status)) {
+			dev_kfree_skb_any(rxbuf->skb);
+			goto requeue;
+		}
+
+		memcpy(IEEE80211_SKB_RXCB(rxbuf->skb), &rx_status,
+		       sizeof(struct ieee80211_rx_status));
+		skb = rxbuf->skb;
+		spin_unlock_irqrestore(&priv->rx.rxbuflock, flags);
+
+		ieee80211_rx(priv->hw, skb);
+
+		spin_lock_irqsave(&priv->rx.rxbuflock, flags);
+requeue:
+		rxbuf->in_process = false;
+		rxbuf->skb = NULL;
+		list_move_tail(&rxbuf->list, &priv->rx.rxbuf);
+		rxbuf = NULL;
+		spin_unlock_irqrestore(&priv->rx.rxbuflock, flags);
+	} while (1);
+
+}
+
+void ath9k_htc_rxep(void *drv_priv, struct sk_buff *skb,
+		    enum htc_endpoint_id ep_id)
+{
+	struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *)drv_priv;
+	struct ath_hw *ah = priv->ah;
+	struct ath_common *common = ath9k_hw_common(ah);
+	struct ath9k_htc_rxbuf *rxbuf = NULL, *tmp_buf = NULL;
+	struct ath_htc_rx_status *rxstatus;
+	u32 len = 0;
+
+	spin_lock(&priv->rx.rxbuflock);
+	list_for_each_entry(tmp_buf, &priv->rx.rxbuf, list) {
+		if (!tmp_buf->in_process) {
+			rxbuf = tmp_buf;
+			break;
+		}
+	}
+	spin_unlock(&priv->rx.rxbuflock);
+
+	if (rxbuf == NULL) {
+		ath_print(common, ATH_DBG_ANY,
+			  "No free RX buffer\n");
+		goto err;
+	}
+
+	len = skb->len;
+	if (len <= HTC_RX_FRAME_HEADER_SIZE) {
+		ath_print(common, ATH_DBG_FATAL,
+			  "Corrupted RX frame, dropping\n");
+		goto err;
+	}
+
+	rxstatus = (struct ath_htc_rx_status *)skb->data;
+
+	rxstatus->rs_tstamp = be64_to_cpu(rxstatus->rs_tstamp);
+	rxstatus->rs_datalen = be16_to_cpu(rxstatus->rs_datalen);
+	rxstatus->evm0 = be32_to_cpu(rxstatus->evm0);
+	rxstatus->evm1 = be32_to_cpu(rxstatus->evm1);
+	rxstatus->evm2 = be32_to_cpu(rxstatus->evm2);
+
+	if (rxstatus->rs_datalen - (len - HTC_RX_FRAME_HEADER_SIZE) != 0) {
+		ath_print(common, ATH_DBG_FATAL,
+			  "Corrupted RX data len, dropping "
+			  "(epid: %d, dlen: %d, skblen: %d)\n",
+			  ep_id, rxstatus->rs_datalen, len);
+		goto err;
+	}
+
+	spin_lock(&priv->rx.rxbuflock);
+	memcpy(&rxbuf->rxstatus, rxstatus, HTC_RX_FRAME_HEADER_SIZE);
+	skb_pull(skb, HTC_RX_FRAME_HEADER_SIZE);
+	skb->len = rxstatus->rs_datalen;
+	rxbuf->skb = skb;
+	rxbuf->in_process = true;
+	spin_unlock(&priv->rx.rxbuflock);
+
+	tasklet_schedule(&priv->rx_tasklet);
+	return;
+err:
+	dev_kfree_skb_any(skb);
+	return;
+}
+
+/* FIXME: Locking for cleanup/init */
+
+void ath9k_rx_cleanup(struct ath9k_htc_priv *priv)
+{
+	struct ath9k_htc_rxbuf *rxbuf, *tbuf;
+
+	list_for_each_entry_safe(rxbuf, tbuf, &priv->rx.rxbuf, list) {
+		list_del(&rxbuf->list);
+		if (rxbuf->skb)
+			dev_kfree_skb_any(rxbuf->skb);
+		kfree(rxbuf);
+	}
+}
+
+int ath9k_rx_init(struct ath9k_htc_priv *priv)
+{
+	struct ath_hw *ah = priv->ah;
+	struct ath_common *common = ath9k_hw_common(ah);
+	struct ath9k_htc_rxbuf *rxbuf;
+	int i = 0;
+
+	INIT_LIST_HEAD(&priv->rx.rxbuf);
+	spin_lock_init(&priv->rx.rxbuflock);
+
+	for (i = 0; i < ATH9K_HTC_RXBUF; i++) {
+		rxbuf = kzalloc(sizeof(struct ath9k_htc_rxbuf), GFP_KERNEL);
+		if (rxbuf == NULL) {
+			ath_print(common, ATH_DBG_FATAL,
+				  "Unable to allocate RX buffers\n");
+			goto err;
+		}
+		list_add_tail(&rxbuf->list, &priv->rx.rxbuf);
+	}
+
+	return 0;
+
+err:
+	ath9k_rx_cleanup(priv);
+	return -ENOMEM;
+}

+ 463 - 0
drivers/net/wireless/ath/ath9k/htc_hst.c

@@ -0,0 +1,463 @@
+/*
+ * Copyright (c) 2010 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "htc.h"
+
+static int htc_issue_send(struct htc_target *target, struct sk_buff* skb,
+			  u16 len, u8 flags, u8 epid,
+			  struct ath9k_htc_tx_ctl *tx_ctl)
+{
+	struct htc_frame_hdr *hdr;
+	struct htc_endpoint *endpoint = &target->endpoint[epid];
+	int status;
+
+	hdr = (struct htc_frame_hdr *)
+		skb_push(skb, sizeof(struct htc_frame_hdr));
+	hdr->endpoint_id = epid;
+	hdr->flags = flags;
+	hdr->payload_len = cpu_to_be16(len);
+
+	status = target->hif->send(target->hif_dev, endpoint->ul_pipeid, skb,
+				   tx_ctl);
+	return status;
+}
+
+static struct htc_endpoint *get_next_avail_ep(struct htc_endpoint *endpoint)
+{
+	enum htc_endpoint_id avail_epid;
+
+	for (avail_epid = ENDPOINT_MAX; avail_epid > ENDPOINT0; avail_epid--)
+		if (endpoint[avail_epid].service_id == 0)
+			return &endpoint[avail_epid];
+	return NULL;
+}
+
+static u8 service_to_ulpipe(u16 service_id)
+{
+	switch (service_id) {
+	case WMI_CONTROL_SVC:
+		return 4;
+	case WMI_BEACON_SVC:
+	case WMI_CAB_SVC:
+	case WMI_UAPSD_SVC:
+	case WMI_MGMT_SVC:
+	case WMI_DATA_VO_SVC:
+	case WMI_DATA_VI_SVC:
+	case WMI_DATA_BE_SVC:
+	case WMI_DATA_BK_SVC:
+		return 1;
+	default:
+		return 0;
+	}
+}
+
+static u8 service_to_dlpipe(u16 service_id)
+{
+	switch (service_id) {
+	case WMI_CONTROL_SVC:
+		return 3;
+	case WMI_BEACON_SVC:
+	case WMI_CAB_SVC:
+	case WMI_UAPSD_SVC:
+	case WMI_MGMT_SVC:
+	case WMI_DATA_VO_SVC:
+	case WMI_DATA_VI_SVC:
+	case WMI_DATA_BE_SVC:
+	case WMI_DATA_BK_SVC:
+		return 2;
+	default:
+		return 0;
+	}
+}
+
+static void htc_process_target_rdy(struct htc_target *target,
+				   void *buf)
+{
+	struct htc_endpoint *endpoint;
+	struct htc_ready_msg *htc_ready_msg = (struct htc_ready_msg *) buf;
+
+	target->credits = be16_to_cpu(htc_ready_msg->credits);
+	target->credit_size = be16_to_cpu(htc_ready_msg->credit_size);
+
+	endpoint = &target->endpoint[ENDPOINT0];
+	endpoint->service_id = HTC_CTRL_RSVD_SVC;
+	endpoint->max_msglen = HTC_MAX_CONTROL_MESSAGE_LENGTH;
+	complete(&target->target_wait);
+}
+
+static void htc_process_conn_rsp(struct htc_target *target,
+				 struct htc_frame_hdr *htc_hdr)
+{
+	struct htc_conn_svc_rspmsg *svc_rspmsg;
+	struct htc_endpoint *endpoint, *tmp_endpoint = NULL;
+	u16 service_id;
+	u16 max_msglen;
+	enum htc_endpoint_id epid, tepid;
+
+	svc_rspmsg = (struct htc_conn_svc_rspmsg *)
+		((void *) htc_hdr + sizeof(struct htc_frame_hdr));
+
+	if (svc_rspmsg->status == HTC_SERVICE_SUCCESS) {
+		epid = svc_rspmsg->endpoint_id;
+		service_id = be16_to_cpu(svc_rspmsg->service_id);
+		max_msglen = be16_to_cpu(svc_rspmsg->max_msg_len);
+		endpoint = &target->endpoint[epid];
+
+		for (tepid = ENDPOINT_MAX; tepid > ENDPOINT0; tepid--) {
+			tmp_endpoint = &target->endpoint[tepid];
+			if (tmp_endpoint->service_id == service_id) {
+				tmp_endpoint->service_id = 0;
+				break;
+			}
+		}
+
+		if (!tmp_endpoint)
+			return;
+
+		endpoint->service_id = service_id;
+		endpoint->max_txqdepth = tmp_endpoint->max_txqdepth;
+		endpoint->ep_callbacks = tmp_endpoint->ep_callbacks;
+		endpoint->ul_pipeid = tmp_endpoint->ul_pipeid;
+		endpoint->dl_pipeid = tmp_endpoint->dl_pipeid;
+		endpoint->max_msglen = max_msglen;
+		target->conn_rsp_epid = epid;
+		complete(&target->cmd_wait);
+	} else {
+		target->conn_rsp_epid = ENDPOINT_UNUSED;
+	}
+}
+
+static int htc_config_pipe_credits(struct htc_target *target)
+{
+	struct sk_buff *skb;
+	struct htc_config_pipe_msg *cp_msg;
+	int ret, time_left;
+
+	skb = dev_alloc_skb(50 + sizeof(struct htc_frame_hdr));
+	if (!skb) {
+		dev_err(target->dev, "failed to allocate send buffer\n");
+		return -ENOMEM;
+	}
+	skb_reserve(skb, sizeof(struct htc_frame_hdr));
+
+	cp_msg = (struct htc_config_pipe_msg *)
+		skb_put(skb, sizeof(struct htc_config_pipe_msg));
+
+	cp_msg->message_id = cpu_to_be16(HTC_MSG_CONFIG_PIPE_ID);
+	cp_msg->pipe_id = USB_WLAN_TX_PIPE;
+	cp_msg->credits = 28;
+
+	target->htc_flags |= HTC_OP_CONFIG_PIPE_CREDITS;
+
+	ret = htc_issue_send(target, skb, skb->len, 0, ENDPOINT0, NULL);
+	if (ret)
+		goto err;
+
+	time_left = wait_for_completion_timeout(&target->cmd_wait, HZ);
+	if (!time_left) {
+		dev_err(target->dev, "HTC credit config timeout\n");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+err:
+	dev_kfree_skb(skb);
+	return -EINVAL;
+}
+
+static int htc_setup_complete(struct htc_target *target)
+{
+	struct sk_buff *skb;
+	struct htc_comp_msg *comp_msg;
+	int ret = 0, time_left;
+
+	skb = dev_alloc_skb(50 + sizeof(struct htc_frame_hdr));
+	if (!skb) {
+		dev_err(target->dev, "failed to allocate send buffer\n");
+		return -ENOMEM;
+	}
+	skb_reserve(skb, sizeof(struct htc_frame_hdr));
+
+	comp_msg = (struct htc_comp_msg *)
+		skb_put(skb, sizeof(struct htc_comp_msg));
+	comp_msg->msg_id = cpu_to_be16(HTC_MSG_SETUP_COMPLETE_ID);
+
+	target->htc_flags |= HTC_OP_START_WAIT;
+
+	ret = htc_issue_send(target, skb, skb->len, 0, ENDPOINT0, NULL);
+	if (ret)
+		goto err;
+
+	time_left = wait_for_completion_timeout(&target->cmd_wait, HZ);
+	if (!time_left) {
+		dev_err(target->dev, "HTC start timeout\n");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+
+err:
+	dev_kfree_skb(skb);
+	return -EINVAL;
+}
+
+/* HTC APIs */
+
+int htc_init(struct htc_target *target)
+{
+	int ret;
+
+	ret = htc_config_pipe_credits(target);
+	if (ret)
+		return ret;
+
+	return htc_setup_complete(target);
+}
+
+int htc_connect_service(struct htc_target *target,
+		     struct htc_service_connreq *service_connreq,
+		     enum htc_endpoint_id *conn_rsp_epid)
+{
+	struct sk_buff *skb;
+	struct htc_endpoint *endpoint;
+	struct htc_conn_svc_msg *conn_msg;
+	int ret, time_left;
+
+	/* Find an available endpoint */
+	endpoint = get_next_avail_ep(target->endpoint);
+	if (!endpoint) {
+		dev_err(target->dev, "Endpoint is not available for"
+			"service %d\n", service_connreq->service_id);
+		return -EINVAL;
+	}
+
+	endpoint->service_id = service_connreq->service_id;
+	endpoint->max_txqdepth = service_connreq->max_send_qdepth;
+	endpoint->ul_pipeid = service_to_ulpipe(service_connreq->service_id);
+	endpoint->dl_pipeid = service_to_dlpipe(service_connreq->service_id);
+	endpoint->ep_callbacks = service_connreq->ep_callbacks;
+
+	skb = dev_alloc_skb(sizeof(struct htc_conn_svc_msg) +
+			    sizeof(struct htc_frame_hdr));
+	if (!skb) {
+		dev_err(target->dev, "Failed to allocate buf to send"
+			"service connect req\n");
+		return -ENOMEM;
+	}
+
+	skb_reserve(skb, sizeof(struct htc_frame_hdr));
+
+	conn_msg = (struct htc_conn_svc_msg *)
+			skb_put(skb, sizeof(struct htc_conn_svc_msg));
+	conn_msg->service_id = cpu_to_be16(service_connreq->service_id);
+	conn_msg->msg_id = cpu_to_be16(HTC_MSG_CONNECT_SERVICE_ID);
+	conn_msg->con_flags = cpu_to_be16(service_connreq->con_flags);
+	conn_msg->dl_pipeid = endpoint->dl_pipeid;
+	conn_msg->ul_pipeid = endpoint->ul_pipeid;
+
+	ret = htc_issue_send(target, skb, skb->len, 0, ENDPOINT0, NULL);
+	if (ret)
+		goto err;
+
+	time_left = wait_for_completion_timeout(&target->cmd_wait, HZ);
+	if (!time_left) {
+		dev_err(target->dev, "Service connection timeout for: %d\n",
+			service_connreq->service_id);
+		return -ETIMEDOUT;
+	}
+
+	*conn_rsp_epid = target->conn_rsp_epid;
+	return 0;
+err:
+	dev_kfree_skb(skb);
+	return ret;
+}
+
+int htc_send(struct htc_target *target, struct sk_buff *skb,
+	     enum htc_endpoint_id epid, struct ath9k_htc_tx_ctl *tx_ctl)
+{
+	return htc_issue_send(target, skb, skb->len, 0, epid, tx_ctl);
+}
+
+void htc_stop(struct htc_target *target)
+{
+	enum htc_endpoint_id epid;
+	struct htc_endpoint *endpoint;
+
+	for (epid = ENDPOINT0; epid <= ENDPOINT_MAX; epid++) {
+		endpoint = &target->endpoint[epid];
+		if (endpoint->service_id != 0)
+			target->hif->stop(target->hif_dev, endpoint->ul_pipeid);
+	}
+}
+
+void htc_start(struct htc_target *target)
+{
+	enum htc_endpoint_id epid;
+	struct htc_endpoint *endpoint;
+
+	for (epid = ENDPOINT0; epid <= ENDPOINT_MAX; epid++) {
+		endpoint = &target->endpoint[epid];
+		if (endpoint->service_id != 0)
+			target->hif->start(target->hif_dev,
+					   endpoint->ul_pipeid);
+	}
+}
+
+void ath9k_htc_txcompletion_cb(struct htc_target *htc_handle,
+			       struct sk_buff *skb, bool txok)
+{
+	struct htc_endpoint *endpoint;
+	struct htc_frame_hdr *htc_hdr;
+
+	if (htc_handle->htc_flags & HTC_OP_CONFIG_PIPE_CREDITS) {
+		complete(&htc_handle->cmd_wait);
+		htc_handle->htc_flags &= ~HTC_OP_CONFIG_PIPE_CREDITS;
+	}
+
+	if (htc_handle->htc_flags & HTC_OP_START_WAIT) {
+		complete(&htc_handle->cmd_wait);
+		htc_handle->htc_flags &= ~HTC_OP_START_WAIT;
+	}
+
+	if (skb) {
+		htc_hdr = (struct htc_frame_hdr *) skb->data;
+		endpoint = &htc_handle->endpoint[htc_hdr->endpoint_id];
+		skb_pull(skb, sizeof(struct htc_frame_hdr));
+
+		if (endpoint->ep_callbacks.tx) {
+			endpoint->ep_callbacks.tx(htc_handle->drv_priv, skb,
+						  htc_hdr->endpoint_id, txok);
+		}
+	}
+}
+
+/*
+ * HTC Messages are handled directly here and the obtained SKB
+ * is freed.
+ *
+ * Sevice messages (Data, WMI) passed to the corresponding
+ * endpoint RX handlers, which have to free the SKB.
+ */
+void ath9k_htc_rx_msg(struct htc_target *htc_handle,
+		      struct sk_buff *skb, u32 len, u8 pipe_id)
+{
+	struct htc_frame_hdr *htc_hdr;
+	enum htc_endpoint_id epid;
+	struct htc_endpoint *endpoint;
+	u16 *msg_id;
+
+	if (!htc_handle || !skb)
+		return;
+
+	htc_hdr = (struct htc_frame_hdr *) skb->data;
+	epid = htc_hdr->endpoint_id;
+
+	if (epid >= ENDPOINT_MAX) {
+		dev_kfree_skb_any(skb);
+		return;
+	}
+
+	if (epid == ENDPOINT0) {
+
+		/* Handle trailer */
+		if (htc_hdr->flags & HTC_FLAGS_RECV_TRAILER) {
+			if (be32_to_cpu(*(u32 *) skb->data) == 0x00C60000)
+				/* Move past the Watchdog pattern */
+				htc_hdr = (struct htc_frame_hdr *) skb->data + 4;
+		}
+
+		/* Get the message ID */
+		msg_id = (u16 *) ((void *) htc_hdr +
+					   sizeof(struct htc_frame_hdr));
+
+		/* Now process HTC messages */
+		switch (be16_to_cpu(*msg_id)) {
+		case HTC_MSG_READY_ID:
+			htc_process_target_rdy(htc_handle, htc_hdr);
+			break;
+		case HTC_MSG_CONNECT_SERVICE_RESPONSE_ID:
+			htc_process_conn_rsp(htc_handle, htc_hdr);
+			break;
+		default:
+			break;
+		}
+
+		dev_kfree_skb_any(skb);
+
+	} else {
+		if (htc_hdr->flags & HTC_FLAGS_RECV_TRAILER)
+			skb_trim(skb, len - htc_hdr->control[0]);
+
+		skb_pull(skb, sizeof(struct htc_frame_hdr));
+
+		endpoint = &htc_handle->endpoint[epid];
+		if (endpoint->ep_callbacks.rx)
+			endpoint->ep_callbacks.rx(endpoint->ep_callbacks.priv,
+						  skb, epid);
+	}
+}
+
+struct htc_target *ath9k_htc_hw_alloc(void *hif_handle)
+{
+	struct htc_target *target;
+
+	target = kzalloc(sizeof(struct htc_target), GFP_KERNEL);
+	if (!target)
+		printk(KERN_ERR "Unable to allocate memory for"
+			"target device\n");
+
+	return target;
+}
+
+void ath9k_htc_hw_free(struct htc_target *htc)
+{
+	kfree(htc);
+}
+
+int ath9k_htc_hw_init(struct ath9k_htc_hif *hif, struct htc_target *target,
+		      void *hif_handle, struct device *dev, u16 devid,
+		      enum ath9k_hif_transports transport)
+{
+	struct htc_endpoint *endpoint;
+	int err = 0;
+
+	init_completion(&target->target_wait);
+	init_completion(&target->cmd_wait);
+
+	target->hif = hif;
+	target->hif_dev = hif_handle;
+	target->dev = dev;
+
+	/* Assign control endpoint pipe IDs */
+	endpoint = &target->endpoint[ENDPOINT0];
+	endpoint->ul_pipeid = hif->control_ul_pipe;
+	endpoint->dl_pipeid = hif->control_dl_pipe;
+
+	err = ath9k_htc_probe_device(target, dev, devid);
+	if (err) {
+		printk(KERN_ERR "Failed to initialize the device\n");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+void ath9k_htc_hw_deinit(struct htc_target *target, bool hot_unplug)
+{
+	if (target)
+		ath9k_htc_disconnect_device(target, hot_unplug);
+}

+ 246 - 0
drivers/net/wireless/ath/ath9k/htc_hst.h

@@ -0,0 +1,246 @@
+/*
+ * Copyright (c) 2010 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef HTC_HST_H
+#define HTC_HST_H
+
+struct ath9k_htc_priv;
+struct htc_target;
+struct ath9k_htc_tx_ctl;
+
+enum ath9k_hif_transports {
+	ATH9K_HIF_USB,
+};
+
+struct ath9k_htc_hif {
+	struct list_head list;
+	const enum ath9k_hif_transports transport;
+	const char *name;
+
+	u8 control_dl_pipe;
+	u8 control_ul_pipe;
+
+	void (*start) (void *hif_handle, u8 pipe);
+	void (*stop) (void *hif_handle, u8 pipe);
+	int (*send) (void *hif_handle, u8 pipe, struct sk_buff *buf,
+		     struct ath9k_htc_tx_ctl *tx_ctl);
+};
+
+enum htc_endpoint_id {
+	ENDPOINT_UNUSED = -1,
+	ENDPOINT0 = 0,
+	ENDPOINT1 = 1,
+	ENDPOINT2 = 2,
+	ENDPOINT3 = 3,
+	ENDPOINT4 = 4,
+	ENDPOINT5 = 5,
+	ENDPOINT6 = 6,
+	ENDPOINT7 = 7,
+	ENDPOINT8 = 8,
+	ENDPOINT_MAX = 22
+};
+
+/* Htc frame hdr flags */
+#define HTC_FLAGS_RECV_TRAILER (1 << 1)
+
+struct htc_frame_hdr {
+	u8 endpoint_id;
+	u8 flags;
+	u16 payload_len;
+	u8 control[4];
+} __packed;
+
+struct htc_ready_msg {
+	u16 message_id;
+	u16 credits;
+	u16 credit_size;
+	u8 max_endpoints;
+	u8 pad;
+} __packed;
+
+struct htc_config_pipe_msg {
+	u16 message_id;
+	u8 pipe_id;
+	u8 credits;
+} __packed;
+
+struct htc_packet {
+	void *pktcontext;
+	u8 *buf;
+	u8 *buf_payload;
+	u32 buflen;
+	u32 payload_len;
+
+	int endpoint;
+	int status;
+
+	void *context;
+	u32 reserved;
+};
+
+struct htc_ep_callbacks {
+	void *priv;
+	void (*tx) (void *, struct sk_buff *, enum htc_endpoint_id, bool txok);
+	void (*rx) (void *, struct sk_buff *, enum htc_endpoint_id);
+};
+
+#define HTC_TX_QUEUE_SIZE 256
+
+struct htc_txq {
+	struct sk_buff *buf[HTC_TX_QUEUE_SIZE];
+	u32 txqdepth;
+	u16 txbuf_cnt;
+	u16 txq_head;
+	u16 txq_tail;
+};
+
+struct htc_endpoint {
+	u16 service_id;
+
+	struct htc_ep_callbacks ep_callbacks;
+	struct htc_txq htc_txq;
+	u32 max_txqdepth;
+	int max_msglen;
+
+	u8 ul_pipeid;
+	u8 dl_pipeid;
+};
+
+#define HTC_MAX_CONTROL_MESSAGE_LENGTH 255
+#define HTC_CONTROL_BUFFER_SIZE	\
+	(HTC_MAX_CONTROL_MESSAGE_LENGTH + sizeof(struct htc_frame_hdr))
+
+#define NUM_CONTROL_BUFFERS 8
+#define HST_ENDPOINT_MAX 8
+
+struct htc_control_buf {
+	struct htc_packet htc_pkt;
+	u8 buf[HTC_CONTROL_BUFFER_SIZE];
+};
+
+#define HTC_OP_START_WAIT           BIT(0)
+#define HTC_OP_CONFIG_PIPE_CREDITS  BIT(1)
+
+struct htc_target {
+	void *hif_dev;
+	struct ath9k_htc_priv *drv_priv;
+	struct device *dev;
+	struct ath9k_htc_hif *hif;
+	struct htc_endpoint endpoint[HST_ENDPOINT_MAX];
+	struct completion target_wait;
+	struct completion cmd_wait;
+	struct list_head list;
+	enum htc_endpoint_id conn_rsp_epid;
+	u16 credits;
+	u16 credit_size;
+	u8 htc_flags;
+};
+
+enum htc_msg_id {
+	HTC_MSG_READY_ID = 1,
+	HTC_MSG_CONNECT_SERVICE_ID,
+	HTC_MSG_CONNECT_SERVICE_RESPONSE_ID,
+	HTC_MSG_SETUP_COMPLETE_ID,
+	HTC_MSG_CONFIG_PIPE_ID,
+	HTC_MSG_CONFIG_PIPE_RESPONSE_ID,
+};
+
+struct htc_service_connreq {
+	u16 service_id;
+	u16 con_flags;
+	u32 max_send_qdepth;
+	struct htc_ep_callbacks ep_callbacks;
+};
+
+/* Current service IDs */
+
+enum htc_service_group_ids{
+	RSVD_SERVICE_GROUP = 0,
+	WMI_SERVICE_GROUP = 1,
+
+	HTC_SERVICE_GROUP_LAST = 255
+};
+
+#define MAKE_SERVICE_ID(group, index)		\
+	(int)(((int)group << 8) | (int)(index))
+
+/* NOTE: service ID of 0x0000 is reserved and should never be used */
+#define HTC_CTRL_RSVD_SVC MAKE_SERVICE_ID(RSVD_SERVICE_GROUP, 1)
+#define HTC_LOOPBACK_RSVD_SVC MAKE_SERVICE_ID(RSVD_SERVICE_GROUP, 2)
+
+#define WMI_CONTROL_SVC   MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 0)
+#define WMI_BEACON_SVC	  MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 1)
+#define WMI_CAB_SVC	  MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 2)
+#define WMI_UAPSD_SVC	  MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 3)
+#define WMI_MGMT_SVC	  MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 4)
+#define WMI_DATA_VO_SVC   MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 5)
+#define WMI_DATA_VI_SVC   MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 6)
+#define WMI_DATA_BE_SVC   MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 7)
+#define WMI_DATA_BK_SVC   MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 8)
+
+struct htc_conn_svc_msg {
+	u16 msg_id;
+	u16 service_id;
+	u16 con_flags;
+	u8 dl_pipeid;
+	u8 ul_pipeid;
+	u8 svc_meta_len;
+	u8 pad;
+} __packed;
+
+/* connect response status codes */
+#define HTC_SERVICE_SUCCESS      0
+#define HTC_SERVICE_NOT_FOUND    1
+#define HTC_SERVICE_FAILED       2
+#define HTC_SERVICE_NO_RESOURCES 3
+#define HTC_SERVICE_NO_MORE_EP   4
+
+struct htc_conn_svc_rspmsg {
+	u16 msg_id;
+	u16 service_id;
+	u8 status;
+	u8 endpoint_id;
+	u16 max_msg_len;
+	u8 svc_meta_len;
+	u8 pad;
+} __packed;
+
+struct htc_comp_msg {
+	u16 msg_id;
+} __packed;
+
+int htc_init(struct htc_target *target);
+int htc_connect_service(struct htc_target *target,
+			  struct htc_service_connreq *service_connreq,
+			  enum htc_endpoint_id *conn_rsp_eid);
+int htc_send(struct htc_target *target, struct sk_buff *skb,
+	     enum htc_endpoint_id eid, struct ath9k_htc_tx_ctl *tx_ctl);
+void htc_stop(struct htc_target *target);
+void htc_start(struct htc_target *target);
+
+void ath9k_htc_rx_msg(struct htc_target *htc_handle,
+		      struct sk_buff *skb, u32 len, u8 pipe_id);
+void ath9k_htc_txcompletion_cb(struct htc_target *htc_handle,
+			       struct sk_buff *skb, bool txok);
+
+struct htc_target *ath9k_htc_hw_alloc(void *hif_handle);
+void ath9k_htc_hw_free(struct htc_target *htc);
+int ath9k_htc_hw_init(struct ath9k_htc_hif *hif, struct htc_target *target,
+		      void *hif_handle, struct device *dev, u16 devid,
+		      enum ath9k_hif_transports transport);
+void ath9k_htc_hw_deinit(struct htc_target *target, bool hot_unplug);
+
+#endif /* HTC_HST_H */

+ 68 - 66
drivers/net/wireless/ath/ath9k/hw.c

@@ -499,8 +499,10 @@ static int ath9k_hw_post_init(struct ath_hw *ah)
 {
 {
 	int ecode;
 	int ecode;
 
 
-	if (!ath9k_hw_chip_test(ah))
-		return -ENODEV;
+	if (!AR_SREV_9271(ah)) {
+		if (!ath9k_hw_chip_test(ah))
+			return -ENODEV;
+	}
 
 
 	ecode = ath9k_hw_rf_claim(ah);
 	ecode = ath9k_hw_rf_claim(ah);
 	if (ecode != 0)
 	if (ecode != 0)
@@ -603,9 +605,23 @@ static void ath9k_hw_init_mode_regs(struct ath_hw *ah)
 			       ARRAY_SIZE(ar9271Modes_9271), 6);
 			       ARRAY_SIZE(ar9271Modes_9271), 6);
 		INIT_INI_ARRAY(&ah->iniCommon, ar9271Common_9271,
 		INIT_INI_ARRAY(&ah->iniCommon, ar9271Common_9271,
 			       ARRAY_SIZE(ar9271Common_9271), 2);
 			       ARRAY_SIZE(ar9271Common_9271), 2);
+		INIT_INI_ARRAY(&ah->iniCommon_normal_cck_fir_coeff_9271,
+			       ar9271Common_normal_cck_fir_coeff_9271,
+			       ARRAY_SIZE(ar9271Common_normal_cck_fir_coeff_9271), 2);
+		INIT_INI_ARRAY(&ah->iniCommon_japan_2484_cck_fir_coeff_9271,
+			       ar9271Common_japan_2484_cck_fir_coeff_9271,
+			       ARRAY_SIZE(ar9271Common_japan_2484_cck_fir_coeff_9271), 2);
 		INIT_INI_ARRAY(&ah->iniModes_9271_1_0_only,
 		INIT_INI_ARRAY(&ah->iniModes_9271_1_0_only,
 			       ar9271Modes_9271_1_0_only,
 			       ar9271Modes_9271_1_0_only,
 			       ARRAY_SIZE(ar9271Modes_9271_1_0_only), 6);
 			       ARRAY_SIZE(ar9271Modes_9271_1_0_only), 6);
+		INIT_INI_ARRAY(&ah->iniModes_9271_ANI_reg, ar9271Modes_9271_ANI_reg,
+			       ARRAY_SIZE(ar9271Modes_9271_ANI_reg), 6);
+		INIT_INI_ARRAY(&ah->iniModes_high_power_tx_gain_9271,
+			       ar9271Modes_high_power_tx_gain_9271,
+			       ARRAY_SIZE(ar9271Modes_high_power_tx_gain_9271), 6);
+		INIT_INI_ARRAY(&ah->iniModes_normal_power_tx_gain_9271,
+			       ar9271Modes_normal_power_tx_gain_9271,
+			       ARRAY_SIZE(ar9271Modes_normal_power_tx_gain_9271), 6);
 		return;
 		return;
 	}
 	}
 
 
@@ -990,22 +1006,6 @@ static void ath9k_hw_init_qos(struct ath_hw *ah)
 	REG_WRITE(ah, AR_TXOP_12_15, 0xFFFFFFFF);
 	REG_WRITE(ah, AR_TXOP_12_15, 0xFFFFFFFF);
 }
 }
 
 
-static void ath9k_hw_change_target_baud(struct ath_hw *ah, u32 freq, u32 baud)
-{
-	u32 lcr;
-	u32 baud_divider = freq * 1000 * 1000 / 16 / baud;
-
-	lcr = REG_READ(ah , 0x5100c);
-	lcr |= 0x80;
-
-	REG_WRITE(ah, 0x5100c, lcr);
-	REG_WRITE(ah, 0x51004, (baud_divider >> 8));
-	REG_WRITE(ah, 0x51000, (baud_divider & 0xff));
-
-	lcr &= ~0x80;
-	REG_WRITE(ah, 0x5100c, lcr);
-}
-
 static void ath9k_hw_init_pll(struct ath_hw *ah,
 static void ath9k_hw_init_pll(struct ath_hw *ah,
 			      struct ath9k_channel *chan)
 			      struct ath9k_channel *chan)
 {
 {
@@ -1071,22 +1071,8 @@ static void ath9k_hw_init_pll(struct ath_hw *ah,
 
 
 	/* Switch the core clock for ar9271 to 117Mhz */
 	/* Switch the core clock for ar9271 to 117Mhz */
 	if (AR_SREV_9271(ah)) {
 	if (AR_SREV_9271(ah)) {
-		if ((pll == 0x142c) || (pll == 0x2850) ) {
-			udelay(500);
-			/* set CLKOBS to output AHB clock */
-			REG_WRITE(ah, 0x7020, 0xe);
-			/*
-			 * 0x304: 117Mhz, ahb_ratio: 1x1
-			 * 0x306: 40Mhz, ahb_ratio: 1x1
-			 */
-			REG_WRITE(ah, 0x50040, 0x304);
-			/*
-			 * makes adjustments for the baud dividor to keep the
-			 * targetted baud rate based on the used core clock.
-			 */
-			ath9k_hw_change_target_baud(ah, AR9271_CORE_CLOCK,
-						    AR9271_TARGET_BAUD_RATE);
-		}
+		udelay(500);
+		REG_WRITE(ah, 0x50040, 0x304);
 	}
 	}
 
 
 	udelay(RTC_PLL_SETTLE_DELAY);
 	udelay(RTC_PLL_SETTLE_DELAY);
@@ -1241,7 +1227,7 @@ void ath9k_hw_deinit(struct ath_hw *ah)
 {
 {
 	struct ath_common *common = ath9k_hw_common(ah);
 	struct ath_common *common = ath9k_hw_common(ah);
 
 
-	if (common->state <= ATH_HW_INITIALIZED)
+	if (common->state < ATH_HW_INITIALIZED)
 		goto free_hw;
 		goto free_hw;
 
 
 	if (!AR_SREV_9100(ah))
 	if (!AR_SREV_9100(ah))
@@ -1252,8 +1238,6 @@ void ath9k_hw_deinit(struct ath_hw *ah)
 free_hw:
 free_hw:
 	if (!AR_SREV_9280_10_OR_LATER(ah))
 	if (!AR_SREV_9280_10_OR_LATER(ah))
 		ath9k_hw_rf_free_ext_banks(ah);
 		ath9k_hw_rf_free_ext_banks(ah);
-	kfree(ah);
-	ah = NULL;
 }
 }
 EXPORT_SYMBOL(ath9k_hw_deinit);
 EXPORT_SYMBOL(ath9k_hw_deinit);
 
 
@@ -1266,26 +1250,6 @@ static void ath9k_hw_override_ini(struct ath_hw *ah,
 {
 {
 	u32 val;
 	u32 val;
 
 
-	if (AR_SREV_9271(ah)) {
-		/*
-		 * Enable spectral scan to solution for issues with stuck
-		 * beacons on AR9271 1.0. The beacon stuck issue is not seeon on
-		 * AR9271 1.1
-		 */
-		if (AR_SREV_9271_10(ah)) {
-			val = REG_READ(ah, AR_PHY_SPECTRAL_SCAN) |
-			      AR_PHY_SPECTRAL_SCAN_ENABLE;
-			REG_WRITE(ah, AR_PHY_SPECTRAL_SCAN, val);
-		}
-		else if (AR_SREV_9271_11(ah))
-			/*
-			 * change AR_PHY_RF_CTL3 setting to fix MAC issue
-			 * present on AR9271 1.1
-			 */
-			REG_WRITE(ah, AR_PHY_RF_CTL3, 0x3a020001);
-		return;
-	}
-
 	/*
 	/*
 	 * Set the RX_ABORT and RX_DIS and clear if off only after
 	 * Set the RX_ABORT and RX_DIS and clear if off only after
 	 * RXE is set for MAC. This prevents frames with corrupted
 	 * RXE is set for MAC. This prevents frames with corrupted
@@ -1294,8 +1258,10 @@ static void ath9k_hw_override_ini(struct ath_hw *ah,
 	REG_SET_BIT(ah, AR_DIAG_SW, (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT));
 	REG_SET_BIT(ah, AR_DIAG_SW, (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT));
 
 
 	if (AR_SREV_9280_10_OR_LATER(ah)) {
 	if (AR_SREV_9280_10_OR_LATER(ah)) {
-		val = REG_READ(ah, AR_PCU_MISC_MODE2) &
-			       (~AR_PCU_MISC_MODE2_HWWAR1);
+		val = REG_READ(ah, AR_PCU_MISC_MODE2);
+
+		if (!AR_SREV_9271(ah))
+			val &= ~AR_PCU_MISC_MODE2_HWWAR1;
 
 
 		if (AR_SREV_9287_10_OR_LATER(ah))
 		if (AR_SREV_9287_10_OR_LATER(ah))
 			val = val & (~AR_PCU_MISC_MODE2_HWWAR2);
 			val = val & (~AR_PCU_MISC_MODE2_HWWAR2);
@@ -1439,7 +1405,10 @@ static int ath9k_hw_process_ini(struct ath_hw *ah,
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
 
 
+	/* Set correct baseband to analog shift setting to access analog chips */
 	REG_WRITE(ah, AR_PHY(0), 0x00000007);
 	REG_WRITE(ah, AR_PHY(0), 0x00000007);
+
+	/* Write ADDAC shifts */
 	REG_WRITE(ah, AR_PHY_ADC_SERIAL_CTL, AR_PHY_SEL_EXTERNAL_RADIO);
 	REG_WRITE(ah, AR_PHY_ADC_SERIAL_CTL, AR_PHY_SEL_EXTERNAL_RADIO);
 	ah->eep_ops->set_addac(ah, chan);
 	ah->eep_ops->set_addac(ah, chan);
 
 
@@ -1451,9 +1420,11 @@ static int ath9k_hw_process_ini(struct ath_hw *ah,
 			sizeof(u32) * ah->iniAddac.ia_rows *
 			sizeof(u32) * ah->iniAddac.ia_rows *
 			ah->iniAddac.ia_columns;
 			ah->iniAddac.ia_columns;
 
 
+		/* For AR5416 2.0/2.1 */
 		memcpy(ah->addac5416_21,
 		memcpy(ah->addac5416_21,
 		       ah->iniAddac.ia_array, addacSize);
 		       ah->iniAddac.ia_array, addacSize);
 
 
+		/* override CLKDRV value at [row, column] = [31, 1] */
 		(ah->addac5416_21)[31 * ah->iniAddac.ia_columns + 1] = 0;
 		(ah->addac5416_21)[31 * ah->iniAddac.ia_columns + 1] = 0;
 
 
 		temp.ia_array = ah->addac5416_21;
 		temp.ia_array = ah->addac5416_21;
@@ -1485,6 +1456,11 @@ static int ath9k_hw_process_ini(struct ath_hw *ah,
 	    AR_SREV_9287_10_OR_LATER(ah))
 	    AR_SREV_9287_10_OR_LATER(ah))
 		REG_WRITE_ARRAY(&ah->iniModesTxGain, modesIndex, regWrites);
 		REG_WRITE_ARRAY(&ah->iniModesTxGain, modesIndex, regWrites);
 
 
+	if (AR_SREV_9271_10(ah))
+		REG_WRITE_ARRAY(&ah->iniModes_9271_1_0_only,
+				modesIndex, regWrites);
+
+	/* Write common array parameters */
 	for (i = 0; i < ah->iniCommon.ia_rows; i++) {
 	for (i = 0; i < ah->iniCommon.ia_rows; i++) {
 		u32 reg = INI_RA(&ah->iniCommon, i, 0);
 		u32 reg = INI_RA(&ah->iniCommon, i, 0);
 		u32 val = INI_RA(&ah->iniCommon, i, 1);
 		u32 val = INI_RA(&ah->iniCommon, i, 1);
@@ -1499,11 +1475,16 @@ static int ath9k_hw_process_ini(struct ath_hw *ah,
 		DO_DELAY(regWrites);
 		DO_DELAY(regWrites);
 	}
 	}
 
 
-	ath9k_hw_write_regs(ah, freqIndex, regWrites);
+	if (AR_SREV_9271(ah)) {
+		if (ah->eep_ops->get_eeprom(ah, EEP_TXGAIN_TYPE) == 1)
+			REG_WRITE_ARRAY(&ah->iniModes_high_power_tx_gain_9271,
+					modesIndex, regWrites);
+		else
+			REG_WRITE_ARRAY(&ah->iniModes_normal_power_tx_gain_9271,
+					modesIndex, regWrites);
+	}
 
 
-	if (AR_SREV_9271_10(ah))
-		REG_WRITE_ARRAY(&ah->iniModes_9271_1_0_only,
-				modesIndex, regWrites);
+	ath9k_hw_write_regs(ah, freqIndex, regWrites);
 
 
 	if (AR_SREV_9280_20(ah) && IS_CHAN_A_5MHZ_SPACED(chan)) {
 	if (AR_SREV_9280_20(ah) && IS_CHAN_A_5MHZ_SPACED(chan)) {
 		REG_WRITE_ARRAY(&ah->iniModesAdditional, modesIndex,
 		REG_WRITE_ARRAY(&ah->iniModesAdditional, modesIndex,
@@ -1517,6 +1498,7 @@ static int ath9k_hw_process_ini(struct ath_hw *ah,
 	if (OLC_FOR_AR9280_20_LATER)
 	if (OLC_FOR_AR9280_20_LATER)
 		ath9k_olc_init(ah);
 		ath9k_olc_init(ah);
 
 
+	/* Set TX power */
 	ah->eep_ops->set_txpower(ah, chan,
 	ah->eep_ops->set_txpower(ah, chan,
 				 ath9k_regd_get_ctl(regulatory, chan),
 				 ath9k_regd_get_ctl(regulatory, chan),
 				 channel->max_antenna_gain * 2,
 				 channel->max_antenna_gain * 2,
@@ -1524,6 +1506,7 @@ static int ath9k_hw_process_ini(struct ath_hw *ah,
 				 min((u32) MAX_RATE_POWER,
 				 min((u32) MAX_RATE_POWER,
 				 (u32) regulatory->power_limit));
 				 (u32) regulatory->power_limit));
 
 
+	/* Write analog registers */
 	if (!ath9k_hw_set_rf_regs(ah, chan, freqIndex)) {
 	if (!ath9k_hw_set_rf_regs(ah, chan, freqIndex)) {
 		ath_print(ath9k_hw_common(ah), ATH_DBG_FATAL,
 		ath_print(ath9k_hw_common(ah), ATH_DBG_FATAL,
 			  "ar5416SetRfRegs failed\n");
 			  "ar5416SetRfRegs failed\n");
@@ -1966,6 +1949,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
 
 
 	ath9k_hw_mark_phy_inactive(ah);
 	ath9k_hw_mark_phy_inactive(ah);
 
 
+	/* Only required on the first reset */
 	if (AR_SREV_9271(ah) && ah->htc_reset_init) {
 	if (AR_SREV_9271(ah) && ah->htc_reset_init) {
 		REG_WRITE(ah,
 		REG_WRITE(ah,
 			  AR9271_RESET_POWER_DOWN_CONTROL,
 			  AR9271_RESET_POWER_DOWN_CONTROL,
@@ -1978,6 +1962,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
 
 
+	/* Only required on the first reset */
 	if (AR_SREV_9271(ah) && ah->htc_reset_init) {
 	if (AR_SREV_9271(ah) && ah->htc_reset_init) {
 		ah->htc_reset_init = false;
 		ah->htc_reset_init = false;
 		REG_WRITE(ah,
 		REG_WRITE(ah,
@@ -2438,7 +2423,7 @@ static void ath9k_set_power_sleep(struct ath_hw *ah, int setChip)
 		if (!AR_SREV_9100(ah))
 		if (!AR_SREV_9100(ah))
 			REG_WRITE(ah, AR_RC, AR_RC_AHB | AR_RC_HOSTIF);
 			REG_WRITE(ah, AR_RC, AR_RC_AHB | AR_RC_HOSTIF);
 
 
-		if(!AR_SREV_5416(ah))
+		if (!AR_SREV_5416(ah) && !AR_SREV_9271(ah))
 			REG_CLR_BIT(ah, (AR_RTC_RESET),
 			REG_CLR_BIT(ah, (AR_RTC_RESET),
 				    AR_RTC_RESET_EN);
 				    AR_RTC_RESET_EN);
 	}
 	}
@@ -3216,7 +3201,9 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah)
 	else
 	else
 		pCap->tx_triglevel_max = MAX_TX_FIFO_THRESHOLD;
 		pCap->tx_triglevel_max = MAX_TX_FIFO_THRESHOLD;
 
 
-	if (AR_SREV_9285_10_OR_LATER(ah))
+	if (AR_SREV_9271(ah))
+		pCap->num_gpio_pins = AR9271_NUM_GPIO;
+	else if (AR_SREV_9285_10_OR_LATER(ah))
 		pCap->num_gpio_pins = AR9285_NUM_GPIO;
 		pCap->num_gpio_pins = AR9285_NUM_GPIO;
 	else if (AR_SREV_9280_10_OR_LATER(ah))
 	else if (AR_SREV_9280_10_OR_LATER(ah))
 		pCap->num_gpio_pins = AR928X_NUM_GPIO;
 		pCap->num_gpio_pins = AR928X_NUM_GPIO;
@@ -3452,7 +3439,9 @@ u32 ath9k_hw_gpio_get(struct ath_hw *ah, u32 gpio)
 	if (gpio >= ah->caps.num_gpio_pins)
 	if (gpio >= ah->caps.num_gpio_pins)
 		return 0xffffffff;
 		return 0xffffffff;
 
 
-	if (AR_SREV_9287_10_OR_LATER(ah))
+	if (AR_SREV_9271(ah))
+		return MS_REG_READ(AR9271, gpio) != 0;
+	else if (AR_SREV_9287_10_OR_LATER(ah))
 		return MS_REG_READ(AR9287, gpio) != 0;
 		return MS_REG_READ(AR9287, gpio) != 0;
 	else if (AR_SREV_9285_10_OR_LATER(ah))
 	else if (AR_SREV_9285_10_OR_LATER(ah))
 		return MS_REG_READ(AR9285, gpio) != 0;
 		return MS_REG_READ(AR9285, gpio) != 0;
@@ -3481,6 +3470,9 @@ EXPORT_SYMBOL(ath9k_hw_cfg_output);
 
 
 void ath9k_hw_set_gpio(struct ath_hw *ah, u32 gpio, u32 val)
 void ath9k_hw_set_gpio(struct ath_hw *ah, u32 gpio, u32 val)
 {
 {
+	if (AR_SREV_9271(ah))
+		val = ~val;
+
 	REG_RMW(ah, AR_GPIO_IN_OUT, ((val & 1) << gpio),
 	REG_RMW(ah, AR_GPIO_IN_OUT, ((val & 1) << gpio),
 		AR_GPIO_BIT(gpio));
 		AR_GPIO_BIT(gpio));
 }
 }
@@ -3865,6 +3857,16 @@ void ath_gen_timer_isr(struct ath_hw *ah)
 }
 }
 EXPORT_SYMBOL(ath_gen_timer_isr);
 EXPORT_SYMBOL(ath_gen_timer_isr);
 
 
+/********/
+/* HTC  */
+/********/
+
+void ath9k_hw_htc_resetinit(struct ath_hw *ah)
+{
+	ah->htc_reset_init = true;
+}
+EXPORT_SYMBOL(ath9k_hw_htc_resetinit);
+
 static struct {
 static struct {
 	u32 version;
 	u32 version;
 	const char * name;
 	const char * name;

+ 8 - 0
drivers/net/wireless/ath/ath9k/hw.h

@@ -599,6 +599,11 @@ struct ath_hw {
 	struct ar5416IniArray iniModes_9271_1_0_only;
 	struct ar5416IniArray iniModes_9271_1_0_only;
 	struct ar5416IniArray iniCckfirNormal;
 	struct ar5416IniArray iniCckfirNormal;
 	struct ar5416IniArray iniCckfirJapan2484;
 	struct ar5416IniArray iniCckfirJapan2484;
+	struct ar5416IniArray iniCommon_normal_cck_fir_coeff_9271;
+	struct ar5416IniArray iniCommon_japan_2484_cck_fir_coeff_9271;
+	struct ar5416IniArray iniModes_9271_ANI_reg;
+	struct ar5416IniArray iniModes_high_power_tx_gain_9271;
+	struct ar5416IniArray iniModes_normal_power_tx_gain_9271;
 
 
 	u32 intr_gen_timer_trigger;
 	u32 intr_gen_timer_trigger;
 	u32 intr_gen_timer_thresh;
 	u32 intr_gen_timer_thresh;
@@ -702,6 +707,9 @@ u32 ath9k_hw_gettsf32(struct ath_hw *ah);
 
 
 void ath9k_hw_name(struct ath_hw *ah, char *hw_name, size_t len);
 void ath9k_hw_name(struct ath_hw *ah, char *hw_name, size_t len);
 
 
+/* HTC */
+void ath9k_hw_htc_resetinit(struct ath_hw *ah);
+
 #define ATH_PCIE_CAP_LINK_CTRL	0x70
 #define ATH_PCIE_CAP_LINK_CTRL	0x70
 #define ATH_PCIE_CAP_LINK_L0S	1
 #define ATH_PCIE_CAP_LINK_L0S	1
 #define ATH_PCIE_CAP_LINK_L1	2
 #define ATH_PCIE_CAP_LINK_L1	2

+ 3 - 0
drivers/net/wireless/ath/ath9k/init.c

@@ -758,6 +758,9 @@ static void ath9k_deinit_softc(struct ath_softc *sc)
 
 
 	tasklet_kill(&sc->intr_tq);
 	tasklet_kill(&sc->intr_tq);
 	tasklet_kill(&sc->bcon_tasklet);
 	tasklet_kill(&sc->bcon_tasklet);
+
+	kfree(sc->sc_ah);
+	sc->sc_ah = NULL;
 }
 }
 
 
 void ath9k_deinit_device(struct ath_softc *sc)
 void ath9k_deinit_device(struct ath_softc *sc)

+ 102 - 39
drivers/net/wireless/ath/ath9k/initvals.h

@@ -6441,7 +6441,7 @@ static const u_int32_t ar9271Modes_9271[][6] = {
     { 0x00009a44, 0x00000000, 0x00000000, 0x000581a8, 0x000581a8, 0x00000000 },
     { 0x00009a44, 0x00000000, 0x00000000, 0x000581a8, 0x000581a8, 0x00000000 },
     { 0x00009a48, 0x00000000, 0x00000000, 0x00058284, 0x00058284, 0x00000000 },
     { 0x00009a48, 0x00000000, 0x00000000, 0x00058284, 0x00058284, 0x00000000 },
     { 0x00009a4c, 0x00000000, 0x00000000, 0x00058288, 0x00058288, 0x00000000 },
     { 0x00009a4c, 0x00000000, 0x00000000, 0x00058288, 0x00058288, 0x00000000 },
-    { 0x00009a50, 0x00000000, 0x00000000, 0x00058220, 0x00058220, 0x00000000 },
+    { 0x00009a50, 0x00000000, 0x00000000, 0x00058224, 0x00058224, 0x00000000 },
     { 0x00009a54, 0x00000000, 0x00000000, 0x00058290, 0x00058290, 0x00000000 },
     { 0x00009a54, 0x00000000, 0x00000000, 0x00058290, 0x00058290, 0x00000000 },
     { 0x00009a58, 0x00000000, 0x00000000, 0x00058300, 0x00058300, 0x00000000 },
     { 0x00009a58, 0x00000000, 0x00000000, 0x00058300, 0x00058300, 0x00000000 },
     { 0x00009a5c, 0x00000000, 0x00000000, 0x00058304, 0x00058304, 0x00000000 },
     { 0x00009a5c, 0x00000000, 0x00000000, 0x00058304, 0x00058304, 0x00000000 },
@@ -6455,8 +6455,8 @@ static const u_int32_t ar9271Modes_9271[][6] = {
     { 0x00009a7c, 0x00000000, 0x00000000, 0x0006870c, 0x0006870c, 0x00000000 },
     { 0x00009a7c, 0x00000000, 0x00000000, 0x0006870c, 0x0006870c, 0x00000000 },
     { 0x00009a80, 0x00000000, 0x00000000, 0x00068780, 0x00068780, 0x00000000 },
     { 0x00009a80, 0x00000000, 0x00000000, 0x00068780, 0x00068780, 0x00000000 },
     { 0x00009a84, 0x00000000, 0x00000000, 0x00068784, 0x00068784, 0x00000000 },
     { 0x00009a84, 0x00000000, 0x00000000, 0x00068784, 0x00068784, 0x00000000 },
-    { 0x00009a88, 0x00000000, 0x00000000, 0x00078b04, 0x00078b04, 0x00000000 },
-    { 0x00009a8c, 0x00000000, 0x00000000, 0x00078b08, 0x00078b08, 0x00000000 },
+    { 0x00009a88, 0x00000000, 0x00000000, 0x00078b00, 0x00078b00, 0x00000000 },
+    { 0x00009a8c, 0x00000000, 0x00000000, 0x00078b04, 0x00078b04, 0x00000000 },
     { 0x00009a90, 0x00000000, 0x00000000, 0x00078b08, 0x00078b08, 0x00000000 },
     { 0x00009a90, 0x00000000, 0x00000000, 0x00078b08, 0x00078b08, 0x00000000 },
     { 0x00009a94, 0x00000000, 0x00000000, 0x00078b0c, 0x00078b0c, 0x00000000 },
     { 0x00009a94, 0x00000000, 0x00000000, 0x00078b0c, 0x00078b0c, 0x00000000 },
     { 0x00009a98, 0x00000000, 0x00000000, 0x00078b80, 0x00078b80, 0x00000000 },
     { 0x00009a98, 0x00000000, 0x00000000, 0x00078b80, 0x00078b80, 0x00000000 },
@@ -6569,7 +6569,7 @@ static const u_int32_t ar9271Modes_9271[][6] = {
     { 0x0000aa44, 0x00000000, 0x00000000, 0x000581a8, 0x000581a8, 0x00000000 },
     { 0x0000aa44, 0x00000000, 0x00000000, 0x000581a8, 0x000581a8, 0x00000000 },
     { 0x0000aa48, 0x00000000, 0x00000000, 0x00058284, 0x00058284, 0x00000000 },
     { 0x0000aa48, 0x00000000, 0x00000000, 0x00058284, 0x00058284, 0x00000000 },
     { 0x0000aa4c, 0x00000000, 0x00000000, 0x00058288, 0x00058288, 0x00000000 },
     { 0x0000aa4c, 0x00000000, 0x00000000, 0x00058288, 0x00058288, 0x00000000 },
-    { 0x0000aa50, 0x00000000, 0x00000000, 0x00058220, 0x00058220, 0x00000000 },
+    { 0x0000aa50, 0x00000000, 0x00000000, 0x00058224, 0x00058224, 0x00000000 },
     { 0x0000aa54, 0x00000000, 0x00000000, 0x00058290, 0x00058290, 0x00000000 },
     { 0x0000aa54, 0x00000000, 0x00000000, 0x00058290, 0x00058290, 0x00000000 },
     { 0x0000aa58, 0x00000000, 0x00000000, 0x00058300, 0x00058300, 0x00000000 },
     { 0x0000aa58, 0x00000000, 0x00000000, 0x00058300, 0x00058300, 0x00000000 },
     { 0x0000aa5c, 0x00000000, 0x00000000, 0x00058304, 0x00058304, 0x00000000 },
     { 0x0000aa5c, 0x00000000, 0x00000000, 0x00058304, 0x00058304, 0x00000000 },
@@ -6583,8 +6583,8 @@ static const u_int32_t ar9271Modes_9271[][6] = {
     { 0x0000aa7c, 0x00000000, 0x00000000, 0x0006870c, 0x0006870c, 0x00000000 },
     { 0x0000aa7c, 0x00000000, 0x00000000, 0x0006870c, 0x0006870c, 0x00000000 },
     { 0x0000aa80, 0x00000000, 0x00000000, 0x00068780, 0x00068780, 0x00000000 },
     { 0x0000aa80, 0x00000000, 0x00000000, 0x00068780, 0x00068780, 0x00000000 },
     { 0x0000aa84, 0x00000000, 0x00000000, 0x00068784, 0x00068784, 0x00000000 },
     { 0x0000aa84, 0x00000000, 0x00000000, 0x00068784, 0x00068784, 0x00000000 },
-    { 0x0000aa88, 0x00000000, 0x00000000, 0x00078b04, 0x00078b04, 0x00000000 },
-    { 0x0000aa8c, 0x00000000, 0x00000000, 0x00078b08, 0x00078b08, 0x00000000 },
+    { 0x0000aa88, 0x00000000, 0x00000000, 0x00078b00, 0x00078b00, 0x00000000 },
+    { 0x0000aa8c, 0x00000000, 0x00000000, 0x00078b04, 0x00078b04, 0x00000000 },
     { 0x0000aa90, 0x00000000, 0x00000000, 0x00078b08, 0x00078b08, 0x00000000 },
     { 0x0000aa90, 0x00000000, 0x00000000, 0x00078b08, 0x00078b08, 0x00000000 },
     { 0x0000aa94, 0x00000000, 0x00000000, 0x00078b0c, 0x00078b0c, 0x00000000 },
     { 0x0000aa94, 0x00000000, 0x00000000, 0x00078b0c, 0x00078b0c, 0x00000000 },
     { 0x0000aa98, 0x00000000, 0x00000000, 0x00078b80, 0x00078b80, 0x00000000 },
     { 0x0000aa98, 0x00000000, 0x00000000, 0x00078b80, 0x00078b80, 0x00000000 },
@@ -6683,25 +6683,6 @@ static const u_int32_t ar9271Modes_9271[][6] = {
     { 0x0000a21c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a },
     { 0x0000a21c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a },
     { 0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108, 0x00000000 },
     { 0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108, 0x00000000 },
     { 0x0000a250, 0x0004f000, 0x0004f000, 0x0004a000, 0x0004a000, 0x0004a000 },
     { 0x0000a250, 0x0004f000, 0x0004f000, 0x0004a000, 0x0004a000, 0x0004a000 },
-    { 0x0000a274, 0x0a21c652, 0x0a21c652, 0x0a218652, 0x0a218652, 0x0a22a652 },
-    { 0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
-    { 0x0000a304, 0x00000000, 0x00000000, 0x00009200, 0x00009200, 0x00000000 },
-    { 0x0000a308, 0x00000000, 0x00000000, 0x00010208, 0x00010208, 0x00000000 },
-    { 0x0000a30c, 0x00000000, 0x00000000, 0x00019608, 0x00019608, 0x00000000 },
-    { 0x0000a310, 0x00000000, 0x00000000, 0x0001e610, 0x0001e610, 0x00000000 },
-    { 0x0000a314, 0x00000000, 0x00000000, 0x0002d6d0, 0x0002d6d0, 0x00000000 },
-    { 0x0000a318, 0x00000000, 0x00000000, 0x00039758, 0x00039758, 0x00000000 },
-    { 0x0000a31c, 0x00000000, 0x00000000, 0x0003b759, 0x0003b759, 0x00000000 },
-    { 0x0000a320, 0x00000000, 0x00000000, 0x0003d75a, 0x0003d75a, 0x00000000 },
-    { 0x0000a324, 0x00000000, 0x00000000, 0x0004175c, 0x0004175c, 0x00000000 },
-    { 0x0000a328, 0x00000000, 0x00000000, 0x0004575e, 0x0004575e, 0x00000000 },
-    { 0x0000a32c, 0x00000000, 0x00000000, 0x0004979f, 0x0004979f, 0x00000000 },
-    { 0x0000a330, 0x00000000, 0x00000000, 0x0004d7df, 0x0004d7df, 0x00000000 },
-    { 0x0000a334, 0x000368de, 0x000368de, 0x000368de, 0x000368de, 0x00000000 },
-    { 0x0000a338, 0x0003891e, 0x0003891e, 0x0003891e, 0x0003891e, 0x00000000 },
-    { 0x0000a33c, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x00000000 },
-    { 0x0000a340, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 },
-    { 0x0000a344, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 },
     { 0x0000a358, 0x7999aa02, 0x7999aa02, 0x7999aa0e, 0x7999aa0e, 0x7999aa0e },
     { 0x0000a358, 0x7999aa02, 0x7999aa02, 0x7999aa0e, 0x7999aa0e, 0x7999aa0e },
 };
 };
 
 
@@ -6879,7 +6860,7 @@ static const u_int32_t ar9271Common_9271[][2] = {
     { 0x00008258, 0x00000000 },
     { 0x00008258, 0x00000000 },
     { 0x0000825c, 0x400000ff },
     { 0x0000825c, 0x400000ff },
     { 0x00008260, 0x00080922 },
     { 0x00008260, 0x00080922 },
-    { 0x00008264, 0x88a00010 },
+    { 0x00008264, 0xa8a00010 },
     { 0x00008270, 0x00000000 },
     { 0x00008270, 0x00000000 },
     { 0x00008274, 0x40000000 },
     { 0x00008274, 0x40000000 },
     { 0x00008278, 0x003e4180 },
     { 0x00008278, 0x003e4180 },
@@ -6910,13 +6891,10 @@ static const u_int32_t ar9271Common_9271[][2] = {
     { 0x00007810, 0x71c0d388 },
     { 0x00007810, 0x71c0d388 },
     { 0x00007814, 0x924934a8 },
     { 0x00007814, 0x924934a8 },
     { 0x0000781c, 0x00000000 },
     { 0x0000781c, 0x00000000 },
-    { 0x00007820, 0x00000c04 },
-    { 0x00007824, 0x00d8abff },
     { 0x00007828, 0x66964300 },
     { 0x00007828, 0x66964300 },
     { 0x0000782c, 0x8db6d961 },
     { 0x0000782c, 0x8db6d961 },
     { 0x00007830, 0x8db6d96c },
     { 0x00007830, 0x8db6d96c },
     { 0x00007834, 0x6140008b },
     { 0x00007834, 0x6140008b },
-    { 0x00007838, 0x00000029 },
     { 0x0000783c, 0x72ee0a72 },
     { 0x0000783c, 0x72ee0a72 },
     { 0x00007840, 0xbbfffffc },
     { 0x00007840, 0xbbfffffc },
     { 0x00007844, 0x000c0db6 },
     { 0x00007844, 0x000c0db6 },
@@ -6929,7 +6907,6 @@ static const u_int32_t ar9271Common_9271[][2] = {
     { 0x00007860, 0x21084210 },
     { 0x00007860, 0x21084210 },
     { 0x00007864, 0xf7d7ffde },
     { 0x00007864, 0xf7d7ffde },
     { 0x00007868, 0xc2034080 },
     { 0x00007868, 0xc2034080 },
-    { 0x0000786c, 0x48609eb4 },
     { 0x00007870, 0x10142c00 },
     { 0x00007870, 0x10142c00 },
     { 0x00009808, 0x00000000 },
     { 0x00009808, 0x00000000 },
     { 0x0000980c, 0xafe68e30 },
     { 0x0000980c, 0xafe68e30 },
@@ -6982,9 +6959,6 @@ static const u_int32_t ar9271Common_9271[][2] = {
     { 0x000099e8, 0x3c466478 },
     { 0x000099e8, 0x3c466478 },
     { 0x000099ec, 0x0cc80caa },
     { 0x000099ec, 0x0cc80caa },
     { 0x000099f0, 0x00000000 },
     { 0x000099f0, 0x00000000 },
-    { 0x0000a1f4, 0x00000000 },
-    { 0x0000a1f8, 0x71733d01 },
-    { 0x0000a1fc, 0xd0ad5c12 },
     { 0x0000a208, 0x803e68c8 },
     { 0x0000a208, 0x803e68c8 },
     { 0x0000a210, 0x4080a333 },
     { 0x0000a210, 0x4080a333 },
     { 0x0000a214, 0x00206c10 },
     { 0x0000a214, 0x00206c10 },
@@ -7004,13 +6978,9 @@ static const u_int32_t ar9271Common_9271[][2] = {
     { 0x0000a260, 0xdfa90f01 },
     { 0x0000a260, 0xdfa90f01 },
     { 0x0000a268, 0x00000000 },
     { 0x0000a268, 0x00000000 },
     { 0x0000a26c, 0x0ebae9e6 },
     { 0x0000a26c, 0x0ebae9e6 },
-    { 0x0000a278, 0x3bdef7bd },
-    { 0x0000a27c, 0x050e83bd },
     { 0x0000a388, 0x0c000000 },
     { 0x0000a388, 0x0c000000 },
     { 0x0000a38c, 0x20202020 },
     { 0x0000a38c, 0x20202020 },
     { 0x0000a390, 0x20202020 },
     { 0x0000a390, 0x20202020 },
-    { 0x0000a394, 0x3bdef7bd },
-    { 0x0000a398, 0x000003bd },
     { 0x0000a39c, 0x00000001 },
     { 0x0000a39c, 0x00000001 },
     { 0x0000a3a0, 0x00000000 },
     { 0x0000a3a0, 0x00000000 },
     { 0x0000a3a4, 0x00000000 },
     { 0x0000a3a4, 0x00000000 },
@@ -7025,8 +6995,6 @@ static const u_int32_t ar9271Common_9271[][2] = {
     { 0x0000a3cc, 0x20202020 },
     { 0x0000a3cc, 0x20202020 },
     { 0x0000a3d0, 0x20202020 },
     { 0x0000a3d0, 0x20202020 },
     { 0x0000a3d4, 0x20202020 },
     { 0x0000a3d4, 0x20202020 },
-    { 0x0000a3dc, 0x3bdef7bd },
-    { 0x0000a3e0, 0x000003bd },
     { 0x0000a3e4, 0x00000000 },
     { 0x0000a3e4, 0x00000000 },
     { 0x0000a3e8, 0x18c43433 },
     { 0x0000a3e8, 0x18c43433 },
     { 0x0000a3ec, 0x00f70081 },
     { 0x0000a3ec, 0x00f70081 },
@@ -7046,7 +7014,102 @@ static const u_int32_t ar9271Common_9271[][2] = {
     { 0x0000d384, 0xf3307ff0 },
     { 0x0000d384, 0xf3307ff0 },
 };
 };
 
 
+static const u_int32_t ar9271Common_normal_cck_fir_coeff_9271[][2] = {
+    { 0x0000a1f4, 0x00fffeff },
+    { 0x0000a1f8, 0x00f5f9ff },
+    { 0x0000a1fc, 0xb79f6427 },
+};
+
+static const u_int32_t ar9271Common_japan_2484_cck_fir_coeff_9271[][2] = {
+    { 0x0000a1f4, 0x00000000 },
+    { 0x0000a1f8, 0xefff0301 },
+    { 0x0000a1fc, 0xca9228ee },
+};
+
 static const u_int32_t ar9271Modes_9271_1_0_only[][6] = {
 static const u_int32_t ar9271Modes_9271_1_0_only[][6] = {
     { 0x00009910, 0x30002311, 0x30002311, 0x30002311, 0x30002311, 0x30002311 },
     { 0x00009910, 0x30002311, 0x30002311, 0x30002311, 0x30002311, 0x30002311 },
     { 0x00009828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001 },
     { 0x00009828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001 },
 };
 };
+
+static const u_int32_t ar9271Modes_9271_ANI_reg[][6] = {
+    { 0x00009850, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2 },
+    { 0x0000985c, 0x3139605e, 0x3139605e, 0x3137605e, 0x3137605e, 0x3139605e },
+    { 0x00009858, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e },
+    { 0x0000986c, 0x06903881, 0x06903881, 0x06903881, 0x06903881, 0x06903881 },
+    { 0x00009868, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0 },
+    { 0x0000a208, 0x803e68c8, 0x803e68c8, 0x803e68c8, 0x803e68c8, 0x803e68c8 },
+    { 0x00009924, 0xd00a8007, 0xd00a8007, 0xd00a800d, 0xd00a800d, 0xd00a800d },
+    { 0x000099c0, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4 },
+};
+
+static const u_int32_t ar9271Modes_normal_power_tx_gain_9271[][6] = {
+    { 0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+    { 0x0000a304, 0x00000000, 0x00000000, 0x00009200, 0x00009200, 0x00000000 },
+    { 0x0000a308, 0x00000000, 0x00000000, 0x00010208, 0x00010208, 0x00000000 },
+    { 0x0000a30c, 0x00000000, 0x00000000, 0x00019608, 0x00019608, 0x00000000 },
+    { 0x0000a310, 0x00000000, 0x00000000, 0x0001e610, 0x0001e610, 0x00000000 },
+    { 0x0000a314, 0x00000000, 0x00000000, 0x0002d6d0, 0x0002d6d0, 0x00000000 },
+    { 0x0000a318, 0x00000000, 0x00000000, 0x00039758, 0x00039758, 0x00000000 },
+    { 0x0000a31c, 0x00000000, 0x00000000, 0x0003b759, 0x0003b759, 0x00000000 },
+    { 0x0000a320, 0x00000000, 0x00000000, 0x0003d75a, 0x0003d75a, 0x00000000 },
+    { 0x0000a324, 0x00000000, 0x00000000, 0x0004175c, 0x0004175c, 0x00000000 },
+    { 0x0000a328, 0x00000000, 0x00000000, 0x0004575e, 0x0004575e, 0x00000000 },
+    { 0x0000a32c, 0x00000000, 0x00000000, 0x0004979f, 0x0004979f, 0x00000000 },
+    { 0x0000a330, 0x00000000, 0x00000000, 0x0004d7df, 0x0004d7df, 0x00000000 },
+    { 0x0000a334, 0x000368de, 0x000368de, 0x000368de, 0x000368de, 0x00000000 },
+    { 0x0000a338, 0x0003891e, 0x0003891e, 0x0003891e, 0x0003891e, 0x00000000 },
+    { 0x0000a33c, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x00000000 },
+    { 0x0000a340, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 },
+    { 0x0000a344, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 },
+    { 0x0000a348, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 },
+    { 0x0000a34c, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 },
+    { 0x0000a350, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 },
+    { 0x0000a354, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 },
+    { 0x00007838, 0x00000029, 0x00000029, 0x00000029, 0x00000029, 0x00000029 },
+    { 0x00007824, 0x00d8abff, 0x00d8abff, 0x00d8abff, 0x00d8abff, 0x00d8abff },
+    { 0x0000786c, 0x48609eb4, 0x48609eb4, 0x48609eb4, 0x48609eb4, 0x48609eb4 },
+    { 0x00007820, 0x00000c04, 0x00000c04, 0x00000c04, 0x00000c04, 0x00000c04 },
+    { 0x0000a274, 0x0a21c652, 0x0a21c652, 0x0a218652, 0x0a218652, 0x0a22a652 },
+    { 0x0000a278, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd },
+    { 0x0000a27c, 0x050e83bd, 0x050e83bd, 0x050e83bd, 0x050e83bd, 0x050e83bd },
+    { 0x0000a394, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd },
+    { 0x0000a398, 0x000003bd, 0x000003bd, 0x000003bd, 0x000003bd, 0x000003bd },
+    { 0x0000a3dc, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd },
+    { 0x0000a3e0, 0x000003bd, 0x000003bd, 0x000003bd, 0x000003bd, 0x000003bd },
+};
+
+static const u_int32_t ar9271Modes_high_power_tx_gain_9271[][6] = {
+    { 0x0000a300, 0x00000000, 0x00000000, 0x00010000, 0x00010000, 0x00000000 },
+    { 0x0000a304, 0x00000000, 0x00000000, 0x00016200, 0x00016200, 0x00000000 },
+    { 0x0000a308, 0x00000000, 0x00000000, 0x00018201, 0x00018201, 0x00000000 },
+    { 0x0000a30c, 0x00000000, 0x00000000, 0x0001b240, 0x0001b240, 0x00000000 },
+    { 0x0000a310, 0x00000000, 0x00000000, 0x0001d241, 0x0001d241, 0x00000000 },
+    { 0x0000a314, 0x00000000, 0x00000000, 0x0001f600, 0x0001f600, 0x00000000 },
+    { 0x0000a318, 0x00000000, 0x00000000, 0x00022800, 0x00022800, 0x00000000 },
+    { 0x0000a31c, 0x00000000, 0x00000000, 0x00026802, 0x00026802, 0x00000000 },
+    { 0x0000a320, 0x00000000, 0x00000000, 0x0002b805, 0x0002b805, 0x00000000 },
+    { 0x0000a324, 0x00000000, 0x00000000, 0x0002ea41, 0x0002ea41, 0x00000000 },
+    { 0x0000a328, 0x00000000, 0x00000000, 0x00038b00, 0x00038b00, 0x00000000 },
+    { 0x0000a32c, 0x00000000, 0x00000000, 0x0003ab40, 0x0003ab40, 0x00000000 },
+    { 0x0000a330, 0x00000000, 0x00000000, 0x0003cd80, 0x0003cd80, 0x00000000 },
+    { 0x0000a334, 0x000368de, 0x000368de, 0x000368de, 0x000368de, 0x00000000 },
+    { 0x0000a338, 0x0003891e, 0x0003891e, 0x0003891e, 0x0003891e, 0x00000000 },
+    { 0x0000a33c, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x00000000 },
+    { 0x0000a340, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 },
+    { 0x0000a344, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 },
+    { 0x0000a348, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 },
+    { 0x0000a34c, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 },
+    { 0x0000a350, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 },
+    { 0x0000a354, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 },
+    { 0x00007838, 0x0000002b, 0x0000002b, 0x0000002b, 0x0000002b, 0x0000002b },
+    { 0x00007824, 0x00d8a7ff, 0x00d8a7ff, 0x00d8a7ff, 0x00d8a7ff, 0x00d8a7ff },
+    { 0x0000786c, 0x08609eb6, 0x08609eb6, 0x08609eba, 0x08609eba, 0x08609eb6 },
+    { 0x00007820, 0x00000c00, 0x00000c00, 0x00000c00, 0x00000c00, 0x00000c00 },
+    { 0x0000a274, 0x0a22a652, 0x0a22a652, 0x0a212652, 0x0a212652, 0x0a22a652 },
+    { 0x0000a278, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7 },
+    { 0x0000a27c, 0x05018063, 0x05038063, 0x05018063, 0x05018063, 0x05018063 },
+    { 0x0000a394, 0x06318c63, 0x06318c63, 0x06318c63, 0x06318c63, 0x06318c63 },
+    { 0x0000a398, 0x00000063, 0x00000063, 0x00000063, 0x00000063, 0x00000063 },
+    { 0x0000a3dc, 0x06318c63, 0x06318c63, 0x06318c63, 0x06318c63, 0x06318c63 },
+    { 0x0000a3e0, 0x00000063, 0x00000063, 0x00000063, 0x00000063, 0x00000063 },
+};

+ 1 - 1
drivers/net/wireless/ath/ath9k/mac.c

@@ -351,7 +351,7 @@ void ath9k_hw_set11n_txdesc(struct ath_hw *ah, struct ath_desc *ds,
 
 
 	ads->ds_ctl6 = SM(keyType, AR_EncrType);
 	ads->ds_ctl6 = SM(keyType, AR_EncrType);
 
 
-	if (AR_SREV_9285(ah)) {
+	if (AR_SREV_9285(ah) || AR_SREV_9271(ah)) {
 		ads->ds_ctl8 = 0;
 		ads->ds_ctl8 = 0;
 		ads->ds_ctl9 = 0;
 		ads->ds_ctl9 = 0;
 		ads->ds_ctl10 = 0;
 		ads->ds_ctl10 = 0;

+ 26 - 0
drivers/net/wireless/ath/ath9k/mac.h

@@ -150,6 +150,32 @@ struct ath_rx_status {
 	u32 evm2;
 	u32 evm2;
 };
 };
 
 
+struct ath_htc_rx_status {
+	u64 rs_tstamp;
+	u16 rs_datalen;
+	u8 rs_status;
+	u8 rs_phyerr;
+	int8_t rs_rssi;
+	int8_t rs_rssi_ctl0;
+	int8_t rs_rssi_ctl1;
+	int8_t rs_rssi_ctl2;
+	int8_t rs_rssi_ext0;
+	int8_t rs_rssi_ext1;
+	int8_t rs_rssi_ext2;
+	u8 rs_keyix;
+	u8 rs_rate;
+	u8 rs_antenna;
+	u8 rs_more;
+	u8 rs_isaggr;
+	u8 rs_moreaggr;
+	u8 rs_num_delims;
+	u8 rs_flags;
+	u8 rs_dummy;
+	u32 evm0;
+	u32 evm1;
+	u32 evm2;
+};
+
 #define ATH9K_RXERR_CRC           0x01
 #define ATH9K_RXERR_CRC           0x01
 #define ATH9K_RXERR_PHY           0x02
 #define ATH9K_RXERR_PHY           0x02
 #define ATH9K_RXERR_FIFO          0x04
 #define ATH9K_RXERR_FIFO          0x04

+ 2 - 2
drivers/net/wireless/ath/ath9k/rc.h

@@ -110,8 +110,8 @@ struct ath_rate_table {
 	int rate_cnt;
 	int rate_cnt;
 	int mcs_start;
 	int mcs_start;
 	struct {
 	struct {
-		int valid;
-		int valid_single_stream;
+		u8 valid;
+		u8 valid_single_stream;
 		u8 phy;
 		u8 phy;
 		u32 ratekbps;
 		u32 ratekbps;
 		u32 user_ratekbps;
 		u32 user_ratekbps;

+ 3 - 0
drivers/net/wireless/ath/ath9k/reg.h

@@ -940,6 +940,7 @@ enum {
 #define AR928X_NUM_GPIO                          10
 #define AR928X_NUM_GPIO                          10
 #define AR9285_NUM_GPIO                          12
 #define AR9285_NUM_GPIO                          12
 #define AR9287_NUM_GPIO                          11
 #define AR9287_NUM_GPIO                          11
+#define AR9271_NUM_GPIO                          16
 
 
 #define AR_GPIO_IN_OUT                           0x4048
 #define AR_GPIO_IN_OUT                           0x4048
 #define AR_GPIO_IN_VAL                           0x0FFFC000
 #define AR_GPIO_IN_VAL                           0x0FFFC000
@@ -950,6 +951,8 @@ enum {
 #define AR9285_GPIO_IN_VAL_S                     12
 #define AR9285_GPIO_IN_VAL_S                     12
 #define AR9287_GPIO_IN_VAL                       0x003FF800
 #define AR9287_GPIO_IN_VAL                       0x003FF800
 #define AR9287_GPIO_IN_VAL_S                     11
 #define AR9287_GPIO_IN_VAL_S                     11
+#define AR9271_GPIO_IN_VAL                       0xFFFF0000
+#define AR9271_GPIO_IN_VAL_S                     16
 
 
 #define AR_GPIO_OE_OUT                           0x404c
 #define AR_GPIO_OE_OUT                           0x404c
 #define AR_GPIO_OE_OUT_DRV                       0x3
 #define AR_GPIO_OE_OUT_DRV                       0x3

+ 319 - 0
drivers/net/wireless/ath/ath9k/wmi.c

@@ -0,0 +1,319 @@
+/*
+ * Copyright (c) 2010 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "htc.h"
+
+static const char *wmi_cmd_to_name(enum wmi_cmd_id wmi_cmd)
+{
+	switch (wmi_cmd) {
+	case WMI_ECHO_CMDID:
+		return "WMI_ECHO_CMDID";
+	case WMI_ACCESS_MEMORY_CMDID:
+		return "WMI_ACCESS_MEMORY_CMDID";
+	case WMI_DISABLE_INTR_CMDID:
+		return "WMI_DISABLE_INTR_CMDID";
+	case WMI_ENABLE_INTR_CMDID:
+		return "WMI_ENABLE_INTR_CMDID";
+	case WMI_RX_LINK_CMDID:
+		return "WMI_RX_LINK_CMDID";
+	case WMI_ATH_INIT_CMDID:
+		return "WMI_ATH_INIT_CMDID";
+	case WMI_ABORT_TXQ_CMDID:
+		return "WMI_ABORT_TXQ_CMDID";
+	case WMI_STOP_TX_DMA_CMDID:
+		return "WMI_STOP_TX_DMA_CMDID";
+	case WMI_STOP_DMA_RECV_CMDID:
+		return "WMI_STOP_DMA_RECV_CMDID";
+	case WMI_ABORT_TX_DMA_CMDID:
+		return "WMI_ABORT_TX_DMA_CMDID";
+	case WMI_DRAIN_TXQ_CMDID:
+		return "WMI_DRAIN_TXQ_CMDID";
+	case WMI_DRAIN_TXQ_ALL_CMDID:
+		return "WMI_DRAIN_TXQ_ALL_CMDID";
+	case WMI_START_RECV_CMDID:
+		return "WMI_START_RECV_CMDID";
+	case WMI_STOP_RECV_CMDID:
+		return "WMI_STOP_RECV_CMDID";
+	case WMI_FLUSH_RECV_CMDID:
+		return "WMI_FLUSH_RECV_CMDID";
+	case WMI_SET_MODE_CMDID:
+		return "WMI_SET_MODE_CMDID";
+	case WMI_RESET_CMDID:
+		return "WMI_RESET_CMDID";
+	case WMI_NODE_CREATE_CMDID:
+		return "WMI_NODE_CREATE_CMDID";
+	case WMI_NODE_REMOVE_CMDID:
+		return "WMI_NODE_REMOVE_CMDID";
+	case WMI_VAP_REMOVE_CMDID:
+		return "WMI_VAP_REMOVE_CMDID";
+	case WMI_VAP_CREATE_CMDID:
+		return "WMI_VAP_CREATE_CMDID";
+	case WMI_BEACON_UPDATE_CMDID:
+		return "WMI_BEACON_UPDATE_CMDID";
+	case WMI_REG_READ_CMDID:
+		return "WMI_REG_READ_CMDID";
+	case WMI_REG_WRITE_CMDID:
+		return "WMI_REG_WRITE_CMDID";
+	case WMI_RC_STATE_CHANGE_CMDID:
+		return "WMI_RC_STATE_CHANGE_CMDID";
+	case WMI_RC_RATE_UPDATE_CMDID:
+		return "WMI_RC_RATE_UPDATE_CMDID";
+	case WMI_DEBUG_INFO_CMDID:
+		return "WMI_DEBUG_INFO_CMDID";
+	case WMI_HOST_ATTACH:
+		return "WMI_HOST_ATTACH";
+	case WMI_TARGET_IC_UPDATE_CMDID:
+		return "WMI_TARGET_IC_UPDATE_CMDID";
+	case WMI_TGT_STATS_CMDID:
+		return "WMI_TGT_STATS_CMDID";
+	case WMI_TX_AGGR_ENABLE_CMDID:
+		return "WMI_TX_AGGR_ENABLE_CMDID";
+	case WMI_TGT_DETACH_CMDID:
+		return "WMI_TGT_DETACH_CMDID";
+	case WMI_TGT_TXQ_ENABLE_CMDID:
+		return "WMI_TGT_TXQ_ENABLE_CMDID";
+	}
+
+	return "Bogus";
+}
+
+struct wmi *ath9k_init_wmi(struct ath9k_htc_priv *priv)
+{
+	struct wmi *wmi;
+
+	wmi = kzalloc(sizeof(struct wmi), GFP_KERNEL);
+	if (!wmi)
+		return NULL;
+
+	wmi->drv_priv = priv;
+	wmi->stopped = false;
+	mutex_init(&wmi->op_mutex);
+	init_completion(&wmi->cmd_wait);
+
+	return wmi;
+}
+
+void ath9k_deinit_wmi(struct ath9k_htc_priv *priv)
+{
+	struct wmi *wmi = priv->wmi;
+
+	mutex_lock(&wmi->op_mutex);
+	wmi->stopped = true;
+	mutex_unlock(&wmi->op_mutex);
+
+	kfree(priv->wmi);
+}
+
+void ath9k_wmi_tasklet(unsigned long data)
+{
+	struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *)data;
+	struct ath_common *common = ath9k_hw_common(priv->ah);
+	struct wmi_cmd_hdr *hdr;
+	struct wmi_swba *swba_hdr;
+	enum wmi_event_id event;
+	struct sk_buff *skb;
+	void *wmi_event;
+	unsigned long flags;
+#ifdef CONFIG_ATH9K_HTC_DEBUGFS
+	u32 txrate;
+#endif
+
+	spin_lock_irqsave(&priv->wmi->wmi_lock, flags);
+	skb = priv->wmi->wmi_skb;
+	spin_unlock_irqrestore(&priv->wmi->wmi_lock, flags);
+
+	hdr = (struct wmi_cmd_hdr *) skb->data;
+	event = be16_to_cpu(hdr->command_id);
+	wmi_event = skb_pull(skb, sizeof(struct wmi_cmd_hdr));
+
+	ath_print(common, ATH_DBG_WMI,
+		  "WMI Event: 0x%x\n", event);
+
+	switch (event) {
+	case WMI_TGT_RDY_EVENTID:
+		break;
+	case WMI_SWBA_EVENTID:
+		swba_hdr = (struct wmi_swba *) wmi_event;
+		ath9k_htc_swba(priv, swba_hdr->beacon_pending);
+		break;
+	case WMI_FATAL_EVENTID:
+		break;
+	case WMI_TXTO_EVENTID:
+		break;
+	case WMI_BMISS_EVENTID:
+		break;
+	case WMI_WLAN_TXCOMP_EVENTID:
+		break;
+	case WMI_DELBA_EVENTID:
+		break;
+	case WMI_TXRATE_EVENTID:
+#ifdef CONFIG_ATH9K_HTC_DEBUGFS
+		txrate = ((struct wmi_event_txrate *)wmi_event)->txrate;
+		priv->debug.txrate = be32_to_cpu(txrate);
+#endif
+		break;
+	default:
+		break;
+	}
+
+	dev_kfree_skb_any(skb);
+}
+
+static void ath9k_wmi_rsp_callback(struct wmi *wmi, struct sk_buff *skb)
+{
+	skb_pull(skb, sizeof(struct wmi_cmd_hdr));
+
+	if (wmi->cmd_rsp_buf != NULL && wmi->cmd_rsp_len != 0)
+		memcpy(wmi->cmd_rsp_buf, skb->data, wmi->cmd_rsp_len);
+
+	complete(&wmi->cmd_wait);
+}
+
+static void ath9k_wmi_ctrl_rx(void *priv, struct sk_buff *skb,
+			      enum htc_endpoint_id epid)
+{
+	struct wmi *wmi = (struct wmi *) priv;
+	struct wmi_cmd_hdr *hdr;
+	u16 cmd_id;
+
+	if (unlikely(wmi->stopped))
+		goto free_skb;
+
+	hdr = (struct wmi_cmd_hdr *) skb->data;
+	cmd_id = be16_to_cpu(hdr->command_id);
+
+	if (cmd_id & 0x1000) {
+		spin_lock(&wmi->wmi_lock);
+		wmi->wmi_skb = skb;
+		spin_unlock(&wmi->wmi_lock);
+		tasklet_schedule(&wmi->drv_priv->wmi_tasklet);
+		return;
+	}
+
+	/* WMI command response */
+	ath9k_wmi_rsp_callback(wmi, skb);
+
+free_skb:
+	dev_kfree_skb_any(skb);
+}
+
+static void ath9k_wmi_ctrl_tx(void *priv, struct sk_buff *skb,
+			      enum htc_endpoint_id epid, bool txok)
+{
+	dev_kfree_skb_any(skb);
+}
+
+int ath9k_wmi_connect(struct htc_target *htc, struct wmi *wmi,
+		      enum htc_endpoint_id *wmi_ctrl_epid)
+{
+	struct htc_service_connreq connect;
+	int ret;
+
+	wmi->htc = htc;
+
+	memset(&connect, 0, sizeof(connect));
+
+	connect.ep_callbacks.priv = wmi;
+	connect.ep_callbacks.tx = ath9k_wmi_ctrl_tx;
+	connect.ep_callbacks.rx = ath9k_wmi_ctrl_rx;
+	connect.service_id = WMI_CONTROL_SVC;
+
+	ret = htc_connect_service(htc, &connect, &wmi->ctrl_epid);
+	if (ret)
+		return ret;
+
+	*wmi_ctrl_epid = wmi->ctrl_epid;
+
+	return 0;
+}
+
+static int ath9k_wmi_cmd_issue(struct wmi *wmi,
+			       struct sk_buff *skb,
+			       enum wmi_cmd_id cmd, u16 len)
+{
+	struct wmi_cmd_hdr *hdr;
+
+	hdr = (struct wmi_cmd_hdr *) skb_push(skb, sizeof(struct wmi_cmd_hdr));
+	hdr->command_id = cpu_to_be16(cmd);
+	hdr->seq_no = cpu_to_be16(++wmi->tx_seq_id);
+
+	return htc_send(wmi->htc, skb, wmi->ctrl_epid, NULL);
+}
+
+int ath9k_wmi_cmd(struct wmi *wmi, enum wmi_cmd_id cmd_id,
+		  u8 *cmd_buf, u32 cmd_len,
+		  u8 *rsp_buf, u32 rsp_len,
+		  u32 timeout)
+{
+	struct ath_hw *ah = wmi->drv_priv->ah;
+	struct ath_common *common = ath9k_hw_common(ah);
+	u16 headroom = sizeof(struct htc_frame_hdr) +
+		       sizeof(struct wmi_cmd_hdr);
+	struct sk_buff *skb;
+	u8 *data;
+	int time_left, ret = 0;
+
+	if (!wmi)
+		return -EINVAL;
+
+	skb = dev_alloc_skb(headroom + cmd_len);
+	if (!skb)
+		return -ENOMEM;
+
+	skb_reserve(skb, headroom);
+
+	if (cmd_len != 0 && cmd_buf != NULL) {
+		data = (u8 *) skb_put(skb, cmd_len);
+		memcpy(data, cmd_buf, cmd_len);
+	}
+
+	mutex_lock(&wmi->op_mutex);
+
+	/* check if wmi stopped flag is set */
+	if (unlikely(wmi->stopped)) {
+		ret = -EPROTO;
+		goto out;
+	}
+
+	/* record the rsp buffer and length */
+	wmi->cmd_rsp_buf = rsp_buf;
+	wmi->cmd_rsp_len = rsp_len;
+
+	ret = ath9k_wmi_cmd_issue(wmi, skb, cmd_id, cmd_len);
+	if (ret)
+		goto out;
+
+	time_left = wait_for_completion_timeout(&wmi->cmd_wait, timeout);
+	if (!time_left) {
+		ath_print(common, ATH_DBG_WMI,
+			  "Timeout waiting for WMI command: %s\n",
+			  wmi_cmd_to_name(cmd_id));
+		mutex_unlock(&wmi->op_mutex);
+		return -ETIMEDOUT;
+	}
+
+	mutex_unlock(&wmi->op_mutex);
+
+	return 0;
+
+out:
+	ath_print(common, ATH_DBG_WMI,
+		  "WMI failure for: %s\n", wmi_cmd_to_name(cmd_id));
+	mutex_unlock(&wmi->op_mutex);
+	dev_kfree_skb_any(skb);
+
+	return ret;
+}

+ 126 - 0
drivers/net/wireless/ath/ath9k/wmi.h

@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2010 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef WMI_H
+#define WMI_H
+
+
+struct wmi_event_txrate {
+	u32 txrate;
+	struct {
+		u8 rssi_thresh;
+		u8 per;
+	} rc_stats;
+} __packed;
+
+struct wmi_cmd_hdr {
+	u16 command_id;
+	u16 seq_no;
+} __packed;
+
+struct wmi_swba {
+	u8 beacon_pending;
+} __packed;
+
+enum wmi_cmd_id {
+	WMI_ECHO_CMDID = 0x0001,
+	WMI_ACCESS_MEMORY_CMDID,
+
+	/* Commands to Target */
+	WMI_DISABLE_INTR_CMDID,
+	WMI_ENABLE_INTR_CMDID,
+	WMI_RX_LINK_CMDID,
+	WMI_ATH_INIT_CMDID,
+	WMI_ABORT_TXQ_CMDID,
+	WMI_STOP_TX_DMA_CMDID,
+	WMI_STOP_DMA_RECV_CMDID,
+	WMI_ABORT_TX_DMA_CMDID,
+	WMI_DRAIN_TXQ_CMDID,
+	WMI_DRAIN_TXQ_ALL_CMDID,
+	WMI_START_RECV_CMDID,
+	WMI_STOP_RECV_CMDID,
+	WMI_FLUSH_RECV_CMDID,
+	WMI_SET_MODE_CMDID,
+	WMI_RESET_CMDID,
+	WMI_NODE_CREATE_CMDID,
+	WMI_NODE_REMOVE_CMDID,
+	WMI_VAP_REMOVE_CMDID,
+	WMI_VAP_CREATE_CMDID,
+	WMI_BEACON_UPDATE_CMDID,
+	WMI_REG_READ_CMDID,
+	WMI_REG_WRITE_CMDID,
+	WMI_RC_STATE_CHANGE_CMDID,
+	WMI_RC_RATE_UPDATE_CMDID,
+	WMI_DEBUG_INFO_CMDID,
+	WMI_HOST_ATTACH,
+	WMI_TARGET_IC_UPDATE_CMDID,
+	WMI_TGT_STATS_CMDID,
+	WMI_TX_AGGR_ENABLE_CMDID,
+	WMI_TGT_DETACH_CMDID,
+	WMI_TGT_TXQ_ENABLE_CMDID,
+};
+
+enum wmi_event_id {
+	WMI_TGT_RDY_EVENTID = 0x1001,
+	WMI_SWBA_EVENTID,
+	WMI_FATAL_EVENTID,
+	WMI_TXTO_EVENTID,
+	WMI_BMISS_EVENTID,
+	WMI_WLAN_TXCOMP_EVENTID,
+	WMI_DELBA_EVENTID,
+	WMI_TXRATE_EVENTID,
+};
+
+struct wmi {
+	struct ath9k_htc_priv *drv_priv;
+	struct htc_target *htc;
+	enum htc_endpoint_id ctrl_epid;
+	struct mutex op_mutex;
+	struct completion cmd_wait;
+	u16 tx_seq_id;
+	u8 *cmd_rsp_buf;
+	u32 cmd_rsp_len;
+	bool stopped;
+
+	struct sk_buff *wmi_skb;
+	spinlock_t wmi_lock;
+};
+
+struct wmi *ath9k_init_wmi(struct ath9k_htc_priv *priv);
+void ath9k_deinit_wmi(struct ath9k_htc_priv *priv);
+int ath9k_wmi_connect(struct htc_target *htc, struct wmi *wmi,
+		      enum htc_endpoint_id *wmi_ctrl_epid);
+int ath9k_wmi_cmd(struct wmi *wmi, enum wmi_cmd_id cmd_id,
+		  u8 *cmd_buf, u32 cmd_len,
+		  u8 *rsp_buf, u32 rsp_len,
+		  u32 timeout);
+void ath9k_wmi_tasklet(unsigned long data);
+
+#define WMI_CMD(_wmi_cmd)						\
+	do {								\
+		ret = ath9k_wmi_cmd(priv->wmi, _wmi_cmd, NULL, 0,	\
+				    (u8 *) &cmd_rsp,			\
+				    sizeof(cmd_rsp), HZ);		\
+	} while (0)
+
+#define WMI_CMD_BUF(_wmi_cmd, _buf)					\
+	do {								\
+		ret = ath9k_wmi_cmd(priv->wmi, _wmi_cmd,		\
+				    (u8 *) _buf, sizeof(*_buf),		\
+				    &cmd_rsp, sizeof(cmd_rsp), HZ);	\
+	} while (0)
+
+#endif /* WMI_H */

+ 1 - 0
drivers/net/wireless/ath/debug.h

@@ -59,6 +59,7 @@ enum ATH_DEBUG {
 	ATH_DBG_PS		= 0x00000800,
 	ATH_DBG_PS		= 0x00000800,
 	ATH_DBG_HWTIMER		= 0x00001000,
 	ATH_DBG_HWTIMER		= 0x00001000,
 	ATH_DBG_BTCOEX		= 0x00002000,
 	ATH_DBG_BTCOEX		= 0x00002000,
+	ATH_DBG_WMI		= 0x00004000,
 	ATH_DBG_ANY		= 0xffffffff
 	ATH_DBG_ANY		= 0xffffffff
 };
 };
 
 

+ 2 - 3
drivers/net/wireless/b43/main.c

@@ -4348,11 +4348,10 @@ static int b43_wireless_core_init(struct b43_wldev *dev)
 	b43_set_phytxctl_defaults(dev);
 	b43_set_phytxctl_defaults(dev);
 
 
 	/* Minimum Contention Window */
 	/* Minimum Contention Window */
-	if (phy->type == B43_PHYTYPE_B) {
+	if (phy->type == B43_PHYTYPE_B)
 		b43_shm_write16(dev, B43_SHM_SCRATCH, B43_SHM_SC_MINCONT, 0x1F);
 		b43_shm_write16(dev, B43_SHM_SCRATCH, B43_SHM_SC_MINCONT, 0x1F);
-	} else {
+	else
 		b43_shm_write16(dev, B43_SHM_SCRATCH, B43_SHM_SC_MINCONT, 0xF);
 		b43_shm_write16(dev, B43_SHM_SCRATCH, B43_SHM_SC_MINCONT, 0xF);
-	}
 	/* Maximum Contention Window */
 	/* Maximum Contention Window */
 	b43_shm_write16(dev, B43_SHM_SCRATCH, B43_SHM_SC_MAXCONT, 0x3FF);
 	b43_shm_write16(dev, B43_SHM_SCRATCH, B43_SHM_SC_MAXCONT, 0x3FF);
 
 

+ 41 - 42
drivers/net/wireless/ipw2x00/ipw2200.c

@@ -9995,49 +9995,48 @@ static int ipw_wx_sw_reset(struct net_device *dev,
 }
 }
 
 
 /* Rebase the WE IOCTLs to zero for the handler array */
 /* Rebase the WE IOCTLs to zero for the handler array */
-#define IW_IOCTL(x) [(x)-SIOCSIWCOMMIT]
 static iw_handler ipw_wx_handlers[] = {
 static iw_handler ipw_wx_handlers[] = {
-	IW_IOCTL(SIOCGIWNAME) = (iw_handler) cfg80211_wext_giwname,
-	IW_IOCTL(SIOCSIWFREQ) = ipw_wx_set_freq,
-	IW_IOCTL(SIOCGIWFREQ) = ipw_wx_get_freq,
-	IW_IOCTL(SIOCSIWMODE) = ipw_wx_set_mode,
-	IW_IOCTL(SIOCGIWMODE) = ipw_wx_get_mode,
-	IW_IOCTL(SIOCSIWSENS) = ipw_wx_set_sens,
-	IW_IOCTL(SIOCGIWSENS) = ipw_wx_get_sens,
-	IW_IOCTL(SIOCGIWRANGE) = ipw_wx_get_range,
-	IW_IOCTL(SIOCSIWAP) = ipw_wx_set_wap,
-	IW_IOCTL(SIOCGIWAP) = ipw_wx_get_wap,
-	IW_IOCTL(SIOCSIWSCAN) = ipw_wx_set_scan,
-	IW_IOCTL(SIOCGIWSCAN) = ipw_wx_get_scan,
-	IW_IOCTL(SIOCSIWESSID) = ipw_wx_set_essid,
-	IW_IOCTL(SIOCGIWESSID) = ipw_wx_get_essid,
-	IW_IOCTL(SIOCSIWNICKN) = ipw_wx_set_nick,
-	IW_IOCTL(SIOCGIWNICKN) = ipw_wx_get_nick,
-	IW_IOCTL(SIOCSIWRATE) = ipw_wx_set_rate,
-	IW_IOCTL(SIOCGIWRATE) = ipw_wx_get_rate,
-	IW_IOCTL(SIOCSIWRTS) = ipw_wx_set_rts,
-	IW_IOCTL(SIOCGIWRTS) = ipw_wx_get_rts,
-	IW_IOCTL(SIOCSIWFRAG) = ipw_wx_set_frag,
-	IW_IOCTL(SIOCGIWFRAG) = ipw_wx_get_frag,
-	IW_IOCTL(SIOCSIWTXPOW) = ipw_wx_set_txpow,
-	IW_IOCTL(SIOCGIWTXPOW) = ipw_wx_get_txpow,
-	IW_IOCTL(SIOCSIWRETRY) = ipw_wx_set_retry,
-	IW_IOCTL(SIOCGIWRETRY) = ipw_wx_get_retry,
-	IW_IOCTL(SIOCSIWENCODE) = ipw_wx_set_encode,
-	IW_IOCTL(SIOCGIWENCODE) = ipw_wx_get_encode,
-	IW_IOCTL(SIOCSIWPOWER) = ipw_wx_set_power,
-	IW_IOCTL(SIOCGIWPOWER) = ipw_wx_get_power,
-	IW_IOCTL(SIOCSIWSPY) = iw_handler_set_spy,
-	IW_IOCTL(SIOCGIWSPY) = iw_handler_get_spy,
-	IW_IOCTL(SIOCSIWTHRSPY) = iw_handler_set_thrspy,
-	IW_IOCTL(SIOCGIWTHRSPY) = iw_handler_get_thrspy,
-	IW_IOCTL(SIOCSIWGENIE) = ipw_wx_set_genie,
-	IW_IOCTL(SIOCGIWGENIE) = ipw_wx_get_genie,
-	IW_IOCTL(SIOCSIWMLME) = ipw_wx_set_mlme,
-	IW_IOCTL(SIOCSIWAUTH) = ipw_wx_set_auth,
-	IW_IOCTL(SIOCGIWAUTH) = ipw_wx_get_auth,
-	IW_IOCTL(SIOCSIWENCODEEXT) = ipw_wx_set_encodeext,
-	IW_IOCTL(SIOCGIWENCODEEXT) = ipw_wx_get_encodeext,
+	IW_HANDLER(SIOCGIWNAME, (iw_handler)cfg80211_wext_giwname),
+	IW_HANDLER(SIOCSIWFREQ, ipw_wx_set_freq),
+	IW_HANDLER(SIOCGIWFREQ, ipw_wx_get_freq),
+	IW_HANDLER(SIOCSIWMODE, ipw_wx_set_mode),
+	IW_HANDLER(SIOCGIWMODE, ipw_wx_get_mode),
+	IW_HANDLER(SIOCSIWSENS, ipw_wx_set_sens),
+	IW_HANDLER(SIOCGIWSENS, ipw_wx_get_sens),
+	IW_HANDLER(SIOCGIWRANGE, ipw_wx_get_range),
+	IW_HANDLER(SIOCSIWAP, ipw_wx_set_wap),
+	IW_HANDLER(SIOCGIWAP, ipw_wx_get_wap),
+	IW_HANDLER(SIOCSIWSCAN, ipw_wx_set_scan),
+	IW_HANDLER(SIOCGIWSCAN, ipw_wx_get_scan),
+	IW_HANDLER(SIOCSIWESSID, ipw_wx_set_essid),
+	IW_HANDLER(SIOCGIWESSID, ipw_wx_get_essid),
+	IW_HANDLER(SIOCSIWNICKN, ipw_wx_set_nick),
+	IW_HANDLER(SIOCGIWNICKN, ipw_wx_get_nick),
+	IW_HANDLER(SIOCSIWRATE, ipw_wx_set_rate),
+	IW_HANDLER(SIOCGIWRATE, ipw_wx_get_rate),
+	IW_HANDLER(SIOCSIWRTS, ipw_wx_set_rts),
+	IW_HANDLER(SIOCGIWRTS, ipw_wx_get_rts),
+	IW_HANDLER(SIOCSIWFRAG, ipw_wx_set_frag),
+	IW_HANDLER(SIOCGIWFRAG, ipw_wx_get_frag),
+	IW_HANDLER(SIOCSIWTXPOW, ipw_wx_set_txpow),
+	IW_HANDLER(SIOCGIWTXPOW, ipw_wx_get_txpow),
+	IW_HANDLER(SIOCSIWRETRY, ipw_wx_set_retry),
+	IW_HANDLER(SIOCGIWRETRY, ipw_wx_get_retry),
+	IW_HANDLER(SIOCSIWENCODE, ipw_wx_set_encode),
+	IW_HANDLER(SIOCGIWENCODE, ipw_wx_get_encode),
+	IW_HANDLER(SIOCSIWPOWER, ipw_wx_set_power),
+	IW_HANDLER(SIOCGIWPOWER, ipw_wx_get_power),
+	IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy),
+	IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy),
+	IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy),
+	IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy),
+	IW_HANDLER(SIOCSIWGENIE, ipw_wx_set_genie),
+	IW_HANDLER(SIOCGIWGENIE, ipw_wx_get_genie),
+	IW_HANDLER(SIOCSIWMLME, ipw_wx_set_mlme),
+	IW_HANDLER(SIOCSIWAUTH, ipw_wx_set_auth),
+	IW_HANDLER(SIOCGIWAUTH, ipw_wx_get_auth),
+	IW_HANDLER(SIOCSIWENCODEEXT, ipw_wx_set_encodeext),
+	IW_HANDLER(SIOCGIWENCODEEXT, ipw_wx_get_encodeext),
 };
 };
 
 
 enum {
 enum {

+ 7 - 2
drivers/net/wireless/iwlwifi/iwl-1000.c

@@ -212,6 +212,9 @@ static struct iwl_lib_ops iwl1000_lib = {
 		.set_ct_kill = iwl1000_set_ct_threshold,
 		.set_ct_kill = iwl1000_set_ct_threshold,
 	 },
 	 },
 	.add_bcast_station = iwl_add_bcast_station,
 	.add_bcast_station = iwl_add_bcast_station,
+	.recover_from_tx_stall = iwl_bg_monitor_recover,
+	.check_plcp_health = iwl_good_plcp_health,
+	.check_ack_health = iwl_good_ack_health,
 };
 };
 
 
 static const struct iwl_ops iwl1000_ops = {
 static const struct iwl_ops iwl1000_ops = {
@@ -223,7 +226,7 @@ static const struct iwl_ops iwl1000_ops = {
 };
 };
 
 
 struct iwl_cfg iwl1000_bgn_cfg = {
 struct iwl_cfg iwl1000_bgn_cfg = {
-	.name = "1000 Series BGN",
+	.name = "Intel(R) Centrino(R) Wireless-N 1000 BGN",
 	.fw_name_pre = IWL1000_FW_PRE,
 	.fw_name_pre = IWL1000_FW_PRE,
 	.ucode_api_max = IWL1000_UCODE_API_MAX,
 	.ucode_api_max = IWL1000_UCODE_API_MAX,
 	.ucode_api_min = IWL1000_UCODE_API_MIN,
 	.ucode_api_min = IWL1000_UCODE_API_MIN,
@@ -249,10 +252,11 @@ struct iwl_cfg iwl1000_bgn_cfg = {
 	.support_ct_kill_exit = true,
 	.support_ct_kill_exit = true,
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF,
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF,
 	.chain_noise_scale = 1000,
 	.chain_noise_scale = 1000,
+	.monitor_recover_period = IWL_MONITORING_PERIOD,
 };
 };
 
 
 struct iwl_cfg iwl1000_bg_cfg = {
 struct iwl_cfg iwl1000_bg_cfg = {
-	.name = "1000 Series BG",
+	.name = "Intel(R) Centrino(R) Wireless-N 1000 BG",
 	.fw_name_pre = IWL1000_FW_PRE,
 	.fw_name_pre = IWL1000_FW_PRE,
 	.ucode_api_max = IWL1000_UCODE_API_MAX,
 	.ucode_api_max = IWL1000_UCODE_API_MAX,
 	.ucode_api_min = IWL1000_UCODE_API_MIN,
 	.ucode_api_min = IWL1000_UCODE_API_MIN,
@@ -277,6 +281,7 @@ struct iwl_cfg iwl1000_bg_cfg = {
 	.support_ct_kill_exit = true,
 	.support_ct_kill_exit = true,
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF,
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF,
 	.chain_noise_scale = 1000,
 	.chain_noise_scale = 1000,
+	.monitor_recover_period = IWL_MONITORING_PERIOD,
 };
 };
 
 
 MODULE_FIRMWARE(IWL1000_MODULE_FIRMWARE(IWL1000_UCODE_API_MAX));
 MODULE_FIRMWARE(IWL1000_MODULE_FIRMWARE(IWL1000_UCODE_API_MAX));

+ 52 - 31
drivers/net/wireless/iwlwifi/iwl-3945-rs.c

@@ -329,16 +329,25 @@ static void iwl3945_collect_tx_data(struct iwl3945_rs_sta *rs_sta,
 
 
 }
 }
 
 
-static void rs_rate_init(void *priv_r, struct ieee80211_supported_band *sband,
-			 struct ieee80211_sta *sta, void *priv_sta)
+/*
+ * Called after adding a new station to initialize rate scaling
+ */
+void iwl3945_rs_rate_init(struct iwl_priv *priv, struct ieee80211_sta *sta, u8 sta_id)
 {
 {
-	struct iwl3945_rs_sta *rs_sta = priv_sta;
-	struct iwl_priv *priv = (struct iwl_priv *)priv_r;
+	struct ieee80211_hw *hw = priv->hw;
+	struct ieee80211_conf *conf = &priv->hw->conf;
+	struct iwl3945_sta_priv *psta;
+	struct iwl3945_rs_sta *rs_sta;
+	struct ieee80211_supported_band *sband;
 	int i;
 	int i;
 
 
-	IWL_DEBUG_RATE(priv, "enter\n");
+	IWL_DEBUG_INFO(priv, "enter \n");
+	if (sta_id == priv->hw_params.bcast_sta_id)
+		goto out;
 
 
-	spin_lock_init(&rs_sta->lock);
+	psta = (struct iwl3945_sta_priv *) sta->drv_priv;
+	rs_sta = &psta->rs_sta;
+	sband = hw->wiphy->bands[conf->channel->band];
 
 
 	rs_sta->priv = priv;
 	rs_sta->priv = priv;
 
 
@@ -351,9 +360,7 @@ static void rs_rate_init(void *priv_r, struct ieee80211_supported_band *sband,
 	rs_sta->last_flush = jiffies;
 	rs_sta->last_flush = jiffies;
 	rs_sta->flush_time = IWL_RATE_FLUSH;
 	rs_sta->flush_time = IWL_RATE_FLUSH;
 	rs_sta->last_tx_packets = 0;
 	rs_sta->last_tx_packets = 0;
-	rs_sta->ibss_sta_added = 0;
 
 
-	init_timer(&rs_sta->rate_scale_flush);
 	rs_sta->rate_scale_flush.data = (unsigned long)rs_sta;
 	rs_sta->rate_scale_flush.data = (unsigned long)rs_sta;
 	rs_sta->rate_scale_flush.function = iwl3945_bg_rate_scale_flush;
 	rs_sta->rate_scale_flush.function = iwl3945_bg_rate_scale_flush;
 
 
@@ -380,8 +387,10 @@ static void rs_rate_init(void *priv_r, struct ieee80211_supported_band *sband,
 						IWL_FIRST_OFDM_RATE;
 						IWL_FIRST_OFDM_RATE;
 	}
 	}
 
 
+out:
+	priv->stations[sta_id].used &= ~IWL_STA_UCODE_INPROGRESS;
 
 
-	IWL_DEBUG_RATE(priv, "leave\n");
+	IWL_DEBUG_INFO(priv, "leave\n");
 }
 }
 
 
 static void *rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
 static void *rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
@@ -405,6 +414,9 @@ static void *rs_alloc_sta(void *iwl_priv, struct ieee80211_sta *sta, gfp_t gfp)
 
 
 	rs_sta = &psta->rs_sta;
 	rs_sta = &psta->rs_sta;
 
 
+	spin_lock_init(&rs_sta->lock);
+	init_timer(&rs_sta->rate_scale_flush);
+
 	IWL_DEBUG_RATE(priv, "leave\n");
 	IWL_DEBUG_RATE(priv, "leave\n");
 
 
 	return rs_sta;
 	return rs_sta;
@@ -413,13 +425,14 @@ static void *rs_alloc_sta(void *iwl_priv, struct ieee80211_sta *sta, gfp_t gfp)
 static void rs_free_sta(void *iwl_priv, struct ieee80211_sta *sta,
 static void rs_free_sta(void *iwl_priv, struct ieee80211_sta *sta,
 			void *priv_sta)
 			void *priv_sta)
 {
 {
-	struct iwl3945_sta_priv *psta = (void *) sta->drv_priv;
-	struct iwl3945_rs_sta *rs_sta = &psta->rs_sta;
-	struct iwl_priv *priv __maybe_unused = rs_sta->priv;
+	struct iwl3945_rs_sta *rs_sta = priv_sta;
 
 
-	IWL_DEBUG_RATE(priv, "enter\n");
+	/*
+	 * Be careful not to use any members of iwl3945_rs_sta (like trying
+	 * to use iwl_priv to print out debugging) since it may not be fully
+	 * initialized at this point.
+	 */
 	del_timer_sync(&rs_sta->rate_scale_flush);
 	del_timer_sync(&rs_sta->rate_scale_flush);
-	IWL_DEBUG_RATE(priv, "leave\n");
 }
 }
 
 
 
 
@@ -458,6 +471,13 @@ static void rs_tx_status(void *priv_rate, struct ieee80211_supported_band *sband
 		return;
 		return;
 	}
 	}
 
 
+	/* Treat uninitialized rate scaling data same as non-existing. */
+	if (!rs_sta->priv) {
+		IWL_DEBUG_RATE(priv, "leave: STA priv data uninitialized!\n");
+		return;
+	}
+
+
 	rs_sta->tx_packets++;
 	rs_sta->tx_packets++;
 
 
 	scale_rate_index = first_index;
 	scale_rate_index = first_index;
@@ -625,7 +645,6 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta,
 	u32 fail_count;
 	u32 fail_count;
 	s8 scale_action = 0;
 	s8 scale_action = 0;
 	unsigned long flags;
 	unsigned long flags;
-	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
 	u16 rate_mask = sta ? sta->supp_rates[sband->band] : 0;
 	u16 rate_mask = sta ? sta->supp_rates[sband->band] : 0;
 	s8 max_rate_idx = -1;
 	s8 max_rate_idx = -1;
 	struct iwl_priv *priv = (struct iwl_priv *)priv_r;
 	struct iwl_priv *priv = (struct iwl_priv *)priv_r;
@@ -633,6 +652,12 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta,
 
 
 	IWL_DEBUG_RATE(priv, "enter\n");
 	IWL_DEBUG_RATE(priv, "enter\n");
 
 
+	/* Treat uninitialized rate scaling data same as non-existing. */
+	if (rs_sta && !rs_sta->priv) {
+		IWL_DEBUG_RATE(priv, "Rate scaling information not initialized yet.\n");
+		priv_sta = NULL;
+	}
+
 	if (rate_control_send_low(sta, priv_sta, txrc))
 	if (rate_control_send_low(sta, priv_sta, txrc))
 		return;
 		return;
 
 
@@ -650,20 +675,6 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta,
 	if (sband->band == IEEE80211_BAND_5GHZ)
 	if (sband->band == IEEE80211_BAND_5GHZ)
 		rate_mask = rate_mask << IWL_FIRST_OFDM_RATE;
 		rate_mask = rate_mask << IWL_FIRST_OFDM_RATE;
 
 
-	if ((priv->iw_mode == NL80211_IFTYPE_ADHOC) &&
-	    !rs_sta->ibss_sta_added) {
-		u8 sta_id = iwl_find_station(priv, hdr->addr1);
-
-		if (sta_id == IWL_INVALID_STATION) {
-			IWL_DEBUG_RATE(priv, "LQ: ADD station %pM\n",
-				       hdr->addr1);
-			sta_id = iwl_add_station(priv, hdr->addr1, false,
-				CMD_ASYNC, NULL);
-		}
-		if (sta_id != IWL_INVALID_STATION)
-			rs_sta->ibss_sta_added = 1;
-	}
-
 	spin_lock_irqsave(&rs_sta->lock, flags);
 	spin_lock_irqsave(&rs_sta->lock, flags);
 
 
 	/* for recent assoc, choose best rate regarding
 	/* for recent assoc, choose best rate regarding
@@ -883,12 +894,22 @@ static void iwl3945_remove_debugfs(void *priv, void *priv_sta)
 }
 }
 #endif
 #endif
 
 
+/*
+ * Initialization of rate scaling information is done by driver after
+ * the station is added. Since mac80211 calls this function before a
+ * station is added we ignore it.
+ */
+static void rs_rate_init_stub(void *priv_r, struct ieee80211_supported_band *sband,
+			      struct ieee80211_sta *sta, void *priv_sta)
+{
+}
+
 static struct rate_control_ops rs_ops = {
 static struct rate_control_ops rs_ops = {
 	.module = NULL,
 	.module = NULL,
 	.name = RS_NAME,
 	.name = RS_NAME,
 	.tx_status = rs_tx_status,
 	.tx_status = rs_tx_status,
 	.get_rate = rs_get_rate,
 	.get_rate = rs_get_rate,
-	.rate_init = rs_rate_init,
+	.rate_init = rs_rate_init_stub,
 	.alloc = rs_alloc,
 	.alloc = rs_alloc,
 	.free = rs_free,
 	.free = rs_free,
 	.alloc_sta = rs_alloc_sta,
 	.alloc_sta = rs_alloc_sta,
@@ -899,7 +920,6 @@ static struct rate_control_ops rs_ops = {
 #endif
 #endif
 
 
 };
 };
-
 void iwl3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id)
 void iwl3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id)
 {
 {
 	struct iwl_priv *priv = hw->priv;
 	struct iwl_priv *priv = hw->priv;
@@ -916,6 +936,7 @@ void iwl3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id)
 	sta = ieee80211_find_sta(priv->vif,
 	sta = ieee80211_find_sta(priv->vif,
 				 priv->stations[sta_id].sta.sta.addr);
 				 priv->stations[sta_id].sta.sta.addr);
 	if (!sta) {
 	if (!sta) {
+		IWL_DEBUG_RATE(priv, "Unable to find station to initialize rate scaling.\n");
 		rcu_read_unlock();
 		rcu_read_unlock();
 		return;
 		return;
 	}
 	}

+ 8 - 14
drivers/net/wireless/iwlwifi/iwl-3945.c

@@ -1911,6 +1911,8 @@ static int iwl3945_commit_rxon(struct iwl_priv *priv)
 				  "configuration (%d).\n", rc);
 				  "configuration (%d).\n", rc);
 			return rc;
 			return rc;
 		}
 		}
+		iwl_clear_ucode_stations(priv, false);
+		iwl_restore_stations(priv);
 	}
 	}
 
 
 	IWL_DEBUG_INFO(priv, "Sending RXON\n"
 	IWL_DEBUG_INFO(priv, "Sending RXON\n"
@@ -1941,7 +1943,10 @@ static int iwl3945_commit_rxon(struct iwl_priv *priv)
 
 
 	memcpy(active_rxon, staging_rxon, sizeof(*active_rxon));
 	memcpy(active_rxon, staging_rxon, sizeof(*active_rxon));
 
 
-	iwl_clear_stations_table(priv);
+	if (!new_assoc) {
+		iwl_clear_ucode_stations(priv, false);
+		iwl_restore_stations(priv);
+	}
 
 
 	/* If we issue a new RXON command which required a tune then we must
 	/* If we issue a new RXON command which required a tune then we must
 	 * send a new TXPOWER command or we won't be able to Tx any frames */
 	 * send a new TXPOWER command or we won't be able to Tx any frames */
@@ -1951,19 +1956,6 @@ static int iwl3945_commit_rxon(struct iwl_priv *priv)
 		return rc;
 		return rc;
 	}
 	}
 
 
-	/* Add the broadcast address so we can send broadcast frames */
-	priv->cfg->ops->lib->add_bcast_station(priv);
-
-	/* If we have set the ASSOC_MSK and we are in BSS mode then
-	 * add the IWL_AP_ID to the station rate table */
-	if (iwl_is_associated(priv) &&
-	    (priv->iw_mode == NL80211_IFTYPE_STATION))
-		if (iwl_add_station(priv, priv->active_rxon.bssid_addr,
-				true, CMD_SYNC, NULL) == IWL_INVALID_STATION) {
-			IWL_ERR(priv, "Error adding AP address for transmit\n");
-			return -EIO;
-		}
-
 	/* Init the hardware's rate fallback order based on the band */
 	/* Init the hardware's rate fallback order based on the band */
 	rc = iwl3945_init_hw_rate_table(priv);
 	rc = iwl3945_init_hw_rate_table(priv);
 	if (rc) {
 	if (rc) {
@@ -2828,6 +2820,7 @@ static struct iwl_cfg iwl3945_bg_cfg = {
 	.led_compensation = 64,
 	.led_compensation = 64,
 	.broken_powersave = true,
 	.broken_powersave = true,
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+	.monitor_recover_period = IWL_MONITORING_PERIOD,
 };
 };
 
 
 static struct iwl_cfg iwl3945_abg_cfg = {
 static struct iwl_cfg iwl3945_abg_cfg = {
@@ -2846,6 +2839,7 @@ static struct iwl_cfg iwl3945_abg_cfg = {
 	.led_compensation = 64,
 	.led_compensation = 64,
 	.broken_powersave = true,
 	.broken_powersave = true,
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+	.monitor_recover_period = IWL_MONITORING_PERIOD,
 };
 };
 
 
 DEFINE_PCI_DEVICE_TABLE(iwl3945_hw_card_ids) = {
 DEFINE_PCI_DEVICE_TABLE(iwl3945_hw_card_ids) = {

+ 0 - 1
drivers/net/wireless/iwlwifi/iwl-3945.h

@@ -95,7 +95,6 @@ struct iwl3945_rs_sta {
 	u8 tgg;
 	u8 tgg;
 	u8 flush_pending;
 	u8 flush_pending;
 	u8 start_rate;
 	u8 start_rate;
-	u8 ibss_sta_added;
 	struct timer_list rate_scale_flush;
 	struct timer_list rate_scale_flush;
 	struct iwl3945_rate_scale_data win[IWL_RATE_COUNT_3945];
 	struct iwl3945_rate_scale_data win[IWL_RATE_COUNT_3945];
 #ifdef CONFIG_MAC80211_DEBUGFS
 #ifdef CONFIG_MAC80211_DEBUGFS

+ 4 - 1
drivers/net/wireless/iwlwifi/iwl-4965.c

@@ -2187,6 +2187,7 @@ static struct iwl_lib_ops iwl4965_lib = {
 	.load_ucode = iwl4965_load_bsm,
 	.load_ucode = iwl4965_load_bsm,
 	.dump_nic_event_log = iwl_dump_nic_event_log,
 	.dump_nic_event_log = iwl_dump_nic_event_log,
 	.dump_nic_error_log = iwl_dump_nic_error_log,
 	.dump_nic_error_log = iwl_dump_nic_error_log,
+	.dump_fh = iwl_dump_fh,
 	.set_channel_switch = iwl4965_hw_channel_switch,
 	.set_channel_switch = iwl4965_hw_channel_switch,
 	.apm_ops = {
 	.apm_ops = {
 		.init = iwl_apm_init,
 		.init = iwl_apm_init,
@@ -2220,6 +2221,7 @@ static struct iwl_lib_ops iwl4965_lib = {
 		.set_ct_kill = iwl4965_set_ct_threshold,
 		.set_ct_kill = iwl4965_set_ct_threshold,
 	},
 	},
 	.add_bcast_station = iwl_add_bcast_station,
 	.add_bcast_station = iwl_add_bcast_station,
+	.check_plcp_health = iwl_good_plcp_health,
 };
 };
 
 
 static const struct iwl_ops iwl4965_ops = {
 static const struct iwl_ops iwl4965_ops = {
@@ -2231,7 +2233,7 @@ static const struct iwl_ops iwl4965_ops = {
 };
 };
 
 
 struct iwl_cfg iwl4965_agn_cfg = {
 struct iwl_cfg iwl4965_agn_cfg = {
-	.name = "4965AGN",
+	.name = "Intel(R) Wireless WiFi Link 4965AGN",
 	.fw_name_pre = IWL4965_FW_PRE,
 	.fw_name_pre = IWL4965_FW_PRE,
 	.ucode_api_max = IWL4965_UCODE_API_MAX,
 	.ucode_api_max = IWL4965_UCODE_API_MAX,
 	.ucode_api_min = IWL4965_UCODE_API_MIN,
 	.ucode_api_min = IWL4965_UCODE_API_MIN,
@@ -2254,6 +2256,7 @@ struct iwl_cfg iwl4965_agn_cfg = {
 	.led_compensation = 61,
 	.led_compensation = 61,
 	.chain_noise_num_beacons = IWL4965_CAL_NUM_BEACONS,
 	.chain_noise_num_beacons = IWL4965_CAL_NUM_BEACONS,
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+	.monitor_recover_period = IWL_MONITORING_PERIOD,
 };
 };
 
 
 /* Module firmware */
 /* Module firmware */

+ 20 - 8
drivers/net/wireless/iwlwifi/iwl-5000.c

@@ -544,7 +544,6 @@ void iwl5000_init_alive_start(struct iwl_priv *priv)
 		goto restart;
 		goto restart;
 	}
 	}
 
 
-	iwl_clear_stations_table(priv);
 	ret = priv->cfg->ops->lib->alive_notify(priv);
 	ret = priv->cfg->ops->lib->alive_notify(priv);
 	if (ret) {
 	if (ret) {
 		IWL_WARN(priv,
 		IWL_WARN(priv,
@@ -1500,6 +1499,9 @@ struct iwl_lib_ops iwl5000_lib = {
 		.set_ct_kill = iwl5000_set_ct_threshold,
 		.set_ct_kill = iwl5000_set_ct_threshold,
 	 },
 	 },
 	.add_bcast_station = iwl_add_bcast_station,
 	.add_bcast_station = iwl_add_bcast_station,
+	.recover_from_tx_stall = iwl_bg_monitor_recover,
+	.check_plcp_health = iwl_good_plcp_health,
+	.check_ack_health = iwl_good_ack_health,
 };
 };
 
 
 static struct iwl_lib_ops iwl5150_lib = {
 static struct iwl_lib_ops iwl5150_lib = {
@@ -1554,6 +1556,9 @@ static struct iwl_lib_ops iwl5150_lib = {
 		.set_ct_kill = iwl5150_set_ct_threshold,
 		.set_ct_kill = iwl5150_set_ct_threshold,
 	 },
 	 },
 	.add_bcast_station = iwl_add_bcast_station,
 	.add_bcast_station = iwl_add_bcast_station,
+	.recover_from_tx_stall = iwl_bg_monitor_recover,
+	.check_plcp_health = iwl_good_plcp_health,
+	.check_ack_health = iwl_good_ack_health,
 };
 };
 
 
 static const struct iwl_ops iwl5000_ops = {
 static const struct iwl_ops iwl5000_ops = {
@@ -1580,7 +1585,7 @@ struct iwl_mod_params iwl50_mod_params = {
 
 
 
 
 struct iwl_cfg iwl5300_agn_cfg = {
 struct iwl_cfg iwl5300_agn_cfg = {
-	.name = "5300AGN",
+	.name = "Intel(R) Ultimate N WiFi Link 5300 AGN",
 	.fw_name_pre = IWL5000_FW_PRE,
 	.fw_name_pre = IWL5000_FW_PRE,
 	.ucode_api_max = IWL5000_UCODE_API_MAX,
 	.ucode_api_max = IWL5000_UCODE_API_MAX,
 	.ucode_api_min = IWL5000_UCODE_API_MIN,
 	.ucode_api_min = IWL5000_UCODE_API_MIN,
@@ -1603,10 +1608,11 @@ struct iwl_cfg iwl5300_agn_cfg = {
 	.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
 	.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
 	.chain_noise_scale = 1000,
 	.chain_noise_scale = 1000,
+	.monitor_recover_period = IWL_MONITORING_PERIOD,
 };
 };
 
 
 struct iwl_cfg iwl5100_bgn_cfg = {
 struct iwl_cfg iwl5100_bgn_cfg = {
-	.name = "5100BGN",
+	.name = "Intel(R) WiFi Link 5100 BGN",
 	.fw_name_pre = IWL5000_FW_PRE,
 	.fw_name_pre = IWL5000_FW_PRE,
 	.ucode_api_max = IWL5000_UCODE_API_MAX,
 	.ucode_api_max = IWL5000_UCODE_API_MAX,
 	.ucode_api_min = IWL5000_UCODE_API_MIN,
 	.ucode_api_min = IWL5000_UCODE_API_MIN,
@@ -1629,10 +1635,11 @@ struct iwl_cfg iwl5100_bgn_cfg = {
 	.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
 	.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
 	.chain_noise_scale = 1000,
 	.chain_noise_scale = 1000,
+	.monitor_recover_period = IWL_MONITORING_PERIOD,
 };
 };
 
 
 struct iwl_cfg iwl5100_abg_cfg = {
 struct iwl_cfg iwl5100_abg_cfg = {
-	.name = "5100ABG",
+	.name = "Intel(R) WiFi Link 5100 ABG",
 	.fw_name_pre = IWL5000_FW_PRE,
 	.fw_name_pre = IWL5000_FW_PRE,
 	.ucode_api_max = IWL5000_UCODE_API_MAX,
 	.ucode_api_max = IWL5000_UCODE_API_MAX,
 	.ucode_api_min = IWL5000_UCODE_API_MIN,
 	.ucode_api_min = IWL5000_UCODE_API_MIN,
@@ -1653,10 +1660,11 @@ struct iwl_cfg iwl5100_abg_cfg = {
 	.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
 	.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
 	.chain_noise_scale = 1000,
 	.chain_noise_scale = 1000,
+	.monitor_recover_period = IWL_MONITORING_PERIOD,
 };
 };
 
 
 struct iwl_cfg iwl5100_agn_cfg = {
 struct iwl_cfg iwl5100_agn_cfg = {
-	.name = "5100AGN",
+	.name = "Intel(R) WiFi Link 5100 AGN",
 	.fw_name_pre = IWL5000_FW_PRE,
 	.fw_name_pre = IWL5000_FW_PRE,
 	.ucode_api_max = IWL5000_UCODE_API_MAX,
 	.ucode_api_max = IWL5000_UCODE_API_MAX,
 	.ucode_api_min = IWL5000_UCODE_API_MIN,
 	.ucode_api_min = IWL5000_UCODE_API_MIN,
@@ -1679,10 +1687,11 @@ struct iwl_cfg iwl5100_agn_cfg = {
 	.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
 	.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
 	.chain_noise_scale = 1000,
 	.chain_noise_scale = 1000,
+	.monitor_recover_period = IWL_MONITORING_PERIOD,
 };
 };
 
 
 struct iwl_cfg iwl5350_agn_cfg = {
 struct iwl_cfg iwl5350_agn_cfg = {
-	.name = "5350AGN",
+	.name = "Intel(R) WiMAX/WiFi Link 5350 AGN",
 	.fw_name_pre = IWL5000_FW_PRE,
 	.fw_name_pre = IWL5000_FW_PRE,
 	.ucode_api_max = IWL5000_UCODE_API_MAX,
 	.ucode_api_max = IWL5000_UCODE_API_MAX,
 	.ucode_api_min = IWL5000_UCODE_API_MIN,
 	.ucode_api_min = IWL5000_UCODE_API_MIN,
@@ -1705,10 +1714,11 @@ struct iwl_cfg iwl5350_agn_cfg = {
 	.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
 	.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
 	.chain_noise_scale = 1000,
 	.chain_noise_scale = 1000,
+	.monitor_recover_period = IWL_MONITORING_PERIOD,
 };
 };
 
 
 struct iwl_cfg iwl5150_agn_cfg = {
 struct iwl_cfg iwl5150_agn_cfg = {
-	.name = "5150AGN",
+	.name = "Intel(R) WiMAX/WiFi Link 5150 AGN",
 	.fw_name_pre = IWL5150_FW_PRE,
 	.fw_name_pre = IWL5150_FW_PRE,
 	.ucode_api_max = IWL5150_UCODE_API_MAX,
 	.ucode_api_max = IWL5150_UCODE_API_MAX,
 	.ucode_api_min = IWL5150_UCODE_API_MIN,
 	.ucode_api_min = IWL5150_UCODE_API_MIN,
@@ -1731,10 +1741,11 @@ struct iwl_cfg iwl5150_agn_cfg = {
 	.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
 	.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
 	.chain_noise_scale = 1000,
 	.chain_noise_scale = 1000,
+	.monitor_recover_period = IWL_MONITORING_PERIOD,
 };
 };
 
 
 struct iwl_cfg iwl5150_abg_cfg = {
 struct iwl_cfg iwl5150_abg_cfg = {
-	.name = "5150ABG",
+	.name = "Intel(R) WiMAX/WiFi Link 5150 ABG",
 	.fw_name_pre = IWL5150_FW_PRE,
 	.fw_name_pre = IWL5150_FW_PRE,
 	.ucode_api_max = IWL5150_UCODE_API_MAX,
 	.ucode_api_max = IWL5150_UCODE_API_MAX,
 	.ucode_api_min = IWL5150_UCODE_API_MIN,
 	.ucode_api_min = IWL5150_UCODE_API_MIN,
@@ -1755,6 +1766,7 @@ struct iwl_cfg iwl5150_abg_cfg = {
 	.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
 	.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
 	.chain_noise_scale = 1000,
 	.chain_noise_scale = 1000,
+	.monitor_recover_period = IWL_MONITORING_PERIOD,
 };
 };
 
 
 MODULE_FIRMWARE(IWL5000_MODULE_FIRMWARE(IWL5000_UCODE_API_MAX));
 MODULE_FIRMWARE(IWL5000_MODULE_FIRMWARE(IWL5000_UCODE_API_MAX));

+ 18 - 6
drivers/net/wireless/iwlwifi/iwl-6000.c

@@ -278,6 +278,9 @@ static struct iwl_lib_ops iwl6000_lib = {
 		.set_ct_kill = iwl6000_set_ct_threshold,
 		.set_ct_kill = iwl6000_set_ct_threshold,
 	 },
 	 },
 	.add_bcast_station = iwl_add_bcast_station,
 	.add_bcast_station = iwl_add_bcast_station,
+	.recover_from_tx_stall = iwl_bg_monitor_recover,
+	.check_plcp_health = iwl_good_plcp_health,
+	.check_ack_health = iwl_good_ack_health,
 };
 };
 
 
 static const struct iwl_ops iwl6000_ops = {
 static const struct iwl_ops iwl6000_ops = {
@@ -343,6 +346,9 @@ static struct iwl_lib_ops iwl6050_lib = {
 		.set_calib_version = iwl6050_set_calib_version,
 		.set_calib_version = iwl6050_set_calib_version,
 	 },
 	 },
 	.add_bcast_station = iwl_add_bcast_station,
 	.add_bcast_station = iwl_add_bcast_station,
+	.recover_from_tx_stall = iwl_bg_monitor_recover,
+	.check_plcp_health = iwl_good_plcp_health,
+	.check_ack_health = iwl_good_ack_health,
 };
 };
 
 
 static const struct iwl_ops iwl6050_ops = {
 static const struct iwl_ops iwl6050_ops = {
@@ -357,7 +363,7 @@ static const struct iwl_ops iwl6050_ops = {
  * "i": Internal configuration, use internal Power Amplifier
  * "i": Internal configuration, use internal Power Amplifier
  */
  */
 struct iwl_cfg iwl6000i_2agn_cfg = {
 struct iwl_cfg iwl6000i_2agn_cfg = {
-	.name = "6000 Series 2x2 AGN",
+	.name = "Intel(R) Centrino(R) Advanced-N 6200 AGN",
 	.fw_name_pre = IWL6000_FW_PRE,
 	.fw_name_pre = IWL6000_FW_PRE,
 	.ucode_api_max = IWL6000_UCODE_API_MAX,
 	.ucode_api_max = IWL6000_UCODE_API_MAX,
 	.ucode_api_min = IWL6000_UCODE_API_MIN,
 	.ucode_api_min = IWL6000_UCODE_API_MIN,
@@ -386,10 +392,11 @@ struct iwl_cfg iwl6000i_2agn_cfg = {
 	.support_ct_kill_exit = true,
 	.support_ct_kill_exit = true,
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
 	.chain_noise_scale = 1000,
 	.chain_noise_scale = 1000,
+	.monitor_recover_period = IWL_MONITORING_PERIOD,
 };
 };
 
 
 struct iwl_cfg iwl6000i_2abg_cfg = {
 struct iwl_cfg iwl6000i_2abg_cfg = {
-	.name = "6000 Series 2x2 ABG",
+	.name = "Intel(R) Centrino(R) Advanced-N 6200 ABG",
 	.fw_name_pre = IWL6000_FW_PRE,
 	.fw_name_pre = IWL6000_FW_PRE,
 	.ucode_api_max = IWL6000_UCODE_API_MAX,
 	.ucode_api_max = IWL6000_UCODE_API_MAX,
 	.ucode_api_min = IWL6000_UCODE_API_MIN,
 	.ucode_api_min = IWL6000_UCODE_API_MIN,
@@ -417,10 +424,11 @@ struct iwl_cfg iwl6000i_2abg_cfg = {
 	.support_ct_kill_exit = true,
 	.support_ct_kill_exit = true,
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
 	.chain_noise_scale = 1000,
 	.chain_noise_scale = 1000,
+	.monitor_recover_period = IWL_MONITORING_PERIOD,
 };
 };
 
 
 struct iwl_cfg iwl6000i_2bg_cfg = {
 struct iwl_cfg iwl6000i_2bg_cfg = {
-	.name = "6000 Series 2x2 BG",
+	.name = "Intel(R) Centrino(R) Advanced-N 6200 BG",
 	.fw_name_pre = IWL6000_FW_PRE,
 	.fw_name_pre = IWL6000_FW_PRE,
 	.ucode_api_max = IWL6000_UCODE_API_MAX,
 	.ucode_api_max = IWL6000_UCODE_API_MAX,
 	.ucode_api_min = IWL6000_UCODE_API_MIN,
 	.ucode_api_min = IWL6000_UCODE_API_MIN,
@@ -448,10 +456,11 @@ struct iwl_cfg iwl6000i_2bg_cfg = {
 	.support_ct_kill_exit = true,
 	.support_ct_kill_exit = true,
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
 	.chain_noise_scale = 1000,
 	.chain_noise_scale = 1000,
+	.monitor_recover_period = IWL_MONITORING_PERIOD,
 };
 };
 
 
 struct iwl_cfg iwl6050_2agn_cfg = {
 struct iwl_cfg iwl6050_2agn_cfg = {
-	.name = "6050 Series 2x2 AGN",
+	.name = "Intel(R) Centrino(R) Advanced-N + WiMAX 6250 AGN",
 	.fw_name_pre = IWL6050_FW_PRE,
 	.fw_name_pre = IWL6050_FW_PRE,
 	.ucode_api_max = IWL6050_UCODE_API_MAX,
 	.ucode_api_max = IWL6050_UCODE_API_MAX,
 	.ucode_api_min = IWL6050_UCODE_API_MIN,
 	.ucode_api_min = IWL6050_UCODE_API_MIN,
@@ -480,10 +489,11 @@ struct iwl_cfg iwl6050_2agn_cfg = {
 	.support_ct_kill_exit = true,
 	.support_ct_kill_exit = true,
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
 	.chain_noise_scale = 1500,
 	.chain_noise_scale = 1500,
+	.monitor_recover_period = IWL_MONITORING_PERIOD,
 };
 };
 
 
 struct iwl_cfg iwl6050_2abg_cfg = {
 struct iwl_cfg iwl6050_2abg_cfg = {
-	.name = "6050 Series 2x2 ABG",
+	.name = "Intel(R) Centrino(R) Advanced-N + WiMAX 6250 ABG",
 	.fw_name_pre = IWL6050_FW_PRE,
 	.fw_name_pre = IWL6050_FW_PRE,
 	.ucode_api_max = IWL6050_UCODE_API_MAX,
 	.ucode_api_max = IWL6050_UCODE_API_MAX,
 	.ucode_api_min = IWL6050_UCODE_API_MIN,
 	.ucode_api_min = IWL6050_UCODE_API_MIN,
@@ -511,10 +521,11 @@ struct iwl_cfg iwl6050_2abg_cfg = {
 	.support_ct_kill_exit = true,
 	.support_ct_kill_exit = true,
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
 	.chain_noise_scale = 1500,
 	.chain_noise_scale = 1500,
+	.monitor_recover_period = IWL_MONITORING_PERIOD,
 };
 };
 
 
 struct iwl_cfg iwl6000_3agn_cfg = {
 struct iwl_cfg iwl6000_3agn_cfg = {
-	.name = "6000 Series 3x3 AGN",
+	.name = "Intel(R) Centrino(R) Ultimate-N 6300 AGN",
 	.fw_name_pre = IWL6000_FW_PRE,
 	.fw_name_pre = IWL6000_FW_PRE,
 	.ucode_api_max = IWL6000_UCODE_API_MAX,
 	.ucode_api_max = IWL6000_UCODE_API_MAX,
 	.ucode_api_min = IWL6000_UCODE_API_MIN,
 	.ucode_api_min = IWL6000_UCODE_API_MIN,
@@ -543,6 +554,7 @@ struct iwl_cfg iwl6000_3agn_cfg = {
 	.support_ct_kill_exit = true,
 	.support_ct_kill_exit = true,
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
 	.chain_noise_scale = 1000,
 	.chain_noise_scale = 1000,
+	.monitor_recover_period = IWL_MONITORING_PERIOD,
 };
 };
 
 
 MODULE_FIRMWARE(IWL6000_MODULE_FIRMWARE(IWL6000_UCODE_API_MAX));
 MODULE_FIRMWARE(IWL6000_MODULE_FIRMWARE(IWL6000_UCODE_API_MAX));

+ 61 - 63
drivers/net/wireless/iwlwifi/iwl-agn-rs.c

@@ -769,6 +769,15 @@ static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband,
 
 
 	IWL_DEBUG_RATE_LIMIT(priv, "get frame ack response, update rate scale window\n");
 	IWL_DEBUG_RATE_LIMIT(priv, "get frame ack response, update rate scale window\n");
 
 
+	/* Treat uninitialized rate scaling data same as non-existing. */
+	if (!lq_sta) {
+		IWL_DEBUG_RATE(priv, "Station rate scaling not created yet.\n");
+		return;
+	} else if (!lq_sta->drv) {
+		IWL_DEBUG_RATE(priv, "Rate scaling not initialized yet.\n");
+		return;
+	}
+
 	if (!ieee80211_is_data(hdr->frame_control) ||
 	if (!ieee80211_is_data(hdr->frame_control) ||
 	    info->flags & IEEE80211_TX_CTL_NO_ACK)
 	    info->flags & IEEE80211_TX_CTL_NO_ACK)
 		return;
 		return;
@@ -778,10 +787,6 @@ static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband,
 	    !(info->flags & IEEE80211_TX_STAT_AMPDU))
 	    !(info->flags & IEEE80211_TX_STAT_AMPDU))
 		return;
 		return;
 
 
-	if ((priv->iw_mode == NL80211_IFTYPE_ADHOC) &&
-	    !lq_sta->ibss_sta_added)
-		return;
-
 	/*
 	/*
 	 * Ignore this Tx frame response if its initial rate doesn't match
 	 * Ignore this Tx frame response if its initial rate doesn't match
 	 * that of latest Link Quality command.  There may be stragglers
 	 * that of latest Link Quality command.  There may be stragglers
@@ -827,7 +832,7 @@ static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband,
 		lq_sta->missed_rate_counter++;
 		lq_sta->missed_rate_counter++;
 		if (lq_sta->missed_rate_counter > IWL_MISSED_RATE_MAX) {
 		if (lq_sta->missed_rate_counter > IWL_MISSED_RATE_MAX) {
 			lq_sta->missed_rate_counter = 0;
 			lq_sta->missed_rate_counter = 0;
-			iwl_send_lq_cmd(priv, &lq_sta->lq, CMD_ASYNC);
+			iwl_send_lq_cmd(priv, &lq_sta->lq, CMD_ASYNC, false);
 		}
 		}
 		/* Regardless, ignore this status info for outdated rate */
 		/* Regardless, ignore this status info for outdated rate */
 		return;
 		return;
@@ -1915,7 +1920,7 @@ static u32 rs_update_rate_tbl(struct iwl_priv *priv,
 	/* Update uCode's rate table. */
 	/* Update uCode's rate table. */
 	rate = rate_n_flags_from_tbl(priv, tbl, index, is_green);
 	rate = rate_n_flags_from_tbl(priv, tbl, index, is_green);
 	rs_fill_link_cmd(priv, lq_sta, rate);
 	rs_fill_link_cmd(priv, lq_sta, rate);
-	iwl_send_lq_cmd(priv, &lq_sta->lq, CMD_ASYNC);
+	iwl_send_lq_cmd(priv, &lq_sta->lq, CMD_ASYNC, false);
 
 
 	return rate;
 	return rate;
 }
 }
@@ -2291,7 +2296,7 @@ lq_update:
 			IWL_DEBUG_RATE(priv, "Switch current  mcs: %X index: %d\n",
 			IWL_DEBUG_RATE(priv, "Switch current  mcs: %X index: %d\n",
 				     tbl->current_rate, index);
 				     tbl->current_rate, index);
 			rs_fill_link_cmd(priv, lq_sta, tbl->current_rate);
 			rs_fill_link_cmd(priv, lq_sta, tbl->current_rate);
-			iwl_send_lq_cmd(priv, &lq_sta->lq, CMD_ASYNC);
+			iwl_send_lq_cmd(priv, &lq_sta->lq, CMD_ASYNC, false);
 		} else
 		} else
 			done_search = 1;
 			done_search = 1;
 	}
 	}
@@ -2340,7 +2345,20 @@ out:
 	return;
 	return;
 }
 }
 
 
-
+/**
+ * rs_initialize_lq - Initialize a station's hardware rate table
+ *
+ * The uCode's station table contains a table of fallback rates
+ * for automatic fallback during transmission.
+ *
+ * NOTE: This sets up a default set of values.  These will be replaced later
+ *       if the driver's iwl-agn-rs rate scaling algorithm is used, instead of
+ *       rc80211_simple.
+ *
+ * NOTE: Run REPLY_ADD_STA command to set up station table entry, before
+ *       calling this function (which runs REPLY_TX_LINK_QUALITY_CMD,
+ *       which requires station table entry to exist).
+ */
 static void rs_initialize_lq(struct iwl_priv *priv,
 static void rs_initialize_lq(struct iwl_priv *priv,
 			     struct ieee80211_conf *conf,
 			     struct ieee80211_conf *conf,
 			     struct ieee80211_sta *sta,
 			     struct ieee80211_sta *sta,
@@ -2359,10 +2377,6 @@ static void rs_initialize_lq(struct iwl_priv *priv,
 
 
 	i = lq_sta->last_txrate_idx;
 	i = lq_sta->last_txrate_idx;
 
 
-	if ((lq_sta->lq.sta_id == 0xff) &&
-	    (priv->iw_mode == NL80211_IFTYPE_ADHOC))
-		goto out;
-
 	valid_tx_ant = priv->hw_params.valid_tx_ant;
 	valid_tx_ant = priv->hw_params.valid_tx_ant;
 
 
 	if (!lq_sta->search_better_tbl)
 	if (!lq_sta->search_better_tbl)
@@ -2390,7 +2404,8 @@ static void rs_initialize_lq(struct iwl_priv *priv,
 	tbl->current_rate = rate;
 	tbl->current_rate = rate;
 	rs_set_expected_tpt_table(lq_sta, tbl);
 	rs_set_expected_tpt_table(lq_sta, tbl);
 	rs_fill_link_cmd(NULL, lq_sta, rate);
 	rs_fill_link_cmd(NULL, lq_sta, rate);
-	iwl_send_lq_cmd(priv, &lq_sta->lq, CMD_ASYNC);
+	priv->stations[lq_sta->lq.sta_id].lq = &lq_sta->lq;
+	iwl_send_lq_cmd(priv, &lq_sta->lq, CMD_SYNC, true);
  out:
  out:
 	return;
 	return;
 }
 }
@@ -2402,9 +2417,6 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, void *priv_sta,
 	struct sk_buff *skb = txrc->skb;
 	struct sk_buff *skb = txrc->skb;
 	struct ieee80211_supported_band *sband = txrc->sband;
 	struct ieee80211_supported_band *sband = txrc->sband;
 	struct iwl_priv *priv = (struct iwl_priv *)priv_r;
 	struct iwl_priv *priv = (struct iwl_priv *)priv_r;
-	struct ieee80211_conf *conf = &priv->hw->conf;
-	struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
-	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
 	struct iwl_lq_sta *lq_sta = priv_sta;
 	struct iwl_lq_sta *lq_sta = priv_sta;
 	int rate_idx;
 	int rate_idx;
@@ -2422,30 +2434,18 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, void *priv_sta,
 			lq_sta->max_rate_idx = -1;
 			lq_sta->max_rate_idx = -1;
 	}
 	}
 
 
+	/* Treat uninitialized rate scaling data same as non-existing. */
+	if (lq_sta && !lq_sta->drv) {
+		IWL_DEBUG_RATE(priv, "Rate scaling not initialized yet.\n");
+		priv_sta = NULL;
+	}
+
 	/* Send management frames and NO_ACK data using lowest rate. */
 	/* Send management frames and NO_ACK data using lowest rate. */
 	if (rate_control_send_low(sta, priv_sta, txrc))
 	if (rate_control_send_low(sta, priv_sta, txrc))
 		return;
 		return;
 
 
 	rate_idx  = lq_sta->last_txrate_idx;
 	rate_idx  = lq_sta->last_txrate_idx;
 
 
-	if ((priv->iw_mode == NL80211_IFTYPE_ADHOC) &&
-	    !lq_sta->ibss_sta_added) {
-		u8 sta_id = iwl_find_station(priv, hdr->addr1);
-
-		if (sta_id == IWL_INVALID_STATION) {
-			IWL_DEBUG_RATE(priv, "LQ: ADD station %pM\n",
-				       hdr->addr1);
-			sta_id = iwl_add_station(priv, hdr->addr1,
-						false, CMD_ASYNC, ht_cap);
-		}
-		if ((sta_id != IWL_INVALID_STATION)) {
-			lq_sta->lq.sta_id = sta_id;
-			lq_sta->lq.rs_table[0].rate_n_flags = 0;
-			lq_sta->ibss_sta_added = 1;
-			rs_initialize_lq(priv, conf, sta, lq_sta);
-		}
-	}
-
 	if (lq_sta->last_rate_n_flags & RATE_MCS_HT_MSK) {
 	if (lq_sta->last_rate_n_flags & RATE_MCS_HT_MSK) {
 		rate_idx -= IWL_FIRST_OFDM_RATE;
 		rate_idx -= IWL_FIRST_OFDM_RATE;
 		/* 6M and 9M shared same MCS index */
 		/* 6M and 9M shared same MCS index */
@@ -2495,16 +2495,25 @@ static void *rs_alloc_sta(void *priv_rate, struct ieee80211_sta *sta,
 	return lq_sta;
 	return lq_sta;
 }
 }
 
 
-static void rs_rate_init(void *priv_r, struct ieee80211_supported_band *sband,
-			 struct ieee80211_sta *sta, void *priv_sta)
+/*
+ * Called after adding a new station to initialize rate scaling
+ */
+void iwl_rs_rate_init(struct iwl_priv *priv, struct ieee80211_sta *sta, u8 sta_id)
 {
 {
 	int i, j;
 	int i, j;
-	struct iwl_priv *priv = (struct iwl_priv *)priv_r;
+	struct ieee80211_hw *hw = priv->hw;
 	struct ieee80211_conf *conf = &priv->hw->conf;
 	struct ieee80211_conf *conf = &priv->hw->conf;
 	struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
 	struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
-	struct iwl_lq_sta *lq_sta = priv_sta;
+	struct iwl_station_priv *sta_priv;
+	struct iwl_lq_sta *lq_sta;
+	struct ieee80211_supported_band *sband;
+
+	sta_priv = (struct iwl_station_priv *) sta->drv_priv;
+	lq_sta = &sta_priv->lq_sta;
+	sband = hw->wiphy->bands[conf->channel->band];
+
 
 
-	lq_sta->lq.sta_id = 0xff;
+	lq_sta->lq.sta_id = sta_id;
 
 
 	for (j = 0; j < LQ_SIZE; j++)
 	for (j = 0; j < LQ_SIZE; j++)
 		for (i = 0; i < IWL_RATE_COUNT; i++)
 		for (i = 0; i < IWL_RATE_COUNT; i++)
@@ -2516,33 +2525,13 @@ static void rs_rate_init(void *priv_r, struct ieee80211_supported_band *sband,
 		for (i = 0; i < IWL_RATE_COUNT; i++)
 		for (i = 0; i < IWL_RATE_COUNT; i++)
 			rs_rate_scale_clear_window(&lq_sta->lq_info[j].win[i]);
 			rs_rate_scale_clear_window(&lq_sta->lq_info[j].win[i]);
 
 
-	IWL_DEBUG_RATE(priv, "LQ: *** rate scale station global init ***\n");
+	IWL_DEBUG_RATE(priv, "LQ: *** rate scale station global init for station %d ***\n",
+		       sta_id);
 	/* TODO: what is a good starting rate for STA? About middle? Maybe not
 	/* TODO: what is a good starting rate for STA? About middle? Maybe not
 	 * the lowest or the highest rate.. Could consider using RSSI from
 	 * the lowest or the highest rate.. Could consider using RSSI from
 	 * previous packets? Need to have IEEE 802.1X auth succeed immediately
 	 * previous packets? Need to have IEEE 802.1X auth succeed immediately
 	 * after assoc.. */
 	 * after assoc.. */
 
 
-	lq_sta->ibss_sta_added = 0;
-	if (priv->iw_mode == NL80211_IFTYPE_AP) {
-		u8 sta_id = iwl_find_station(priv,
-								sta->addr);
-
-		/* for IBSS the call are from tasklet */
-		IWL_DEBUG_RATE(priv, "LQ: ADD station %pM\n", sta->addr);
-
-		if (sta_id == IWL_INVALID_STATION) {
-			IWL_DEBUG_RATE(priv, "LQ: ADD station %pM\n", sta->addr);
-			sta_id = iwl_add_station(priv, sta->addr, false,
-						CMD_ASYNC, ht_cap);
-		}
-		if ((sta_id != IWL_INVALID_STATION)) {
-			lq_sta->lq.sta_id = sta_id;
-			lq_sta->lq.rs_table[0].rate_n_flags = 0;
-		}
-		/* FIXME: this is w/a remove it later */
-		priv->assoc_station_added = 1;
-	}
-
 	lq_sta->is_dup = 0;
 	lq_sta->is_dup = 0;
 	lq_sta->max_rate_idx = -1;
 	lq_sta->max_rate_idx = -1;
 	lq_sta->missed_rate_counter = IWL_MISSED_RATE_MAX;
 	lq_sta->missed_rate_counter = IWL_MISSED_RATE_MAX;
@@ -2795,7 +2784,7 @@ static ssize_t rs_sta_dbgfs_scale_table_write(struct file *file,
 
 
 	if (lq_sta->dbg_fixed_rate) {
 	if (lq_sta->dbg_fixed_rate) {
 		rs_fill_link_cmd(NULL, lq_sta, lq_sta->dbg_fixed_rate);
 		rs_fill_link_cmd(NULL, lq_sta, lq_sta->dbg_fixed_rate);
-		iwl_send_lq_cmd(lq_sta->drv, &lq_sta->lq, CMD_ASYNC);
+		iwl_send_lq_cmd(lq_sta->drv, &lq_sta->lq, CMD_ASYNC, false);
 	}
 	}
 
 
 	return count;
 	return count;
@@ -2992,12 +2981,21 @@ static void rs_remove_debugfs(void *priv, void *priv_sta)
 }
 }
 #endif
 #endif
 
 
+/*
+ * Initialization of rate scaling information is done by driver after
+ * the station is added. Since mac80211 calls this function before a
+ * station is added we ignore it.
+ */
+static void rs_rate_init_stub(void *priv_r, struct ieee80211_supported_band *sband,
+			 struct ieee80211_sta *sta, void *priv_sta)
+{
+}
 static struct rate_control_ops rs_ops = {
 static struct rate_control_ops rs_ops = {
 	.module = NULL,
 	.module = NULL,
 	.name = RS_NAME,
 	.name = RS_NAME,
 	.tx_status = rs_tx_status,
 	.tx_status = rs_tx_status,
 	.get_rate = rs_get_rate,
 	.get_rate = rs_get_rate,
-	.rate_init = rs_rate_init,
+	.rate_init = rs_rate_init_stub,
 	.alloc = rs_alloc,
 	.alloc = rs_alloc,
 	.free = rs_free,
 	.free = rs_free,
 	.alloc_sta = rs_alloc_sta,
 	.alloc_sta = rs_alloc_sta,

+ 6 - 1
drivers/net/wireless/iwlwifi/iwl-agn-rs.h

@@ -403,7 +403,6 @@ struct iwl_lq_sta {
 	u8 is_green;
 	u8 is_green;
 	u8 is_dup;
 	u8 is_dup;
 	enum ieee80211_band band;
 	enum ieee80211_band band;
-	u8 ibss_sta_added;
 
 
 	/* The following are bitmaps of rates; IWL_RATE_6M_MASK, etc. */
 	/* The following are bitmaps of rates; IWL_RATE_6M_MASK, etc. */
 	u32 supp_rates;
 	u32 supp_rates;
@@ -478,6 +477,12 @@ static inline u8 iwl3945_get_prev_ieee_rate(u8 rate_index)
  */
  */
 extern void iwl3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id);
 extern void iwl3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id);
 
 
+/* Initialize station's rate scaling information after adding station */
+extern void iwl_rs_rate_init(struct iwl_priv *priv,
+			     struct ieee80211_sta *sta, u8 sta_id);
+extern void iwl3945_rs_rate_init(struct iwl_priv *priv,
+				 struct ieee80211_sta *sta, u8 sta_id);
+
 /**
 /**
  * iwl_rate_control_register - Register the rate control algorithm callbacks
  * iwl_rate_control_register - Register the rate control algorithm callbacks
  *
  *

+ 95 - 59
drivers/net/wireless/iwlwifi/iwl-agn.c

@@ -144,9 +144,6 @@ int iwl_commit_rxon(struct iwl_priv *priv)
 		return 0;
 		return 0;
 	}
 	}
 
 
-	/* station table will be cleared */
-	priv->assoc_station_added = 0;
-
 	/* If we are currently associated and the new config requires
 	/* If we are currently associated and the new config requires
 	 * an RXON_ASSOC and the new config wants the associated mask enabled,
 	 * an RXON_ASSOC and the new config wants the associated mask enabled,
 	 * we must clear the associated from the active configuration
 	 * we must clear the associated from the active configuration
@@ -166,6 +163,8 @@ int iwl_commit_rxon(struct iwl_priv *priv)
 			IWL_ERR(priv, "Error clearing ASSOC_MSK (%d)\n", ret);
 			IWL_ERR(priv, "Error clearing ASSOC_MSK (%d)\n", ret);
 			return ret;
 			return ret;
 		}
 		}
+		iwl_clear_ucode_stations(priv, false);
+		iwl_restore_stations(priv);
 	}
 	}
 
 
 	IWL_DEBUG_INFO(priv, "Sending RXON\n"
 	IWL_DEBUG_INFO(priv, "Sending RXON\n"
@@ -179,9 +178,8 @@ int iwl_commit_rxon(struct iwl_priv *priv)
 	iwl_set_rxon_hwcrypto(priv, !priv->cfg->mod_params->sw_crypto);
 	iwl_set_rxon_hwcrypto(priv, !priv->cfg->mod_params->sw_crypto);
 
 
 	/* Apply the new configuration
 	/* Apply the new configuration
-	 * RXON unassoc clears the station table in uCode, send it before
-	 * we add the bcast station. If assoc bit is set, we will send RXON
-	 * after having added the bcast and bssid station.
+	 * RXON unassoc clears the station table in uCode so restoration of
+	 * stations is needed after it (the RXON command) completes
 	 */
 	 */
 	if (!new_assoc) {
 	if (!new_assoc) {
 		ret = iwl_send_cmd_pdu(priv, REPLY_RXON,
 		ret = iwl_send_cmd_pdu(priv, REPLY_RXON,
@@ -190,35 +188,14 @@ int iwl_commit_rxon(struct iwl_priv *priv)
 			IWL_ERR(priv, "Error setting new RXON (%d)\n", ret);
 			IWL_ERR(priv, "Error setting new RXON (%d)\n", ret);
 			return ret;
 			return ret;
 		}
 		}
+		IWL_DEBUG_INFO(priv, "Return from !new_assoc RXON. \n");
 		memcpy(active_rxon, &priv->staging_rxon, sizeof(*active_rxon));
 		memcpy(active_rxon, &priv->staging_rxon, sizeof(*active_rxon));
+		iwl_clear_ucode_stations(priv, false);
+		iwl_restore_stations(priv);
 	}
 	}
 
 
-	iwl_clear_stations_table(priv);
-
 	priv->start_calib = 0;
 	priv->start_calib = 0;
-
-	/* Add the broadcast address so we can send broadcast frames */
-	priv->cfg->ops->lib->add_bcast_station(priv);
-
-
-	/* If we have set the ASSOC_MSK and we are in BSS mode then
-	 * add the IWL_AP_ID to the station rate table */
 	if (new_assoc) {
 	if (new_assoc) {
-		if (priv->iw_mode == NL80211_IFTYPE_STATION) {
-			ret = iwl_rxon_add_station(priv,
-					   priv->active_rxon.bssid_addr, 1);
-			if (ret == IWL_INVALID_STATION) {
-				IWL_ERR(priv,
-					"Error adding AP address for TX.\n");
-				return -EIO;
-			}
-			priv->assoc_station_added = 1;
-			if (priv->default_wep_key &&
-			    iwl_send_static_wepkey_cmd(priv, 0))
-				IWL_ERR(priv,
-					"Could not send WEP static key.\n");
-		}
-
 		/*
 		/*
 		 * allow CTS-to-self if possible for new association.
 		 * allow CTS-to-self if possible for new association.
 		 * this is relevant only for 5000 series and up,
 		 * this is relevant only for 5000 series and up,
@@ -2087,7 +2064,6 @@ static void iwl_alive_start(struct iwl_priv *priv)
 		goto restart;
 		goto restart;
 	}
 	}
 
 
-	iwl_clear_stations_table(priv);
 	ret = priv->cfg->ops->lib->alive_notify(priv);
 	ret = priv->cfg->ops->lib->alive_notify(priv);
 	if (ret) {
 	if (ret) {
 		IWL_WARN(priv,
 		IWL_WARN(priv,
@@ -2098,6 +2074,13 @@ static void iwl_alive_start(struct iwl_priv *priv)
 	/* After the ALIVE response, we can send host commands to the uCode */
 	/* After the ALIVE response, we can send host commands to the uCode */
 	set_bit(STATUS_ALIVE, &priv->status);
 	set_bit(STATUS_ALIVE, &priv->status);
 
 
+	if (priv->cfg->ops->lib->recover_from_tx_stall) {
+		/* Enable timer to monitor the driver queues */
+		mod_timer(&priv->monitor_recover,
+			jiffies +
+			msecs_to_jiffies(priv->cfg->monitor_recover_period));
+	}
+
 	if (iwl_is_rfkill(priv))
 	if (iwl_is_rfkill(priv))
 		return;
 		return;
 
 
@@ -2143,6 +2126,8 @@ static void iwl_alive_start(struct iwl_priv *priv)
 	wake_up_interruptible(&priv->wait_command_queue);
 	wake_up_interruptible(&priv->wait_command_queue);
 
 
 	iwl_power_update_mode(priv, true);
 	iwl_power_update_mode(priv, true);
+	IWL_DEBUG_INFO(priv, "Updated power mode\n");
+
 
 
 	return;
 	return;
 
 
@@ -2162,7 +2147,7 @@ static void __iwl_down(struct iwl_priv *priv)
 	if (!exit_pending)
 	if (!exit_pending)
 		set_bit(STATUS_EXIT_PENDING, &priv->status);
 		set_bit(STATUS_EXIT_PENDING, &priv->status);
 
 
-	iwl_clear_stations_table(priv);
+	iwl_clear_ucode_stations(priv, true);
 
 
 	/* Unblock any waiting calls */
 	/* Unblock any waiting calls */
 	wake_up_interruptible_all(&priv->wait_command_queue);
 	wake_up_interruptible_all(&priv->wait_command_queue);
@@ -2359,8 +2344,6 @@ static int __iwl_up(struct iwl_priv *priv)
 
 
 	for (i = 0; i < MAX_HW_RESTARTS; i++) {
 	for (i = 0; i < MAX_HW_RESTARTS; i++) {
 
 
-		iwl_clear_stations_table(priv);
-
 		/* load bootstrap state machine,
 		/* load bootstrap state machine,
 		 * load bootstrap program into processor's memory,
 		 * load bootstrap program into processor's memory,
 		 * prepare to load the "initialize" uCode */
 		 * prepare to load the "initialize" uCode */
@@ -2501,10 +2484,6 @@ void iwl_post_associate(struct iwl_priv *priv)
 		return;
 		return;
 	}
 	}
 
 
-	IWL_DEBUG_ASSOC(priv, "Associated as %d to: %pM\n",
-			priv->assoc_id, priv->active_rxon.bssid_addr);
-
-
 	if (test_bit(STATUS_EXIT_PENDING, &priv->status))
 	if (test_bit(STATUS_EXIT_PENDING, &priv->status))
 		return;
 		return;
 
 
@@ -2556,6 +2535,9 @@ void iwl_post_associate(struct iwl_priv *priv)
 
 
 	iwlcore_commit_rxon(priv);
 	iwlcore_commit_rxon(priv);
 
 
+	IWL_DEBUG_ASSOC(priv, "Associated as %d to: %pM\n",
+			priv->assoc_id, priv->active_rxon.bssid_addr);
+
 	switch (priv->iw_mode) {
 	switch (priv->iw_mode) {
 	case NL80211_IFTYPE_STATION:
 	case NL80211_IFTYPE_STATION:
 		break;
 		break;
@@ -2565,7 +2547,7 @@ void iwl_post_associate(struct iwl_priv *priv)
 		/* assume default assoc id */
 		/* assume default assoc id */
 		priv->assoc_id = 1;
 		priv->assoc_id = 1;
 
 
-		iwl_rxon_add_station(priv, priv->bssid, 0);
+		iwl_add_local_station(priv, priv->bssid, true);
 		iwl_send_beacon_cmd(priv);
 		iwl_send_beacon_cmd(priv);
 
 
 		break;
 		break;
@@ -2576,9 +2558,6 @@ void iwl_post_associate(struct iwl_priv *priv)
 		break;
 		break;
 	}
 	}
 
 
-	if (priv->iw_mode == NL80211_IFTYPE_ADHOC)
-		priv->assoc_station_added = 1;
-
 	spin_lock_irqsave(&priv->lock, flags);
 	spin_lock_irqsave(&priv->lock, flags);
 	iwl_activate_qos(priv, 0);
 	iwl_activate_qos(priv, 0);
 	spin_unlock_irqrestore(&priv->lock, flags);
 	spin_unlock_irqrestore(&priv->lock, flags);
@@ -2937,10 +2916,21 @@ static int iwl_mac_ampdu_action(struct ieee80211_hw *hw,
 			return ret;
 			return ret;
 	case IEEE80211_AMPDU_TX_START:
 	case IEEE80211_AMPDU_TX_START:
 		IWL_DEBUG_HT(priv, "start Tx\n");
 		IWL_DEBUG_HT(priv, "start Tx\n");
-		return iwl_tx_agg_start(priv, sta->addr, tid, ssn);
+		ret = iwl_tx_agg_start(priv, sta->addr, tid, ssn);
+		if (ret == 0) {
+			priv->_agn.agg_tids_count++;
+			IWL_DEBUG_HT(priv, "priv->_agn.agg_tids_count = %u\n",
+				     priv->_agn.agg_tids_count);
+		}
+		return ret;
 	case IEEE80211_AMPDU_TX_STOP:
 	case IEEE80211_AMPDU_TX_STOP:
 		IWL_DEBUG_HT(priv, "stop Tx\n");
 		IWL_DEBUG_HT(priv, "stop Tx\n");
 		ret = iwl_tx_agg_stop(priv, sta->addr, tid);
 		ret = iwl_tx_agg_stop(priv, sta->addr, tid);
+		if ((ret == 0) && (priv->_agn.agg_tids_count > 0)) {
+			priv->_agn.agg_tids_count--;
+			IWL_DEBUG_HT(priv, "priv->_agn.agg_tids_count = %u\n",
+				     priv->_agn.agg_tids_count);
+		}
 		if (test_bit(STATUS_EXIT_PENDING, &priv->status))
 		if (test_bit(STATUS_EXIT_PENDING, &priv->status))
 			return 0;
 			return 0;
 		else
 		else
@@ -2977,18 +2967,7 @@ static void iwl_mac_sta_notify(struct ieee80211_hw *hw,
 	struct iwl_station_priv *sta_priv = (void *)sta->drv_priv;
 	struct iwl_station_priv *sta_priv = (void *)sta->drv_priv;
 	int sta_id;
 	int sta_id;
 
 
-	/*
-	 * TODO: We really should use this callback to
-	 *	 actually maintain the station table in
-	 *	 the device.
-	 */
-
 	switch (cmd) {
 	switch (cmd) {
-	case STA_NOTIFY_ADD:
-		atomic_set(&sta_priv->pending_frames, 0);
-		if (vif->type == NL80211_IFTYPE_AP)
-			sta_priv->client = true;
-		break;
 	case STA_NOTIFY_SLEEP:
 	case STA_NOTIFY_SLEEP:
 		WARN_ON(!sta_priv->client);
 		WARN_ON(!sta_priv->client);
 		sta_priv->asleep = true;
 		sta_priv->asleep = true;
@@ -3009,6 +2988,55 @@ static void iwl_mac_sta_notify(struct ieee80211_hw *hw,
 	}
 	}
 }
 }
 
 
+/**
+ * iwl_restore_wepkeys - Restore WEP keys to device
+ */
+static void iwl_restore_wepkeys(struct iwl_priv *priv)
+{
+	mutex_lock(&priv->mutex);
+	if (priv->iw_mode == NL80211_IFTYPE_STATION &&
+	    priv->default_wep_key &&
+	    iwl_send_static_wepkey_cmd(priv, 0))
+		IWL_ERR(priv, "Could not send WEP static key\n");
+	mutex_unlock(&priv->mutex);
+}
+
+static int iwlagn_mac_sta_add(struct ieee80211_hw *hw,
+			      struct ieee80211_vif *vif,
+			      struct ieee80211_sta *sta)
+{
+	struct iwl_priv *priv = hw->priv;
+	struct iwl_station_priv *sta_priv = (void *)sta->drv_priv;
+	bool is_ap = priv->iw_mode == NL80211_IFTYPE_STATION;
+	int ret;
+	u8 sta_id;
+
+	IWL_DEBUG_INFO(priv, "received request to add station %pM\n",
+			sta->addr);
+
+	atomic_set(&sta_priv->pending_frames, 0);
+	if (vif->type == NL80211_IFTYPE_AP)
+		sta_priv->client = true;
+
+	ret = iwl_add_station_common(priv, sta->addr, is_ap, &sta->ht_cap,
+				     &sta_id);
+	if (ret) {
+		IWL_ERR(priv, "Unable to add station %pM (%d)\n",
+			sta->addr, ret);
+		/* Should we return success if return code is EEXIST ? */
+		return ret;
+	}
+
+	iwl_restore_wepkeys(priv);
+
+	/* Initialize rate scaling */
+	IWL_DEBUG_INFO(priv, "Initializing rate scaling for station %pM \n",
+		       sta->addr);
+	iwl_rs_rate_init(priv, sta, sta_id);
+
+	return ret;
+}
+
 /*****************************************************************************
 /*****************************************************************************
  *
  *
  * sysfs attributes
  * sysfs attributes
@@ -3214,6 +3242,13 @@ static void iwl_setup_deferred_work(struct iwl_priv *priv)
 	priv->ucode_trace.data = (unsigned long)priv;
 	priv->ucode_trace.data = (unsigned long)priv;
 	priv->ucode_trace.function = iwl_bg_ucode_trace;
 	priv->ucode_trace.function = iwl_bg_ucode_trace;
 
 
+	if (priv->cfg->ops->lib->recover_from_tx_stall) {
+		init_timer(&priv->monitor_recover);
+		priv->monitor_recover.data = (unsigned long)priv;
+		priv->monitor_recover.function =
+			priv->cfg->ops->lib->recover_from_tx_stall;
+	}
+
 	if (!priv->cfg->use_isr_legacy)
 	if (!priv->cfg->use_isr_legacy)
 		tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
 		tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
 			iwl_irq_tasklet, (unsigned long)priv);
 			iwl_irq_tasklet, (unsigned long)priv);
@@ -3233,6 +3268,8 @@ static void iwl_cancel_deferred_work(struct iwl_priv *priv)
 	cancel_work_sync(&priv->beacon_update);
 	cancel_work_sync(&priv->beacon_update);
 	del_timer_sync(&priv->statistics_periodic);
 	del_timer_sync(&priv->statistics_periodic);
 	del_timer_sync(&priv->ucode_trace);
 	del_timer_sync(&priv->ucode_trace);
+	if (priv->cfg->ops->lib->recover_from_tx_stall)
+		del_timer_sync(&priv->monitor_recover);
 }
 }
 
 
 static void iwl_init_hw_rates(struct iwl_priv *priv,
 static void iwl_init_hw_rates(struct iwl_priv *priv,
@@ -3270,9 +3307,6 @@ static int iwl_init_drv(struct iwl_priv *priv)
 	mutex_init(&priv->mutex);
 	mutex_init(&priv->mutex);
 	mutex_init(&priv->sync_cmd_mutex);
 	mutex_init(&priv->sync_cmd_mutex);
 
 
-	/* Clear the driver's (not device's) station table */
-	iwl_clear_stations_table(priv);
-
 	priv->ieee_channels = NULL;
 	priv->ieee_channels = NULL;
 	priv->ieee_rates = NULL;
 	priv->ieee_rates = NULL;
 	priv->band = IEEE80211_BAND_2GHZ;
 	priv->band = IEEE80211_BAND_2GHZ;
@@ -3280,6 +3314,7 @@ static int iwl_init_drv(struct iwl_priv *priv)
 	priv->iw_mode = NL80211_IFTYPE_STATION;
 	priv->iw_mode = NL80211_IFTYPE_STATION;
 	priv->current_ht_config.smps = IEEE80211_SMPS_STATIC;
 	priv->current_ht_config.smps = IEEE80211_SMPS_STATIC;
 	priv->missed_beacon_threshold = IWL_MISSED_BEACON_THRESHOLD_DEF;
 	priv->missed_beacon_threshold = IWL_MISSED_BEACON_THRESHOLD_DEF;
+	priv->_agn.agg_tids_count = 0;
 
 
 	/* initialize force reset */
 	/* initialize force reset */
 	priv->force_reset[IWL_RF_RESET].reset_duration =
 	priv->force_reset[IWL_RF_RESET].reset_duration =
@@ -3365,6 +3400,8 @@ static struct ieee80211_ops iwl_hw_ops = {
 	.ampdu_action = iwl_mac_ampdu_action,
 	.ampdu_action = iwl_mac_ampdu_action,
 	.hw_scan = iwl_mac_hw_scan,
 	.hw_scan = iwl_mac_hw_scan,
 	.sta_notify = iwl_mac_sta_notify,
 	.sta_notify = iwl_mac_sta_notify,
+	.sta_add = iwlagn_mac_sta_add,
+	.sta_remove = iwl_mac_sta_remove,
 };
 };
 
 
 static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
@@ -3468,7 +3505,7 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 	iwl_write32(priv, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET);
 	iwl_write32(priv, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET);
 
 
 	iwl_hw_detect(priv);
 	iwl_hw_detect(priv);
-	IWL_INFO(priv, "Detected Intel Wireless WiFi Link %s REV=0x%X\n",
+	IWL_INFO(priv, "Detected %s, REV=0x%X\n",
 		priv->cfg->name, priv->hw_rev);
 		priv->cfg->name, priv->hw_rev);
 
 
 	/* We disable the RETRY_TIMEOUT register (0x41) to keep
 	/* We disable the RETRY_TIMEOUT register (0x41) to keep
@@ -3649,7 +3686,6 @@ static void __devexit iwl_pci_remove(struct pci_dev *pdev)
 		iwl_rx_queue_free(priv, &priv->rxq);
 		iwl_rx_queue_free(priv, &priv->rxq);
 	iwl_hw_txq_ctx_free(priv);
 	iwl_hw_txq_ctx_free(priv);
 
 
-	iwl_clear_stations_table(priv);
 	iwl_eeprom_free(priv);
 	iwl_eeprom_free(priv);
 
 
 
 

+ 99 - 3
drivers/net/wireless/iwlwifi/iwl-core.c

@@ -2283,8 +2283,6 @@ static int iwl_set_mode(struct iwl_priv *priv, struct ieee80211_vif *vif)
 
 
 	memcpy(priv->staging_rxon.node_addr, priv->mac_addr, ETH_ALEN);
 	memcpy(priv->staging_rxon.node_addr, priv->mac_addr, ETH_ALEN);
 
 
-	iwl_clear_stations_table(priv);
-
 	return iwlcore_commit_rxon(priv);
 	return iwlcore_commit_rxon(priv);
 }
 }
 
 
@@ -2317,6 +2315,10 @@ int iwl_mac_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
 	err = iwl_set_mode(priv, vif);
 	err = iwl_set_mode(priv, vif);
 	if (err)
 	if (err)
 		goto out_err;
 		goto out_err;
+
+	/* Add the broadcast address so we can send broadcast frames */
+	priv->cfg->ops->lib->add_bcast_station(priv);
+
 	goto out;
 	goto out;
 
 
  out_err:
  out_err:
@@ -2339,6 +2341,8 @@ void iwl_mac_remove_interface(struct ieee80211_hw *hw,
 
 
 	mutex_lock(&priv->mutex);
 	mutex_lock(&priv->mutex);
 
 
+	iwl_clear_ucode_stations(priv, true);
+
 	if (iwl_is_ready_rf(priv)) {
 	if (iwl_is_ready_rf(priv)) {
 		iwl_scan_cancel_timeout(priv, 100);
 		iwl_scan_cancel_timeout(priv, 100);
 		priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
 		priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
@@ -2526,7 +2530,6 @@ void iwl_mac_reset_tsf(struct ieee80211_hw *hw)
 	spin_lock_irqsave(&priv->lock, flags);
 	spin_lock_irqsave(&priv->lock, flags);
 	priv->assoc_id = 0;
 	priv->assoc_id = 0;
 	priv->assoc_capability = 0;
 	priv->assoc_capability = 0;
-	priv->assoc_station_added = 0;
 
 
 	/* new association get rid of ibss beacon skb */
 	/* new association get rid of ibss beacon skb */
 	if (priv->ibss_beacon)
 	if (priv->ibss_beacon)
@@ -3048,6 +3051,99 @@ int iwl_force_reset(struct iwl_priv *priv, int mode)
 	}
 	}
 	return 0;
 	return 0;
 }
 }
+EXPORT_SYMBOL(iwl_force_reset);
+
+/**
+ * iwl_bg_monitor_recover - Timer callback to check for stuck queue and recover
+ *
+ * During normal condition (no queue is stuck), the timer is continually set to
+ * execute every monitor_recover_period milliseconds after the last timer
+ * expired.  When the queue read_ptr is at the same place, the timer is
+ * shorten to 100mSecs.  This is
+ *      1) to reduce the chance that the read_ptr may wrap around (not stuck)
+ *      2) to detect the stuck queues quicker before the station and AP can
+ *      disassociate each other.
+ *
+ * This function monitors all the tx queues and recover from it if any
+ * of the queues are stuck.
+ * 1. It first check the cmd queue for stuck conditions.  If it is stuck,
+ *      it will recover by resetting the firmware and return.
+ * 2. Then, it checks for station association.  If it associates it will check
+ *      other queues.  If any queue is stuck, it will recover by resetting
+ *      the firmware.
+ * Note: It the number of times the queue read_ptr to be at the same place to
+ *      be MAX_REPEAT+1 in order to consider to be stuck.
+ */
+/*
+ * The maximum number of times the read pointer of the tx queue at the
+ * same place without considering to be stuck.
+ */
+#define MAX_REPEAT      (2)
+static int iwl_check_stuck_queue(struct iwl_priv *priv, int cnt)
+{
+	struct iwl_tx_queue *txq;
+	struct iwl_queue *q;
+
+	txq = &priv->txq[cnt];
+	q = &txq->q;
+	/* queue is empty, skip */
+	if (q->read_ptr != q->write_ptr) {
+		if (q->read_ptr == q->last_read_ptr) {
+			/* a queue has not been read from last time */
+			if (q->repeat_same_read_ptr > MAX_REPEAT) {
+				IWL_ERR(priv,
+					"queue %d stuck %d time. Fw reload.\n",
+					q->id, q->repeat_same_read_ptr);
+				q->repeat_same_read_ptr = 0;
+				iwl_force_reset(priv, IWL_FW_RESET);
+			} else {
+				q->repeat_same_read_ptr++;
+				IWL_DEBUG_RADIO(priv,
+						"queue %d, not read %d time\n",
+						q->id,
+						q->repeat_same_read_ptr);
+				mod_timer(&priv->monitor_recover, jiffies +
+					msecs_to_jiffies(IWL_ONE_HUNDRED_MSECS));
+			}
+			return 1;
+		} else {
+			q->last_read_ptr = q->read_ptr;
+			q->repeat_same_read_ptr = 0;
+		}
+	}
+	return 0;
+}
+
+void iwl_bg_monitor_recover(unsigned long data)
+{
+	struct iwl_priv *priv = (struct iwl_priv *)data;
+	int cnt;
+
+	if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+		return;
+
+	/* monitor and check for stuck cmd queue */
+	if (iwl_check_stuck_queue(priv, IWL_CMD_QUEUE_NUM))
+		return;
+
+	/* monitor and check for other stuck queues */
+	if (iwl_is_associated(priv)) {
+		for (cnt = 0; cnt < priv->hw_params.max_txq_num; cnt++) {
+			/* skip as we already checked the command queue */
+			if (cnt == IWL_CMD_QUEUE_NUM)
+				continue;
+			if (iwl_check_stuck_queue(priv, cnt))
+				return;
+		}
+	}
+	/*
+	 * Reschedule the timer to occur in
+	 * priv->cfg->monitor_recover_period
+	 */
+	mod_timer(&priv->monitor_recover,
+		jiffies + msecs_to_jiffies(priv->cfg->monitor_recover_period));
+}
+EXPORT_SYMBOL(iwl_bg_monitor_recover);
 
 
 #ifdef CONFIG_PM
 #ifdef CONFIG_PM
 
 

+ 18 - 1
drivers/net/wireless/iwlwifi/iwl-core.h

@@ -191,6 +191,14 @@ struct iwl_lib_ops {
 	struct iwl_temp_ops temp_ops;
 	struct iwl_temp_ops temp_ops;
 	/* station management */
 	/* station management */
 	void (*add_bcast_station)(struct iwl_priv *priv);
 	void (*add_bcast_station)(struct iwl_priv *priv);
+	/* recover from tx queue stall */
+	void (*recover_from_tx_stall)(unsigned long data);
+	/* check for plcp health */
+	bool (*check_plcp_health)(struct iwl_priv *priv,
+					struct iwl_rx_packet *pkt);
+	/* check for ack health */
+	bool (*check_ack_health)(struct iwl_priv *priv,
+					struct iwl_rx_packet *pkt);
 };
 };
 
 
 struct iwl_led_ops {
 struct iwl_led_ops {
@@ -295,6 +303,8 @@ struct iwl_cfg {
 	const bool support_wimax_coexist;
 	const bool support_wimax_coexist;
 	u8 plcp_delta_threshold;
 	u8 plcp_delta_threshold;
 	s32 chain_noise_scale;
 	s32 chain_noise_scale;
+	/* timer period for monitor the driver queues */
+	u32 monitor_recover_period;
 };
 };
 
 
 /***************************
 /***************************
@@ -430,6 +440,10 @@ void iwl_rx_missed_beacon_notif(struct iwl_priv *priv,
 			       struct iwl_rx_mem_buffer *rxb);
 			       struct iwl_rx_mem_buffer *rxb);
 void iwl_rx_spectrum_measure_notif(struct iwl_priv *priv,
 void iwl_rx_spectrum_measure_notif(struct iwl_priv *priv,
 					  struct iwl_rx_mem_buffer *rxb);
 					  struct iwl_rx_mem_buffer *rxb);
+bool iwl_good_plcp_health(struct iwl_priv *priv,
+				 struct iwl_rx_packet *pkt);
+bool iwl_good_ack_health(struct iwl_priv *priv,
+				 struct iwl_rx_packet *pkt);
 void iwl_rx_statistics(struct iwl_priv *priv,
 void iwl_rx_statistics(struct iwl_priv *priv,
 			      struct iwl_rx_mem_buffer *rxb);
 			      struct iwl_rx_mem_buffer *rxb);
 void iwl_reply_statistics(struct iwl_priv *priv,
 void iwl_reply_statistics(struct iwl_priv *priv,
@@ -568,6 +582,9 @@ static inline u16 iwl_pcie_link_ctl(struct iwl_priv *priv)
 	pci_read_config_word(priv->pci_dev, pos + PCI_EXP_LNKCTL, &pci_lnk_ctl);
 	pci_read_config_word(priv->pci_dev, pos + PCI_EXP_LNKCTL, &pci_lnk_ctl);
 	return pci_lnk_ctl;
 	return pci_lnk_ctl;
 }
 }
+
+void iwl_bg_monitor_recover(unsigned long data);
+
 #ifdef CONFIG_PM
 #ifdef CONFIG_PM
 int iwl_pci_suspend(struct pci_dev *pdev, pm_message_t state);
 int iwl_pci_suspend(struct pci_dev *pdev, pm_message_t state);
 int iwl_pci_resume(struct pci_dev *pdev);
 int iwl_pci_resume(struct pci_dev *pdev);
@@ -667,7 +684,7 @@ extern int iwl_send_statistics_request(struct iwl_priv *priv,
 				       u8 flags, bool clear);
 				       u8 flags, bool clear);
 extern int iwl_verify_ucode(struct iwl_priv *priv);
 extern int iwl_verify_ucode(struct iwl_priv *priv);
 extern int iwl_send_lq_cmd(struct iwl_priv *priv,
 extern int iwl_send_lq_cmd(struct iwl_priv *priv,
-		struct iwl_link_quality_cmd *lq, u8 flags);
+		struct iwl_link_quality_cmd *lq, u8 flags, bool init);
 extern void iwl_rx_reply_rx(struct iwl_priv *priv,
 extern void iwl_rx_reply_rx(struct iwl_priv *priv,
 		struct iwl_rx_mem_buffer *rxb);
 		struct iwl_rx_mem_buffer *rxb);
 extern void iwl_rx_reply_rx_phy(struct iwl_priv *priv,
 extern void iwl_rx_reply_rx_phy(struct iwl_priv *priv,

+ 22 - 1
drivers/net/wireless/iwlwifi/iwl-dev.h

@@ -183,6 +183,10 @@ struct iwl_queue {
 	int n_bd;              /* number of BDs in this queue */
 	int n_bd;              /* number of BDs in this queue */
 	int write_ptr;       /* 1-st empty entry (index) host_w*/
 	int write_ptr;       /* 1-st empty entry (index) host_w*/
 	int read_ptr;         /* last used entry (index) host_r*/
 	int read_ptr;         /* last used entry (index) host_r*/
+	/* use for monitoring and recovering the stuck queue */
+	int last_read_ptr;      /* storing the last read_ptr */
+	/* number of time read_ptr and last_read_ptr are the same */
+	u8 repeat_same_read_ptr;
 	dma_addr_t dma_addr;   /* physical addr for BD's */
 	dma_addr_t dma_addr;   /* physical addr for BD's */
 	int n_window;	       /* safe queue window */
 	int n_window;	       /* safe queue window */
 	u32 id;
 	u32 id;
@@ -544,11 +548,18 @@ struct iwl_qos_info {
 	struct iwl_qosparam_cmd def_qos_parm;
 	struct iwl_qosparam_cmd def_qos_parm;
 };
 };
 
 
+/*
+ * Structure should be accessed with sta_lock held. When station addition
+ * is in progress (IWL_STA_UCODE_INPROGRESS) it is possible to access only
+ * the commands (iwl_addsta_cmd and iwl_link_quality_cmd) without sta_lock
+ * held.
+ */
 struct iwl_station_entry {
 struct iwl_station_entry {
 	struct iwl_addsta_cmd sta;
 	struct iwl_addsta_cmd sta;
 	struct iwl_tid_data tid[MAX_TID_COUNT];
 	struct iwl_tid_data tid[MAX_TID_COUNT];
 	u8 used;
 	u8 used;
 	struct iwl_hw_key keyinfo;
 	struct iwl_hw_key keyinfo;
+	struct iwl_link_quality_cmd *lq;
 };
 };
 
 
 /*
 /*
@@ -1037,6 +1048,11 @@ struct iwl_event_log {
 #define IWL_DELAY_NEXT_FORCE_RF_RESET  (HZ*3)
 #define IWL_DELAY_NEXT_FORCE_RF_RESET  (HZ*3)
 #define IWL_DELAY_NEXT_FORCE_FW_RELOAD (HZ*5)
 #define IWL_DELAY_NEXT_FORCE_FW_RELOAD (HZ*5)
 
 
+/* timer constants use to monitor and recover stuck tx queues in mSecs */
+#define IWL_MONITORING_PERIOD  (1000)
+#define IWL_ONE_HUNDRED_MSECS   (100)
+#define IWL_SIXTY_SECS          (60000)
+
 enum iwl_reset {
 enum iwl_reset {
 	IWL_RF_RESET = 0,
 	IWL_RF_RESET = 0,
 	IWL_FW_RESET,
 	IWL_FW_RESET,
@@ -1163,7 +1179,6 @@ struct iwl_priv {
 
 
 	u16 active_rate;
 	u16 active_rate;
 
 
-	u8 assoc_station_added;
 	u8 start_calib;
 	u8 start_calib;
 	struct iwl_sensitivity_data sensitivity_data;
 	struct iwl_sensitivity_data sensitivity_data;
 	struct iwl_chain_noise_data chain_noise_data;
 	struct iwl_chain_noise_data chain_noise_data;
@@ -1285,6 +1300,11 @@ struct iwl_priv {
 			int ict_index;
 			int ict_index;
 			u32 inta;
 			u32 inta;
 			bool use_ict;
 			bool use_ict;
+			/*
+			 * reporting the number of tids has AGG on. 0 means
+			 * no AGGREGATION
+			 */
+			u8 agg_tids_count;
 		} _agn;
 		} _agn;
 #endif
 #endif
 	};
 	};
@@ -1348,6 +1368,7 @@ struct iwl_priv {
 	struct work_struct run_time_calib_work;
 	struct work_struct run_time_calib_work;
 	struct timer_list statistics_periodic;
 	struct timer_list statistics_periodic;
 	struct timer_list ucode_trace;
 	struct timer_list ucode_trace;
+	struct timer_list monitor_recover;
 	bool hw_ready;
 	bool hw_ready;
 
 
 	struct iwl_event_log event_log;
 	struct iwl_event_log event_log;

+ 127 - 28
drivers/net/wireless/iwlwifi/iwl-rx.c

@@ -616,29 +616,77 @@ static void iwl_accumulative_statistics(struct iwl_priv *priv,
 
 
 #define REG_RECALIB_PERIOD (60)
 #define REG_RECALIB_PERIOD (60)
 
 
-#define PLCP_MSG "plcp_err exceeded %u, %u, %u, %u, %u, %d, %u mSecs\n"
-void iwl_rx_statistics(struct iwl_priv *priv,
-			      struct iwl_rx_mem_buffer *rxb)
+/* the threshold ratio of actual_ack_cnt to expected_ack_cnt in percent */
+#define ACK_CNT_RATIO (50)
+#define BA_TIMEOUT_CNT (5)
+#define BA_TIMEOUT_MAX (16)
+
+#if defined(CONFIG_IWLAGN) || defined(CONFIG_IWLAGN_MODULE)
+/**
+ * iwl_good_ack_health - checks for ACK count ratios, BA timeout retries.
+ *
+ * When the ACK count ratio is 0 and aggregated BA timeout retries exceeding
+ * the BA_TIMEOUT_MAX, reload firmware and bring system back to normal
+ * operation state.
+ */
+bool iwl_good_ack_health(struct iwl_priv *priv,
+				struct iwl_rx_packet *pkt)
 {
 {
-	int change;
-	struct iwl_rx_packet *pkt = rxb_addr(rxb);
+	bool rc = true;
+	int actual_ack_cnt_delta, expected_ack_cnt_delta;
+	int ba_timeout_delta;
+
+	actual_ack_cnt_delta =
+		le32_to_cpu(pkt->u.stats.tx.actual_ack_cnt) -
+		le32_to_cpu(priv->statistics.tx.actual_ack_cnt);
+	expected_ack_cnt_delta =
+		le32_to_cpu(pkt->u.stats.tx.expected_ack_cnt) -
+		le32_to_cpu(priv->statistics.tx.expected_ack_cnt);
+	ba_timeout_delta =
+		le32_to_cpu(pkt->u.stats.tx.agg.ba_timeout) -
+		le32_to_cpu(priv->statistics.tx.agg.ba_timeout);
+	if ((priv->_agn.agg_tids_count > 0) &&
+	    (expected_ack_cnt_delta > 0) &&
+	    (((actual_ack_cnt_delta * 100) / expected_ack_cnt_delta)
+		< ACK_CNT_RATIO) &&
+	    (ba_timeout_delta > BA_TIMEOUT_CNT)) {
+		IWL_DEBUG_RADIO(priv, "actual_ack_cnt delta = %d,"
+				" expected_ack_cnt = %d\n",
+				actual_ack_cnt_delta, expected_ack_cnt_delta);
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+		IWL_DEBUG_RADIO(priv, "rx_detected_cnt delta = %d\n",
+				priv->delta_statistics.tx.rx_detected_cnt);
+		IWL_DEBUG_RADIO(priv,
+				"ack_or_ba_timeout_collision delta = %d\n",
+				priv->delta_statistics.tx.
+				ack_or_ba_timeout_collision);
+#endif
+		IWL_DEBUG_RADIO(priv, "agg ba_timeout delta = %d\n",
+				ba_timeout_delta);
+		if (!actual_ack_cnt_delta &&
+		    (ba_timeout_delta >= BA_TIMEOUT_MAX))
+			rc = false;
+	}
+	return rc;
+}
+EXPORT_SYMBOL(iwl_good_ack_health);
+#endif
+
+/**
+ * iwl_good_plcp_health - checks for plcp error.
+ *
+ * When the plcp error is exceeding the thresholds, reset the radio
+ * to improve the throughput.
+ */
+bool iwl_good_plcp_health(struct iwl_priv *priv,
+				struct iwl_rx_packet *pkt)
+{
+	bool rc = true;
 	int combined_plcp_delta;
 	int combined_plcp_delta;
 	unsigned int plcp_msec;
 	unsigned int plcp_msec;
 	unsigned long plcp_received_jiffies;
 	unsigned long plcp_received_jiffies;
 
 
-	IWL_DEBUG_RX(priv, "Statistics notification received (%d vs %d).\n",
-		     (int)sizeof(priv->statistics),
-		     le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK);
-
-	change = ((priv->statistics.general.temperature !=
-		   pkt->u.stats.general.temperature) ||
-		  ((priv->statistics.flag &
-		    STATISTICS_REPLY_FLG_HT40_MODE_MSK) !=
-		   (pkt->u.stats.flag & STATISTICS_REPLY_FLG_HT40_MODE_MSK)));
-
-#ifdef CONFIG_IWLWIFI_DEBUG
-	iwl_accumulative_statistics(priv, (__le32 *)&pkt->u.stats);
-#endif
 	/*
 	/*
 	 * check for plcp_err and trigger radio reset if it exceeds
 	 * check for plcp_err and trigger radio reset if it exceeds
 	 * the plcp error threshold plcp_delta.
 	 * the plcp error threshold plcp_delta.
@@ -659,11 +707,11 @@ void iwl_rx_statistics(struct iwl_priv *priv,
 			le32_to_cpu(priv->statistics.rx.ofdm_ht.plcp_err));
 			le32_to_cpu(priv->statistics.rx.ofdm_ht.plcp_err));
 
 
 		if ((combined_plcp_delta > 0) &&
 		if ((combined_plcp_delta > 0) &&
-			((combined_plcp_delta * 100) / plcp_msec) >
+		    ((combined_plcp_delta * 100) / plcp_msec) >
 			priv->cfg->plcp_delta_threshold) {
 			priv->cfg->plcp_delta_threshold) {
 			/*
 			/*
-			 * if plcp_err exceed the threshold, the following
-			 * data is printed in csv format:
+			 * if plcp_err exceed the threshold,
+			 * the following data is printed in csv format:
 			 *    Text: plcp_err exceeded %d,
 			 *    Text: plcp_err exceeded %d,
 			 *    Received ofdm.plcp_err,
 			 *    Received ofdm.plcp_err,
 			 *    Current ofdm.plcp_err,
 			 *    Current ofdm.plcp_err,
@@ -672,22 +720,73 @@ void iwl_rx_statistics(struct iwl_priv *priv,
 			 *    combined_plcp_delta,
 			 *    combined_plcp_delta,
 			 *    plcp_msec
 			 *    plcp_msec
 			 */
 			 */
-			IWL_DEBUG_RADIO(priv, PLCP_MSG,
+			IWL_DEBUG_RADIO(priv, "plcp_err exceeded %u, "
+				"%u, %u, %u, %u, %d, %u mSecs\n",
 				priv->cfg->plcp_delta_threshold,
 				priv->cfg->plcp_delta_threshold,
 				le32_to_cpu(pkt->u.stats.rx.ofdm.plcp_err),
 				le32_to_cpu(pkt->u.stats.rx.ofdm.plcp_err),
 				le32_to_cpu(priv->statistics.rx.ofdm.plcp_err),
 				le32_to_cpu(priv->statistics.rx.ofdm.plcp_err),
 				le32_to_cpu(pkt->u.stats.rx.ofdm_ht.plcp_err),
 				le32_to_cpu(pkt->u.stats.rx.ofdm_ht.plcp_err),
 				le32_to_cpu(
 				le32_to_cpu(
-					priv->statistics.rx.ofdm_ht.plcp_err),
+				  priv->statistics.rx.ofdm_ht.plcp_err),
 				combined_plcp_delta, plcp_msec);
 				combined_plcp_delta, plcp_msec);
+			rc = false;
+		}
+	}
+	return rc;
+}
+EXPORT_SYMBOL(iwl_good_plcp_health);
 
 
-			/*
-			 * Reset the RF radio due to the high plcp
-			 * error rate
-			 */
-			iwl_force_reset(priv, IWL_RF_RESET);
+static void iwl_recover_from_statistics(struct iwl_priv *priv,
+				struct iwl_rx_packet *pkt)
+{
+	if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+		return;
+	if (iwl_is_associated(priv)) {
+		if (priv->cfg->ops->lib->check_ack_health) {
+			if (!priv->cfg->ops->lib->check_ack_health(
+			    priv, pkt)) {
+				/*
+				 * low ack count detected
+				 * restart Firmware
+				 */
+				IWL_ERR(priv, "low ack count detected, "
+					"restart firmware\n");
+				iwl_force_reset(priv, IWL_FW_RESET);
+			}
+		} else if (priv->cfg->ops->lib->check_plcp_health) {
+			if (!priv->cfg->ops->lib->check_plcp_health(
+			    priv, pkt)) {
+				/*
+				 * high plcp error detected
+				 * reset Radio
+				 */
+				iwl_force_reset(priv, IWL_RF_RESET);
+			}
 		}
 		}
 	}
 	}
+}
+
+void iwl_rx_statistics(struct iwl_priv *priv,
+			      struct iwl_rx_mem_buffer *rxb)
+{
+	int change;
+	struct iwl_rx_packet *pkt = rxb_addr(rxb);
+
+
+	IWL_DEBUG_RX(priv, "Statistics notification received (%d vs %d).\n",
+		     (int)sizeof(priv->statistics),
+		     le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK);
+
+	change = ((priv->statistics.general.temperature !=
+		   pkt->u.stats.general.temperature) ||
+		  ((priv->statistics.flag &
+		    STATISTICS_REPLY_FLG_HT40_MODE_MSK) !=
+		   (pkt->u.stats.flag & STATISTICS_REPLY_FLG_HT40_MODE_MSK)));
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+	iwl_accumulative_statistics(priv, (__le32 *)&pkt->u.stats);
+#endif
+	iwl_recover_from_statistics(priv, pkt);
 
 
 	memcpy(&priv->statistics, &pkt->u.stats, sizeof(priv->statistics));
 	memcpy(&priv->statistics, &pkt->u.stats, sizeof(priv->statistics));
 
 

+ 375 - 255
drivers/net/wireless/iwlwifi/iwl-sta.c

@@ -29,14 +29,12 @@
 
 
 #include <net/mac80211.h>
 #include <net/mac80211.h>
 #include <linux/etherdevice.h>
 #include <linux/etherdevice.h>
+#include <linux/sched.h>
 
 
 #include "iwl-dev.h"
 #include "iwl-dev.h"
 #include "iwl-core.h"
 #include "iwl-core.h"
 #include "iwl-sta.h"
 #include "iwl-sta.h"
 
 
-#define IWL_STA_DRIVER_ACTIVE BIT(0) /* driver entry is active */
-#define IWL_STA_UCODE_ACTIVE  BIT(1) /* ucode entry is active */
-
 u8 iwl_find_station(struct iwl_priv *priv, const u8 *addr)
 u8 iwl_find_station(struct iwl_priv *priv, const u8 *addr)
 {
 {
 	int i;
 	int i;
@@ -64,6 +62,19 @@ u8 iwl_find_station(struct iwl_priv *priv, const u8 *addr)
 			      addr, priv->num_stations);
 			      addr, priv->num_stations);
 
 
  out:
  out:
+	/*
+	 * It may be possible that more commands interacting with stations
+	 * arrive before we completed processing the adding of
+	 * station
+	 */
+	if (ret != IWL_INVALID_STATION &&
+	    (!(priv->stations[ret].used & IWL_STA_UCODE_ACTIVE) ||
+	     ((priv->stations[ret].used & IWL_STA_UCODE_ACTIVE) &&
+	      (priv->stations[ret].used & IWL_STA_UCODE_INPROGRESS)))) {
+		IWL_ERR(priv, "Requested station info for sta %d before ready. \n",
+			ret);
+		ret = IWL_INVALID_STATION;
+	}
 	spin_unlock_irqrestore(&priv->sta_lock, flags);
 	spin_unlock_irqrestore(&priv->sta_lock, flags);
 	return ret;
 	return ret;
 }
 }
@@ -158,13 +169,6 @@ static void iwl_process_add_sta_resp(struct iwl_priv *priv,
 		       priv->stations[sta_id].sta.mode ==
 		       priv->stations[sta_id].sta.mode ==
 		       STA_CONTROL_MODIFY_MSK ? "Modified" : "Added",
 		       STA_CONTROL_MODIFY_MSK ? "Modified" : "Added",
 		       addsta->sta.addr);
 		       addsta->sta.addr);
-
-	/*
-	 * Determine if we wanted to modify or add a station,
-	 * if adding a station succeeded we have some more initialization
-	 * to do when using station notification. TODO
-	 */
-
 	spin_unlock_irqrestore(&priv->sta_lock, flags);
 	spin_unlock_irqrestore(&priv->sta_lock, flags);
 }
 }
 
 
@@ -190,6 +194,10 @@ int iwl_send_add_sta(struct iwl_priv *priv,
 		.flags = flags,
 		.flags = flags,
 		.data = data,
 		.data = data,
 	};
 	};
+	u8 sta_id = sta->sta.sta_id;
+
+	IWL_DEBUG_INFO(priv, "Adding sta %u (%pM) %ssynchronously\n",
+		       sta_id, sta->sta.addr, flags & CMD_ASYNC ?  "a" : "");
 
 
 	if (flags & CMD_ASYNC)
 	if (flags & CMD_ASYNC)
 		cmd.callback = iwl_add_sta_callback;
 		cmd.callback = iwl_add_sta_callback;
@@ -263,18 +271,19 @@ static void iwl_set_ht_add_station(struct iwl_priv *priv, u8 index,
 }
 }
 
 
 /**
 /**
- * iwl_add_station - Add station to tables in driver and device
+ * iwl_prep_station - Prepare station information for addition
+ *
+ * should be called with sta_lock held
  */
  */
-u8 iwl_add_station(struct iwl_priv *priv, const u8 *addr, bool is_ap, u8 flags,
-		struct ieee80211_sta_ht_cap *ht_info)
+static u8 iwl_prep_station(struct iwl_priv *priv, const u8 *addr,
+			   bool is_ap,
+			   struct ieee80211_sta_ht_cap *ht_info)
 {
 {
 	struct iwl_station_entry *station;
 	struct iwl_station_entry *station;
-	unsigned long flags_spin;
 	int i;
 	int i;
-	int sta_id = IWL_INVALID_STATION;
+	u8 sta_id = IWL_INVALID_STATION;
 	u16 rate;
 	u16 rate;
 
 
-	spin_lock_irqsave(&priv->sta_lock, flags_spin);
 	if (is_ap)
 	if (is_ap)
 		sta_id = IWL_AP_ID;
 		sta_id = IWL_AP_ID;
 	else if (is_broadcast_ether_addr(addr))
 	else if (is_broadcast_ether_addr(addr))
@@ -292,20 +301,32 @@ u8 iwl_add_station(struct iwl_priv *priv, const u8 *addr, bool is_ap, u8 flags,
 				sta_id = i;
 				sta_id = i;
 		}
 		}
 
 
-	/* These two conditions have the same outcome, but keep them separate
-	   since they have different meanings */
-	if (unlikely(sta_id == IWL_INVALID_STATION)) {
-		spin_unlock_irqrestore(&priv->sta_lock, flags_spin);
+	/*
+	 * These two conditions have the same outcome, but keep them
+	 * separate
+	 */
+	if (unlikely(sta_id == IWL_INVALID_STATION))
+		return sta_id;
+
+	/*
+	 * uCode is not able to deal with multiple requests to add a
+	 * station. Keep track if one is in progress so that we do not send
+	 * another.
+	 */
+	if (priv->stations[sta_id].used & IWL_STA_UCODE_INPROGRESS) {
+		IWL_DEBUG_INFO(priv, "STA %d already in process of being added.\n",
+				sta_id);
 		return sta_id;
 		return sta_id;
 	}
 	}
 
 
-	if (priv->stations[sta_id].used &&
+	if ((priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE) &&
+	    (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE) &&
 	    !compare_ether_addr(priv->stations[sta_id].sta.sta.addr, addr)) {
 	    !compare_ether_addr(priv->stations[sta_id].sta.sta.addr, addr)) {
-		spin_unlock_irqrestore(&priv->sta_lock, flags_spin);
+		IWL_DEBUG_ASSOC(priv, "STA %d (%pM) already added, not adding again.\n",
+				sta_id, addr);
 		return sta_id;
 		return sta_id;
 	}
 	}
 
 
-
 	station = &priv->stations[sta_id];
 	station = &priv->stations[sta_id];
 	station->used = IWL_STA_DRIVER_ACTIVE;
 	station->used = IWL_STA_DRIVER_ACTIVE;
 	IWL_DEBUG_ASSOC(priv, "Add STA to driver ID %d: %pM\n",
 	IWL_DEBUG_ASSOC(priv, "Add STA to driver ID %d: %pM\n",
@@ -330,86 +351,185 @@ u8 iwl_add_station(struct iwl_priv *priv, const u8 *addr, bool is_ap, u8 flags,
 	/* Turn on both antennas for the station... */
 	/* Turn on both antennas for the station... */
 	station->sta.rate_n_flags = cpu_to_le16(rate | RATE_MCS_ANT_AB_MSK);
 	station->sta.rate_n_flags = cpu_to_le16(rate | RATE_MCS_ANT_AB_MSK);
 
 
+	return sta_id;
+
+}
+
+#define STA_WAIT_TIMEOUT (HZ/2)
+
+/**
+ * iwl_add_station_common -
+ */
+int iwl_add_station_common(struct iwl_priv *priv, const u8 *addr,
+				  bool is_ap,
+				  struct ieee80211_sta_ht_cap *ht_info,
+				  u8 *sta_id_r)
+{
+	struct iwl_station_entry *station;
+	unsigned long flags_spin;
+	int ret = 0;
+	u8 sta_id;
+
+	*sta_id_r = 0;
+	spin_lock_irqsave(&priv->sta_lock, flags_spin);
+	sta_id = iwl_prep_station(priv, addr, is_ap, ht_info);
+	if (sta_id == IWL_INVALID_STATION) {
+		IWL_ERR(priv, "Unable to prepare station %pM for addition\n",
+			addr);
+		spin_unlock_irqrestore(&priv->sta_lock, flags_spin);
+		return -EINVAL;
+	}
+
+	/*
+	 * uCode is not able to deal with multiple requests to add a
+	 * station. Keep track if one is in progress so that we do not send
+	 * another.
+	 */
+	if (priv->stations[sta_id].used & IWL_STA_UCODE_INPROGRESS) {
+		IWL_DEBUG_INFO(priv, "STA %d already in process of being added.\n",
+			       sta_id);
+		spin_unlock_irqrestore(&priv->sta_lock, flags_spin);
+		return -EEXIST;
+	}
+
+	if ((priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE) &&
+	    (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) {
+		IWL_DEBUG_ASSOC(priv, "STA %d (%pM) already added, not adding again.\n",
+				sta_id, addr);
+		spin_unlock_irqrestore(&priv->sta_lock, flags_spin);
+		return -EEXIST;
+	}
+
+	priv->stations[sta_id].used |= IWL_STA_UCODE_INPROGRESS;
+	station = &priv->stations[sta_id];
 	spin_unlock_irqrestore(&priv->sta_lock, flags_spin);
 	spin_unlock_irqrestore(&priv->sta_lock, flags_spin);
 
 
 	/* Add station to device's station table */
 	/* Add station to device's station table */
-	iwl_send_add_sta(priv, &station->sta, flags);
-	return sta_id;
-
+	ret = iwl_send_add_sta(priv, &station->sta, CMD_SYNC);
+	if (ret) {
+		IWL_ERR(priv, "Adding station %pM failed.\n", station->sta.sta.addr);
+		spin_lock_irqsave(&priv->sta_lock, flags_spin);
+		priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE;
+		priv->stations[sta_id].used &= ~IWL_STA_UCODE_INPROGRESS;
+		spin_unlock_irqrestore(&priv->sta_lock, flags_spin);
+	}
+	*sta_id_r = sta_id;
+	return ret;
 }
 }
-EXPORT_SYMBOL(iwl_add_station);
+EXPORT_SYMBOL(iwl_add_station_common);
 
 
-static void iwl_sta_ucode_deactivate(struct iwl_priv *priv, const u8 *addr)
+static void iwl_sta_init_lq(struct iwl_priv *priv, const u8 *addr, bool is_ap)
 {
 {
-	unsigned long flags;
-	u8 sta_id = iwl_find_station(priv, addr);
+	int i, r;
+	struct iwl_link_quality_cmd link_cmd = {
+		.reserved1 = 0,
+	};
+	u32 rate_flags;
 
 
-	BUG_ON(sta_id == IWL_INVALID_STATION);
+	/* Set up the rate scaling to start at selected rate, fall back
+	 * all the way down to 1M in IEEE order, and then spin on 1M */
+	if (is_ap)
+		r = IWL_RATE_54M_INDEX;
+	else if (priv->band == IEEE80211_BAND_5GHZ)
+		r = IWL_RATE_6M_INDEX;
+	else
+		r = IWL_RATE_1M_INDEX;
+
+	for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) {
+		rate_flags = 0;
+		if (r >= IWL_FIRST_CCK_RATE && r <= IWL_LAST_CCK_RATE)
+			rate_flags |= RATE_MCS_CCK_MSK;
 
 
-	IWL_DEBUG_ASSOC(priv, "Removed STA from Ucode: %pM\n", addr);
+		rate_flags |= first_antenna(priv->hw_params.valid_tx_ant) <<
+				RATE_MCS_ANT_POS;
 
 
-	spin_lock_irqsave(&priv->sta_lock, flags);
+		link_cmd.rs_table[i].rate_n_flags =
+			iwl_hw_set_rate_n_flags(iwl_rates[r].plcp, rate_flags);
+		r = iwl_get_prev_ieee_rate(r);
+	}
 
 
-	/* Ucode must be active and driver must be non active */
-	if (priv->stations[sta_id].used != IWL_STA_UCODE_ACTIVE)
-		IWL_ERR(priv, "removed non active STA %d\n", sta_id);
+	link_cmd.general_params.single_stream_ant_msk =
+				first_antenna(priv->hw_params.valid_tx_ant);
+	link_cmd.general_params.dual_stream_ant_msk = 3;
+	link_cmd.agg_params.agg_dis_start_th = LINK_QUAL_AGG_DISABLE_START_DEF;
+	link_cmd.agg_params.agg_time_limit =
+		cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF);
 
 
-	priv->stations[sta_id].used &= ~IWL_STA_UCODE_ACTIVE;
+	/* Update the rate scaling for control frame Tx to AP */
+	link_cmd.sta_id = is_ap ? IWL_AP_ID : priv->hw_params.bcast_sta_id;
 
 
-	memset(&priv->stations[sta_id], 0, sizeof(struct iwl_station_entry));
-	spin_unlock_irqrestore(&priv->sta_lock, flags);
+	iwl_send_cmd_pdu(priv, REPLY_TX_LINK_QUALITY_CMD,
+			       sizeof(link_cmd), &link_cmd);
 }
 }
 
 
-static void iwl_remove_sta_callback(struct iwl_priv *priv,
-				    struct iwl_device_cmd *cmd,
-				    struct iwl_rx_packet *pkt)
+/*
+ * iwl_add_local_stations - Add stations not requested by mac80211
+ *
+ * This will be either the broadcast station or the bssid station needed by
+ * ad-hoc.
+ *
+ * Function sleeps.
+ */
+int iwl_add_local_station(struct iwl_priv *priv, const u8 *addr, bool init_rs)
 {
 {
-	struct iwl_rem_sta_cmd *rm_sta =
-			(struct iwl_rem_sta_cmd *)cmd->cmd.payload;
-	const u8 *addr = rm_sta->addr;
+	int ret;
+	u8 sta_id;
 
 
-	if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) {
-		IWL_ERR(priv, "Bad return from REPLY_REMOVE_STA (0x%08X)\n",
-		pkt->hdr.flags);
-		return;
+	ret = iwl_add_station_common(priv, addr, 0, NULL, &sta_id);
+	if (ret) {
+		IWL_ERR(priv, "Unable to add station %pM\n", addr);
+		return ret;
 	}
 	}
 
 
-	switch (pkt->u.rem_sta.status) {
-	case REM_STA_SUCCESS_MSK:
-		iwl_sta_ucode_deactivate(priv, addr);
-		break;
-	default:
-		IWL_ERR(priv, "REPLY_REMOVE_STA failed\n");
-		break;
-	}
+	if (init_rs)
+		/* Set up default rate scaling table in device's station table */
+		iwl_sta_init_lq(priv, addr, false);
+	return 0;
+}
+EXPORT_SYMBOL(iwl_add_local_station);
+
+/**
+ * iwl_sta_ucode_deactivate - deactivate ucode status for a station
+ *
+ * priv->sta_lock must be held
+ */
+static void iwl_sta_ucode_deactivate(struct iwl_priv *priv, u8 sta_id)
+{
+	/* Ucode must be active and driver must be non active */
+	if (priv->stations[sta_id].used != IWL_STA_UCODE_ACTIVE)
+		IWL_ERR(priv, "removed non active STA %u\n", sta_id);
+
+	priv->stations[sta_id].used &= ~IWL_STA_UCODE_ACTIVE;
+
+	memset(&priv->stations[sta_id], 0, sizeof(struct iwl_station_entry));
+	IWL_DEBUG_ASSOC(priv, "Removed STA %u\n", sta_id);
 }
 }
 
 
-static int iwl_send_remove_station(struct iwl_priv *priv, const u8 *addr,
-				   u8 flags)
+static int iwl_send_remove_station(struct iwl_priv *priv,
+				   struct iwl_station_entry *station)
 {
 {
 	struct iwl_rx_packet *pkt;
 	struct iwl_rx_packet *pkt;
 	int ret;
 	int ret;
 
 
+	unsigned long flags_spin;
 	struct iwl_rem_sta_cmd rm_sta_cmd;
 	struct iwl_rem_sta_cmd rm_sta_cmd;
 
 
 	struct iwl_host_cmd cmd = {
 	struct iwl_host_cmd cmd = {
 		.id = REPLY_REMOVE_STA,
 		.id = REPLY_REMOVE_STA,
 		.len = sizeof(struct iwl_rem_sta_cmd),
 		.len = sizeof(struct iwl_rem_sta_cmd),
-		.flags = flags,
+		.flags = CMD_SYNC,
 		.data = &rm_sta_cmd,
 		.data = &rm_sta_cmd,
 	};
 	};
 
 
 	memset(&rm_sta_cmd, 0, sizeof(rm_sta_cmd));
 	memset(&rm_sta_cmd, 0, sizeof(rm_sta_cmd));
 	rm_sta_cmd.num_sta = 1;
 	rm_sta_cmd.num_sta = 1;
-	memcpy(&rm_sta_cmd.addr, addr , ETH_ALEN);
+	memcpy(&rm_sta_cmd.addr, &station->sta.sta.addr , ETH_ALEN);
+
+	cmd.flags |= CMD_WANT_SKB;
 
 
-	if (flags & CMD_ASYNC)
-		cmd.callback = iwl_remove_sta_callback;
-	else
-		cmd.flags |= CMD_WANT_SKB;
 	ret = iwl_send_cmd(priv, &cmd);
 	ret = iwl_send_cmd(priv, &cmd);
 
 
-	if (ret || (flags & CMD_ASYNC))
+	if (ret)
 		return ret;
 		return ret;
 
 
 	pkt = (struct iwl_rx_packet *)cmd.reply_page;
 	pkt = (struct iwl_rx_packet *)cmd.reply_page;
@@ -422,7 +542,9 @@ static int iwl_send_remove_station(struct iwl_priv *priv, const u8 *addr,
 	if (!ret) {
 	if (!ret) {
 		switch (pkt->u.rem_sta.status) {
 		switch (pkt->u.rem_sta.status) {
 		case REM_STA_SUCCESS_MSK:
 		case REM_STA_SUCCESS_MSK:
-			iwl_sta_ucode_deactivate(priv, addr);
+			spin_lock_irqsave(&priv->sta_lock, flags_spin);
+			iwl_sta_ucode_deactivate(priv, station->sta.sta.sta_id);
+			spin_unlock_irqrestore(&priv->sta_lock, flags_spin);
 			IWL_DEBUG_ASSOC(priv, "REPLY_REMOVE_STA PASSED\n");
 			IWL_DEBUG_ASSOC(priv, "REPLY_REMOVE_STA PASSED\n");
 			break;
 			break;
 		default:
 		default:
@@ -439,23 +561,35 @@ static int iwl_send_remove_station(struct iwl_priv *priv, const u8 *addr,
 /**
 /**
  * iwl_remove_station - Remove driver's knowledge of station.
  * iwl_remove_station - Remove driver's knowledge of station.
  */
  */
-int iwl_remove_station(struct iwl_priv *priv, const u8 *addr, bool is_ap)
+static int iwl_remove_station(struct iwl_priv *priv, struct ieee80211_sta *sta)
 {
 {
 	int sta_id = IWL_INVALID_STATION;
 	int sta_id = IWL_INVALID_STATION;
 	int i, ret = -EINVAL;
 	int i, ret = -EINVAL;
 	unsigned long flags;
 	unsigned long flags;
+	bool is_ap = priv->iw_mode == NL80211_IFTYPE_STATION;
+	struct iwl_station_entry *station;
+
+	if (!iwl_is_ready(priv)) {
+		IWL_DEBUG_INFO(priv,
+			"Unable to remove station %pM, device not ready. \n",
+			sta->addr);
+		/*
+		 * It is typical for stations to be removed when we are
+		 * going down. Return success since device will be down
+		 * soon anyway
+		 */
+		return 0;
+	}
 
 
 	spin_lock_irqsave(&priv->sta_lock, flags);
 	spin_lock_irqsave(&priv->sta_lock, flags);
 
 
 	if (is_ap)
 	if (is_ap)
 		sta_id = IWL_AP_ID;
 		sta_id = IWL_AP_ID;
-	else if (is_broadcast_ether_addr(addr))
-		sta_id = priv->hw_params.bcast_sta_id;
 	else
 	else
 		for (i = IWL_STA_ID; i < priv->hw_params.max_stations; i++)
 		for (i = IWL_STA_ID; i < priv->hw_params.max_stations; i++)
 			if (priv->stations[i].used &&
 			if (priv->stations[i].used &&
 			    !compare_ether_addr(priv->stations[i].sta.sta.addr,
 			    !compare_ether_addr(priv->stations[i].sta.sta.addr,
-						addr)) {
+						sta->addr)) {
 				sta_id = i;
 				sta_id = i;
 				break;
 				break;
 			}
 			}
@@ -464,17 +598,17 @@ int iwl_remove_station(struct iwl_priv *priv, const u8 *addr, bool is_ap)
 		goto out;
 		goto out;
 
 
 	IWL_DEBUG_ASSOC(priv, "Removing STA from driver:%d  %pM\n",
 	IWL_DEBUG_ASSOC(priv, "Removing STA from driver:%d  %pM\n",
-		sta_id, addr);
+		sta_id, sta->addr);
 
 
 	if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE)) {
 	if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE)) {
-		IWL_ERR(priv, "Removing %pM but non DRIVER active\n",
-				addr);
+		IWL_DEBUG_INFO(priv, "Removing %pM but non DRIVER active\n",
+				sta->addr);
 		goto out;
 		goto out;
 	}
 	}
 
 
 	if (!(priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) {
 	if (!(priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) {
-		IWL_ERR(priv, "Removing %pM but non UCODE active\n",
-				addr);
+		IWL_DEBUG_INFO(priv, "Removing %pM but non UCODE active\n",
+				sta->addr);
 		goto out;
 		goto out;
 	}
 	}
 
 
@@ -485,9 +619,10 @@ int iwl_remove_station(struct iwl_priv *priv, const u8 *addr, bool is_ap)
 
 
 	BUG_ON(priv->num_stations < 0);
 	BUG_ON(priv->num_stations < 0);
 
 
+	station = &priv->stations[sta_id];
 	spin_unlock_irqrestore(&priv->sta_lock, flags);
 	spin_unlock_irqrestore(&priv->sta_lock, flags);
 
 
-	ret = iwl_send_remove_station(priv, addr, CMD_ASYNC);
+	ret = iwl_send_remove_station(priv, station);
 	return ret;
 	return ret;
 out:
 out:
 	spin_unlock_irqrestore(&priv->sta_lock, flags);
 	spin_unlock_irqrestore(&priv->sta_lock, flags);
@@ -495,37 +630,122 @@ out:
 }
 }
 
 
 /**
 /**
- * iwl_clear_stations_table - Clear the driver's station table
- *
- * NOTE:  This does not clear or otherwise alter the device's station table.
+ * iwl_clear_ucode_stations() - clear entire station table driver and/or ucode
+ * @priv:
+ * @force: If set then the uCode station table needs to be cleared here. If
+ *         not set then the uCode station table has already been cleared,
+ *         for example after sending it a RXON command without ASSOC bit
+ *         set, and we just need to change driver state here.
  */
  */
-void iwl_clear_stations_table(struct iwl_priv *priv)
+void iwl_clear_ucode_stations(struct iwl_priv *priv, bool force)
 {
 {
-	unsigned long flags;
 	int i;
 	int i;
+	unsigned long flags_spin;
+	bool cleared = false;
+
+	IWL_DEBUG_INFO(priv, "Clearing ucode stations in driver%s\n",
+			force ? " and ucode" : "");
+
+	if (force) {
+		if (!iwl_is_ready(priv)) {
+			/*
+			 * If device is not ready at this point the station
+			 * table is likely already empty (uCode not ready
+			 * to receive station requests) or will soon be
+			 * due to interface going down.
+			 */
+			IWL_DEBUG_INFO(priv, "Unable to remove stations from device - device not ready\n");
+		} else {
+			iwl_send_cmd_pdu_async(priv, REPLY_REMOVE_ALL_STA, 0, NULL, NULL);
+		}
+	}
 
 
-	spin_lock_irqsave(&priv->sta_lock, flags);
+	spin_lock_irqsave(&priv->sta_lock, flags_spin);
+	if (force) {
+		IWL_DEBUG_INFO(priv, "Clearing all station information in driver\n");
+		priv->num_stations = 0;
+		memset(priv->stations, 0, sizeof(priv->stations));
+	} else {
+		for (i = 0; i < priv->hw_params.max_stations; i++) {
+			if (priv->stations[i].used & IWL_STA_UCODE_ACTIVE) {
+				IWL_DEBUG_INFO(priv, "Clearing ucode active for station %d \n", i);
+				priv->stations[i].used &= ~IWL_STA_UCODE_ACTIVE;
+				cleared = true;
+			}
+		}
+	}
+	spin_unlock_irqrestore(&priv->sta_lock, flags_spin);
 
 
-	if (iwl_is_alive(priv) &&
-	   !test_bit(STATUS_EXIT_PENDING, &priv->status) &&
-	   iwl_send_cmd_pdu_async(priv, REPLY_REMOVE_ALL_STA, 0, NULL, NULL))
-		IWL_ERR(priv, "Couldn't clear the station table\n");
+	if (!cleared)
+		IWL_DEBUG_INFO(priv, "No active stations found to be cleared\n");
+}
+EXPORT_SYMBOL(iwl_clear_ucode_stations);
 
 
-	priv->num_stations = 0;
-	memset(priv->stations, 0, sizeof(priv->stations));
+/**
+ * iwl_restore_stations() - Restore driver known stations to device
+ *
+ * All stations considered active by driver, but not present in ucode, is
+ * restored.
+ *
+ * Function sleeps.
+ */
+void iwl_restore_stations(struct iwl_priv *priv)
+{
+	struct iwl_station_entry *station;
+	unsigned long flags_spin;
+	int i;
+	bool found = false;
+	int ret;
 
 
-	/* clean ucode key table bit map */
-	priv->ucode_key_table = 0;
+	if (!iwl_is_ready(priv)) {
+		IWL_DEBUG_INFO(priv, "Not ready yet, not restoring any stations.\n");
+		return;
+	}
 
 
-	/* keep track of static keys */
-	for (i = 0; i < WEP_KEYS_MAX ; i++) {
-		if (priv->wep_keys[i].key_size)
-			set_bit(i, &priv->ucode_key_table);
+	IWL_DEBUG_ASSOC(priv, "Restoring all known stations ... start.\n");
+	spin_lock_irqsave(&priv->sta_lock, flags_spin);
+	for (i = 0; i < priv->hw_params.max_stations; i++) {
+		if ((priv->stations[i].used & IWL_STA_DRIVER_ACTIVE) &&
+			    !(priv->stations[i].used & IWL_STA_UCODE_ACTIVE)) {
+			IWL_DEBUG_ASSOC(priv, "Restoring sta %pM\n",
+					priv->stations[i].sta.sta.addr);
+			priv->stations[i].sta.mode = 0;
+			priv->stations[i].used |= IWL_STA_UCODE_INPROGRESS;
+			found = true;
+		}
 	}
 	}
 
 
-	spin_unlock_irqrestore(&priv->sta_lock, flags);
+	for (i = 0; i < priv->hw_params.max_stations; i++) {
+		if ((priv->stations[i].used & IWL_STA_UCODE_INPROGRESS)) {
+			spin_unlock_irqrestore(&priv->sta_lock, flags_spin);
+			station = &priv->stations[i];
+			ret = iwl_send_add_sta(priv, &priv->stations[i].sta, CMD_SYNC);
+			if (ret) {
+				IWL_ERR(priv, "Adding station %pM failed.\n",
+					station->sta.sta.addr);
+				spin_lock_irqsave(&priv->sta_lock, flags_spin);
+				priv->stations[i].used &= ~IWL_STA_DRIVER_ACTIVE;
+				priv->stations[i].used &= ~IWL_STA_UCODE_INPROGRESS;
+				spin_unlock_irqrestore(&priv->sta_lock, flags_spin);
+			}
+			/*
+			 * Rate scaling has already been initialized, send
+			 * current LQ command
+			 */
+			if (station->lq)
+				iwl_send_lq_cmd(priv, station->lq, CMD_SYNC, true);
+			spin_lock_irqsave(&priv->sta_lock, flags_spin);
+			priv->stations[i].used &= ~IWL_STA_UCODE_INPROGRESS;
+		}
+	}
+
+	spin_unlock_irqrestore(&priv->sta_lock, flags_spin);
+	if (!found)
+		IWL_DEBUG_INFO(priv, "Restoring all known stations .... no stations to be restored.\n");
+	else
+		IWL_DEBUG_INFO(priv, "Restoring all known stations .... complete.\n");
 }
 }
-EXPORT_SYMBOL(iwl_clear_stations_table);
+EXPORT_SYMBOL(iwl_restore_stations);
 
 
 int iwl_get_free_ucode_key_index(struct iwl_priv *priv)
 int iwl_get_free_ucode_key_index(struct iwl_priv *priv)
 {
 {
@@ -948,9 +1168,22 @@ static inline void iwl_dump_lq_cmd(struct iwl_priv *priv,
 }
 }
 #endif
 #endif
 
 
+/**
+ * iwl_send_lq_cmd() - Send link quality command
+ * @init: This command is sent as part of station initialization right
+ *        after station has been added.
+ *
+ * The link quality command is sent as the last step of station creation.
+ * This is the special case in which init is set and we call a callback in
+ * this case to clear the state indicating that station creation is in
+ * progress.
+ */
 int iwl_send_lq_cmd(struct iwl_priv *priv,
 int iwl_send_lq_cmd(struct iwl_priv *priv,
-		    struct iwl_link_quality_cmd *lq, u8 flags)
+		    struct iwl_link_quality_cmd *lq, u8 flags, bool init)
 {
 {
+	int ret = 0;
+	unsigned long flags_spin;
+
 	struct iwl_host_cmd cmd = {
 	struct iwl_host_cmd cmd = {
 		.id = REPLY_TX_LINK_QUALITY_CMD,
 		.id = REPLY_TX_LINK_QUALITY_CMD,
 		.len = sizeof(struct iwl_link_quality_cmd),
 		.len = sizeof(struct iwl_link_quality_cmd),
@@ -966,167 +1199,31 @@ int iwl_send_lq_cmd(struct iwl_priv *priv,
 		lq->sta_id = IWL_AP_ID;
 		lq->sta_id = IWL_AP_ID;
 
 
 	iwl_dump_lq_cmd(priv, lq);
 	iwl_dump_lq_cmd(priv, lq);
+	BUG_ON(init && (cmd.flags & CMD_ASYNC));
 
 
-	if (iwl_is_associated(priv) && priv->assoc_station_added)
-		return  iwl_send_cmd(priv, &cmd);
+	iwl_dump_lq_cmd(priv, lq);
+	ret = iwl_send_cmd(priv, &cmd);
+	if (ret || (cmd.flags & CMD_ASYNC))
+		return ret;
 
 
+	if (init) {
+		IWL_DEBUG_INFO(priv, "init LQ command complete, clearing sta addition status for sta %d \n",
+			       lq->sta_id);
+		spin_lock_irqsave(&priv->sta_lock, flags_spin);
+		priv->stations[lq->sta_id].used &= ~IWL_STA_UCODE_INPROGRESS;
+		spin_unlock_irqrestore(&priv->sta_lock, flags_spin);
+	}
 	return 0;
 	return 0;
 }
 }
 EXPORT_SYMBOL(iwl_send_lq_cmd);
 EXPORT_SYMBOL(iwl_send_lq_cmd);
 
 
-/**
- * iwl_sta_init_lq - Initialize a station's hardware rate table
- *
- * The uCode's station table contains a table of fallback rates
- * for automatic fallback during transmission.
- *
- * NOTE: This sets up a default set of values.  These will be replaced later
- *       if the driver's iwl-agn-rs rate scaling algorithm is used, instead of
- *       rc80211_simple.
- *
- * NOTE: Run REPLY_ADD_STA command to set up station table entry, before
- *       calling this function (which runs REPLY_TX_LINK_QUALITY_CMD,
- *       which requires station table entry to exist).
- */
-static void iwl_sta_init_lq(struct iwl_priv *priv, const u8 *addr, bool is_ap)
-{
-	int i, r;
-	struct iwl_link_quality_cmd link_cmd = {
-		.reserved1 = 0,
-	};
-	u32 rate_flags;
-
-	/* Set up the rate scaling to start at selected rate, fall back
-	 * all the way down to 1M in IEEE order, and then spin on 1M */
-	if (is_ap)
-		r = IWL_RATE_54M_INDEX;
-	else if (priv->band == IEEE80211_BAND_5GHZ)
-		r = IWL_RATE_6M_INDEX;
-	else
-		r = IWL_RATE_1M_INDEX;
-
-	for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) {
-		rate_flags = 0;
-		if (r >= IWL_FIRST_CCK_RATE && r <= IWL_LAST_CCK_RATE)
-			rate_flags |= RATE_MCS_CCK_MSK;
-
-		rate_flags |= first_antenna(priv->hw_params.valid_tx_ant) <<
-				RATE_MCS_ANT_POS;
-
-		link_cmd.rs_table[i].rate_n_flags =
-			iwl_hw_set_rate_n_flags(iwl_rates[r].plcp, rate_flags);
-		r = iwl_get_prev_ieee_rate(r);
-	}
-
-	link_cmd.general_params.single_stream_ant_msk =
-				first_antenna(priv->hw_params.valid_tx_ant);
-	link_cmd.general_params.dual_stream_ant_msk = 3;
-	link_cmd.agg_params.agg_dis_start_th = LINK_QUAL_AGG_DISABLE_START_DEF;
-	link_cmd.agg_params.agg_time_limit =
-		cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF);
-
-	/* Update the rate scaling for control frame Tx to AP */
-	link_cmd.sta_id = is_ap ? IWL_AP_ID : priv->hw_params.bcast_sta_id;
-
-	iwl_send_cmd_pdu_async(priv, REPLY_TX_LINK_QUALITY_CMD,
-			       sizeof(link_cmd), &link_cmd, NULL);
-}
-
-/**
- * iwl_rxon_add_station - add station into station table.
- *
- * there is only one AP station with id= IWL_AP_ID
- * NOTE: mutex must be held before calling this function
- */
-int iwl_rxon_add_station(struct iwl_priv *priv, const u8 *addr, bool is_ap)
-{
-	struct ieee80211_sta *sta;
-	struct ieee80211_sta_ht_cap ht_config;
-	struct ieee80211_sta_ht_cap *cur_ht_config = NULL;
-	u8 sta_id;
-
-	/*
-	 * Set HT capabilities. It is ok to set this struct even if not using
-	 * HT config: the priv->current_ht_config.is_ht flag will just be false
-	 */
-	rcu_read_lock();
-	sta = ieee80211_find_sta(priv->vif, addr);
-	if (sta) {
-		memcpy(&ht_config, &sta->ht_cap, sizeof(ht_config));
-		cur_ht_config = &ht_config;
-	}
-	rcu_read_unlock();
-
-	/* Add station to device's station table */
-	sta_id = iwl_add_station(priv, addr, is_ap, CMD_SYNC, cur_ht_config);
-
-	/* Set up default rate scaling table in device's station table */
-	iwl_sta_init_lq(priv, addr, is_ap);
-
-	return sta_id;
-}
-EXPORT_SYMBOL(iwl_rxon_add_station);
-
-/**
- * iwl_sta_init_bcast_lq - Initialize a bcast station's hardware rate table
- *
- * NOTE: Run REPLY_ADD_STA command to set up station table entry, before
- *       calling this function (which runs REPLY_TX_LINK_QUALITY_CMD,
- *       which requires station table entry to exist).
- */
-static void iwl_sta_init_bcast_lq(struct iwl_priv *priv)
-{
-	int i, r;
-	struct iwl_link_quality_cmd link_cmd = {
-		.reserved1 = 0,
-	};
-	u32 rate_flags;
-
-	/* Set up the rate scaling to start at selected rate, fall back
-	 * all the way down to 1M in IEEE order, and then spin on 1M */
-	if (priv->band == IEEE80211_BAND_5GHZ)
-		r = IWL_RATE_6M_INDEX;
-	else
-		r = IWL_RATE_1M_INDEX;
-
-	for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) {
-		rate_flags = 0;
-		if (r >= IWL_FIRST_CCK_RATE && r <= IWL_LAST_CCK_RATE)
-			rate_flags |= RATE_MCS_CCK_MSK;
-
-		rate_flags |= first_antenna(priv->hw_params.valid_tx_ant) <<
-				RATE_MCS_ANT_POS;
-
-		link_cmd.rs_table[i].rate_n_flags =
-			iwl_hw_set_rate_n_flags(iwl_rates[r].plcp, rate_flags);
-		r = iwl_get_prev_ieee_rate(r);
-	}
-
-	link_cmd.general_params.single_stream_ant_msk =
-				first_antenna(priv->hw_params.valid_tx_ant);
-	link_cmd.general_params.dual_stream_ant_msk = 3;
-	link_cmd.agg_params.agg_dis_start_th = LINK_QUAL_AGG_DISABLE_START_DEF;
-	link_cmd.agg_params.agg_time_limit =
-		cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF);
-
-	/* Update the rate scaling for control frame Tx to AP */
-	link_cmd.sta_id = priv->hw_params.bcast_sta_id;
-
-	iwl_send_cmd_pdu_async(priv, REPLY_TX_LINK_QUALITY_CMD,
-			       sizeof(link_cmd), &link_cmd, NULL);
-}
-
-
 /**
 /**
  * iwl_add_bcast_station - add broadcast station into station table.
  * iwl_add_bcast_station - add broadcast station into station table.
  */
  */
 void iwl_add_bcast_station(struct iwl_priv *priv)
 void iwl_add_bcast_station(struct iwl_priv *priv)
 {
 {
 	IWL_DEBUG_INFO(priv, "Adding broadcast station to station table\n");
 	IWL_DEBUG_INFO(priv, "Adding broadcast station to station table\n");
-	iwl_add_station(priv, iwl_bcast_addr, false, CMD_SYNC, NULL);
-
-	/* Set up default rate scaling table in device's station table */
-	iwl_sta_init_bcast_lq(priv);
+	iwl_add_local_station(priv, iwl_bcast_addr, true);
 }
 }
 EXPORT_SYMBOL(iwl_add_bcast_station);
 EXPORT_SYMBOL(iwl_add_bcast_station);
 
 
@@ -1136,7 +1233,14 @@ EXPORT_SYMBOL(iwl_add_bcast_station);
 void iwl3945_add_bcast_station(struct iwl_priv *priv)
 void iwl3945_add_bcast_station(struct iwl_priv *priv)
 {
 {
 	IWL_DEBUG_INFO(priv, "Adding broadcast station to station table\n");
 	IWL_DEBUG_INFO(priv, "Adding broadcast station to station table\n");
-	iwl_add_station(priv, iwl_bcast_addr, false, CMD_SYNC, NULL);
+	iwl_add_local_station(priv, iwl_bcast_addr, false);
+	/*
+	 * It is assumed that when station is added more initialization
+	 * needs to be done, but for 3945 it is not the case and we can
+	 * just release station table access right here.
+	 */
+	priv->stations[priv->hw_params.bcast_sta_id].used &= ~IWL_STA_UCODE_INPROGRESS;
+
 }
 }
 EXPORT_SYMBOL(iwl3945_add_bcast_station);
 EXPORT_SYMBOL(iwl3945_add_bcast_station);
 
 
@@ -1159,6 +1263,13 @@ int iwl_get_sta_id(struct iwl_priv *priv, struct ieee80211_hdr *hdr)
 	/* If we are a client station in a BSS network, use the special
 	/* If we are a client station in a BSS network, use the special
 	 * AP station entry (that's the only station we communicate with) */
 	 * AP station entry (that's the only station we communicate with) */
 	case NL80211_IFTYPE_STATION:
 	case NL80211_IFTYPE_STATION:
+		/*
+		 * If addition of station not complete yet, which means
+		 * that rate scaling has not been initialized, then return
+		 * the broadcast station.
+		 */
+		if (!(priv->stations[IWL_AP_ID].used & IWL_STA_UCODE_ACTIVE))
+			return priv->hw_params.bcast_sta_id;
 		return IWL_AP_ID;
 		return IWL_AP_ID;
 
 
 	/* If we are an AP, then find the station, or use BCAST */
 	/* If we are an AP, then find the station, or use BCAST */
@@ -1175,13 +1286,6 @@ int iwl_get_sta_id(struct iwl_priv *priv, struct ieee80211_hdr *hdr)
 		if (sta_id != IWL_INVALID_STATION)
 		if (sta_id != IWL_INVALID_STATION)
 			return sta_id;
 			return sta_id;
 
 
-		/* Create new station table entry */
-		sta_id = iwl_add_station(priv, hdr->addr1, false,
-					CMD_ASYNC, NULL);
-
-		if (sta_id != IWL_INVALID_STATION)
-			return sta_id;
-
 		IWL_DEBUG_DROP(priv, "Station %pM not in station map. "
 		IWL_DEBUG_DROP(priv, "Station %pM not in station map. "
 			       "Defaulting to broadcast...\n",
 			       "Defaulting to broadcast...\n",
 			       hdr->addr1);
 			       hdr->addr1);
@@ -1291,3 +1395,19 @@ void iwl_sta_modify_sleep_tx_count(struct iwl_priv *priv, int sta_id, int cnt)
 
 
 	iwl_send_add_sta(priv, &priv->stations[sta_id].sta, CMD_ASYNC);
 	iwl_send_add_sta(priv, &priv->stations[sta_id].sta, CMD_ASYNC);
 }
 }
+
+int iwl_mac_sta_remove(struct ieee80211_hw *hw,
+			       struct ieee80211_vif *vif,
+			       struct ieee80211_sta *sta)
+{
+	int ret;
+	struct iwl_priv *priv = hw->priv;
+	IWL_DEBUG_INFO(priv, "received request to remove station %pM\n",
+			sta->addr);
+	ret = iwl_remove_station(priv, sta);
+	if (ret)
+		IWL_ERR(priv, "Error removing station %pM\n",
+			sta->addr);
+	return ret;
+}
+EXPORT_SYMBOL(iwl_mac_sta_remove);

+ 15 - 5
drivers/net/wireless/iwlwifi/iwl-sta.h

@@ -32,6 +32,12 @@
 #define HW_KEY_DYNAMIC 0
 #define HW_KEY_DYNAMIC 0
 #define HW_KEY_DEFAULT 1
 #define HW_KEY_DEFAULT 1
 
 
+#define IWL_STA_DRIVER_ACTIVE BIT(0) /* driver entry is active */
+#define IWL_STA_UCODE_ACTIVE  BIT(1) /* ucode entry is active */
+#define IWL_STA_UCODE_INPROGRESS  BIT(2) /* ucode entry is in process of
+					    being activated */
+
+
 /**
 /**
  * iwl_find_station - Find station id for a given BSSID
  * iwl_find_station - Find station id for a given BSSID
  * @bssid: MAC address of station ID to find
  * @bssid: MAC address of station ID to find
@@ -51,18 +57,22 @@ void iwl_update_tkip_key(struct iwl_priv *priv,
 			struct ieee80211_key_conf *keyconf,
 			struct ieee80211_key_conf *keyconf,
 			const u8 *addr, u32 iv32, u16 *phase1key);
 			const u8 *addr, u32 iv32, u16 *phase1key);
 
 
-int iwl_rxon_add_station(struct iwl_priv *priv, const u8 *addr, bool is_ap);
 void iwl_add_bcast_station(struct iwl_priv *priv);
 void iwl_add_bcast_station(struct iwl_priv *priv);
 void iwl3945_add_bcast_station(struct iwl_priv *priv);
 void iwl3945_add_bcast_station(struct iwl_priv *priv);
-int iwl_remove_station(struct iwl_priv *priv, const u8 *addr, bool is_ap);
-void iwl_clear_stations_table(struct iwl_priv *priv);
+void iwl_restore_stations(struct iwl_priv *priv);
+void iwl_clear_ucode_stations(struct iwl_priv *priv, bool force);
 int iwl_get_free_ucode_key_index(struct iwl_priv *priv);
 int iwl_get_free_ucode_key_index(struct iwl_priv *priv);
 int iwl_get_sta_id(struct iwl_priv *priv, struct ieee80211_hdr *hdr);
 int iwl_get_sta_id(struct iwl_priv *priv, struct ieee80211_hdr *hdr);
 int iwl_get_ra_sta_id(struct iwl_priv *priv, struct ieee80211_hdr *hdr);
 int iwl_get_ra_sta_id(struct iwl_priv *priv, struct ieee80211_hdr *hdr);
 int iwl_send_add_sta(struct iwl_priv *priv,
 int iwl_send_add_sta(struct iwl_priv *priv,
 		     struct iwl_addsta_cmd *sta, u8 flags);
 		     struct iwl_addsta_cmd *sta, u8 flags);
-u8 iwl_add_station(struct iwl_priv *priv, const u8 *addr, bool is_ap, u8 flags,
-			struct ieee80211_sta_ht_cap *ht_info);
+int iwl_add_local_station(struct iwl_priv *priv, const u8 *addr, bool init_rs);
+int iwl_add_station_common(struct iwl_priv *priv, const u8 *addr,
+				  bool is_ap,
+				  struct ieee80211_sta_ht_cap *ht_info,
+				  u8 *sta_id_r);
+int iwl_mac_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+		       struct ieee80211_sta *sta);
 void iwl_sta_tx_modify_enable_tid(struct iwl_priv *priv, int sta_id, int tid);
 void iwl_sta_tx_modify_enable_tid(struct iwl_priv *priv, int sta_id, int tid);
 int iwl_sta_rx_agg_start(struct iwl_priv *priv,
 int iwl_sta_rx_agg_start(struct iwl_priv *priv,
 			 const u8 *addr, int tid, u16 ssn);
 			 const u8 *addr, int tid, u16 ssn);

+ 2 - 0
drivers/net/wireless/iwlwifi/iwl-tx.c

@@ -322,6 +322,8 @@ static int iwl_queue_init(struct iwl_priv *priv, struct iwl_queue *q,
 		q->high_mark = 2;
 		q->high_mark = 2;
 
 
 	q->write_ptr = q->read_ptr = 0;
 	q->write_ptr = q->read_ptr = 0;
+	q->last_read_ptr = 0;
+	q->repeat_same_read_ptr = 0;
 
 
 	return 0;
 	return 0;
 }
 }

+ 58 - 14
drivers/net/wireless/iwlwifi/iwl3945-base.c

@@ -2480,8 +2480,6 @@ static void iwl3945_alive_start(struct iwl_priv *priv)
 		goto restart;
 		goto restart;
 	}
 	}
 
 
-	iwl_clear_stations_table(priv);
-
 	rfkill = iwl_read_prph(priv, APMG_RFKILL_REG);
 	rfkill = iwl_read_prph(priv, APMG_RFKILL_REG);
 	IWL_DEBUG_INFO(priv, "RFKILL status: 0x%x\n", rfkill);
 	IWL_DEBUG_INFO(priv, "RFKILL status: 0x%x\n", rfkill);
 
 
@@ -2503,6 +2501,13 @@ static void iwl3945_alive_start(struct iwl_priv *priv)
 	/* After the ALIVE response, we can send commands to 3945 uCode */
 	/* After the ALIVE response, we can send commands to 3945 uCode */
 	set_bit(STATUS_ALIVE, &priv->status);
 	set_bit(STATUS_ALIVE, &priv->status);
 
 
+	if (priv->cfg->ops->lib->recover_from_tx_stall) {
+		/* Enable timer to monitor the driver queues */
+		mod_timer(&priv->monitor_recover,
+			jiffies +
+			msecs_to_jiffies(priv->cfg->monitor_recover_period));
+	}
+
 	if (iwl_is_rfkill(priv))
 	if (iwl_is_rfkill(priv))
 		return;
 		return;
 
 
@@ -2558,7 +2563,8 @@ static void __iwl3945_down(struct iwl_priv *priv)
 	if (!exit_pending)
 	if (!exit_pending)
 		set_bit(STATUS_EXIT_PENDING, &priv->status);
 		set_bit(STATUS_EXIT_PENDING, &priv->status);
 
 
-	iwl_clear_stations_table(priv);
+	/* Station information will now be cleared in device */
+	iwl_clear_ucode_stations(priv, true);
 
 
 	/* Unblock any waiting calls */
 	/* Unblock any waiting calls */
 	wake_up_interruptible_all(&priv->wait_command_queue);
 	wake_up_interruptible_all(&priv->wait_command_queue);
@@ -2692,8 +2698,6 @@ static int __iwl3945_up(struct iwl_priv *priv)
 
 
 	for (i = 0; i < MAX_HW_RESTARTS; i++) {
 	for (i = 0; i < MAX_HW_RESTARTS; i++) {
 
 
-		iwl_clear_stations_table(priv);
-
 		/* load bootstrap state machine,
 		/* load bootstrap state machine,
 		 * load bootstrap program into processor's memory,
 		 * load bootstrap program into processor's memory,
 		 * prepare to load the "initialize" uCode */
 		 * prepare to load the "initialize" uCode */
@@ -3119,12 +3123,13 @@ void iwl3945_post_associate(struct iwl_priv *priv)
 	case NL80211_IFTYPE_ADHOC:
 	case NL80211_IFTYPE_ADHOC:
 
 
 		priv->assoc_id = 1;
 		priv->assoc_id = 1;
-		iwl_add_station(priv, priv->bssid, 0, CMD_SYNC, NULL);
+		iwl_add_local_station(priv, priv->bssid, false);
 		iwl3945_sync_sta(priv, IWL_STA_ID,
 		iwl3945_sync_sta(priv, IWL_STA_ID,
-				 (priv->band == IEEE80211_BAND_5GHZ) ?
-				 IWL_RATE_6M_PLCP : IWL_RATE_1M_PLCP,
+				(priv->band == IEEE80211_BAND_5GHZ) ?
+				IWL_RATE_6M_PLCP : IWL_RATE_1M_PLCP,
 				 CMD_ASYNC);
 				 CMD_ASYNC);
 		iwl3945_rate_scale_init(priv->hw, IWL_STA_ID);
 		iwl3945_rate_scale_init(priv->hw, IWL_STA_ID);
+
 		iwl3945_send_beacon_cmd(priv);
 		iwl3945_send_beacon_cmd(priv);
 
 
 		break;
 		break;
@@ -3309,7 +3314,7 @@ void iwl3945_config_ap(struct iwl_priv *priv)
 		/* restore RXON assoc */
 		/* restore RXON assoc */
 		priv->staging_rxon.filter_flags |= RXON_FILTER_ASSOC_MSK;
 		priv->staging_rxon.filter_flags |= RXON_FILTER_ASSOC_MSK;
 		iwlcore_commit_rxon(priv);
 		iwlcore_commit_rxon(priv);
-		iwl_add_station(priv, iwl_bcast_addr, 0, CMD_SYNC, NULL);
+		iwl_add_local_station(priv, iwl_bcast_addr, false);
 	}
 	}
 	iwl3945_send_beacon_cmd(priv);
 	iwl3945_send_beacon_cmd(priv);
 
 
@@ -3376,6 +3381,38 @@ static int iwl3945_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
 	return ret;
 	return ret;
 }
 }
 
 
+static int iwl3945_mac_sta_add(struct ieee80211_hw *hw,
+			       struct ieee80211_vif *vif,
+			       struct ieee80211_sta *sta)
+{
+	struct iwl_priv *priv = hw->priv;
+	int ret;
+	bool is_ap = priv->iw_mode == NL80211_IFTYPE_STATION;
+	u8 sta_id;
+
+	IWL_DEBUG_INFO(priv, "received request to add station %pM\n",
+			sta->addr);
+
+	ret = iwl_add_station_common(priv, sta->addr, is_ap, &sta->ht_cap,
+				     &sta_id);
+	if (ret) {
+		IWL_ERR(priv, "Unable to add station %pM (%d)\n",
+			sta->addr, ret);
+		/* Should we return success if return code is EEXIST ? */
+		return ret;
+	}
+
+	/* Initialize rate scaling */
+	IWL_DEBUG_INFO(priv, "Initializing rate scaling for station %pM \n",
+		       sta->addr);
+	iwl3945_rs_rate_init(priv, sta, sta_id);
+
+	return 0;
+
+
+
+	return ret;
+}
 /*****************************************************************************
 /*****************************************************************************
  *
  *
  * sysfs attributes
  * sysfs attributes
@@ -3766,6 +3803,13 @@ static void iwl3945_setup_deferred_work(struct iwl_priv *priv)
 
 
 	iwl3945_hw_setup_deferred_work(priv);
 	iwl3945_hw_setup_deferred_work(priv);
 
 
+	if (priv->cfg->ops->lib->recover_from_tx_stall) {
+		init_timer(&priv->monitor_recover);
+		priv->monitor_recover.data = (unsigned long)priv;
+		priv->monitor_recover.function =
+			priv->cfg->ops->lib->recover_from_tx_stall;
+	}
+
 	tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
 	tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
 		     iwl3945_irq_tasklet, (unsigned long)priv);
 		     iwl3945_irq_tasklet, (unsigned long)priv);
 }
 }
@@ -3778,6 +3822,8 @@ static void iwl3945_cancel_deferred_work(struct iwl_priv *priv)
 	cancel_delayed_work(&priv->scan_check);
 	cancel_delayed_work(&priv->scan_check);
 	cancel_delayed_work(&priv->alive_start);
 	cancel_delayed_work(&priv->alive_start);
 	cancel_work_sync(&priv->beacon_update);
 	cancel_work_sync(&priv->beacon_update);
+	if (priv->cfg->ops->lib->recover_from_tx_stall)
+		del_timer_sync(&priv->monitor_recover);
 }
 }
 
 
 static struct attribute *iwl3945_sysfs_entries[] = {
 static struct attribute *iwl3945_sysfs_entries[] = {
@@ -3815,7 +3861,9 @@ static struct ieee80211_ops iwl3945_hw_ops = {
 	.conf_tx = iwl_mac_conf_tx,
 	.conf_tx = iwl_mac_conf_tx,
 	.reset_tsf = iwl_mac_reset_tsf,
 	.reset_tsf = iwl_mac_reset_tsf,
 	.bss_info_changed = iwl_bss_info_changed,
 	.bss_info_changed = iwl_bss_info_changed,
-	.hw_scan = iwl_mac_hw_scan
+	.hw_scan = iwl_mac_hw_scan,
+	.sta_add = iwl3945_mac_sta_add,
+	.sta_remove = iwl_mac_sta_remove,
 };
 };
 
 
 static int iwl3945_init_drv(struct iwl_priv *priv)
 static int iwl3945_init_drv(struct iwl_priv *priv)
@@ -3834,9 +3882,6 @@ static int iwl3945_init_drv(struct iwl_priv *priv)
 	mutex_init(&priv->mutex);
 	mutex_init(&priv->mutex);
 	mutex_init(&priv->sync_cmd_mutex);
 	mutex_init(&priv->sync_cmd_mutex);
 
 
-	/* Clear the driver's (not device's) station table */
-	iwl_clear_stations_table(priv);
-
 	priv->ieee_channels = NULL;
 	priv->ieee_channels = NULL;
 	priv->ieee_rates = NULL;
 	priv->ieee_rates = NULL;
 	priv->band = IEEE80211_BAND_2GHZ;
 	priv->band = IEEE80211_BAND_2GHZ;
@@ -4196,7 +4241,6 @@ static void __devexit iwl3945_pci_remove(struct pci_dev *pdev)
 	iwl3945_hw_txq_ctx_free(priv);
 	iwl3945_hw_txq_ctx_free(priv);
 
 
 	iwl3945_unset_hw_params(priv);
 	iwl3945_unset_hw_params(priv);
-	iwl_clear_stations_table(priv);
 
 
 	/*netif_stop_queue(dev); */
 	/*netif_stop_queue(dev); */
 	flush_workqueue(priv->workqueue);
 	flush_workqueue(priv->workqueue);

+ 21 - 1
drivers/net/wireless/libertas/assoc.c

@@ -31,6 +31,9 @@ u8 lbs_bg_rates[MAX_RATES] =
 0x00, 0x00 };
 0x00, 0x00 };
 
 
 
 
+static int assoc_helper_wep_keys(struct lbs_private *priv,
+		struct assoc_request *assoc_req);
+
 /**
 /**
  *  @brief This function finds common rates between rates and card rates.
  *  @brief This function finds common rates between rates and card rates.
  *
  *
@@ -610,7 +613,7 @@ static int lbs_assoc_post(struct lbs_private *priv,
 
 
 	if (status_code) {
 	if (status_code) {
 		lbs_mac_event_disconnected(priv);
 		lbs_mac_event_disconnected(priv);
-		ret = -1;
+		ret = status_code;
 		goto done;
 		goto done;
 	}
 	}
 
 
@@ -813,7 +816,24 @@ static int lbs_try_associate(struct lbs_private *priv,
 		goto out;
 		goto out;
 
 
 	ret = lbs_associate(priv, assoc_req, CMD_802_11_ASSOCIATE);
 	ret = lbs_associate(priv, assoc_req, CMD_802_11_ASSOCIATE);
+	/* If the association fails with current auth mode, let's
+	 * try by changing the auth mode
+	 */
+	if ((priv->authtype_auto) &&
+			(ret == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG) &&
+			(assoc_req->secinfo.wep_enabled) &&
+			(priv->connect_status != LBS_CONNECTED)) {
+		if (priv->secinfo.auth_mode == IW_AUTH_ALG_OPEN_SYSTEM)
+			priv->secinfo.auth_mode = IW_AUTH_ALG_SHARED_KEY;
+		else
+			priv->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
+		if (!assoc_helper_wep_keys(priv, assoc_req))
+			ret = lbs_associate(priv, assoc_req,
+						CMD_802_11_ASSOCIATE);
+	}
 
 
+	if (ret)
+		ret = -1;
 out:
 out:
 	lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
 	lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
 	return ret;
 	return ret;

+ 1 - 0
drivers/net/wireless/libertas/dev.h

@@ -133,6 +133,7 @@ struct lbs_private {
 	u8 wpa_ie_len;
 	u8 wpa_ie_len;
 	u16 wep_tx_keyidx;
 	u16 wep_tx_keyidx;
 	struct enc_key wep_keys[4];
 	struct enc_key wep_keys[4];
+	u8 authtype_auto;
 
 
 	/* Wake On LAN */
 	/* Wake On LAN */
 	uint32_t wol_criteria;
 	uint32_t wol_criteria;

+ 1 - 0
drivers/net/wireless/libertas/main.c

@@ -835,6 +835,7 @@ static int lbs_init_adapter(struct lbs_private *priv)
 	priv->is_auto_deep_sleep_enabled = 0;
 	priv->is_auto_deep_sleep_enabled = 0;
 	priv->wakeup_dev_required = 0;
 	priv->wakeup_dev_required = 0;
 	init_waitqueue_head(&priv->ds_awake_q);
 	init_waitqueue_head(&priv->ds_awake_q);
+	priv->authtype_auto = 1;
 
 
 	mutex_init(&priv->lock);
 	mutex_init(&priv->lock);
 
 

+ 4 - 0
drivers/net/wireless/libertas/wext.c

@@ -1440,8 +1440,10 @@ static int lbs_set_encode(struct net_device *dev,
 		set_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags);
 		set_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags);
 
 
 	if (dwrq->flags & IW_ENCODE_RESTRICTED) {
 	if (dwrq->flags & IW_ENCODE_RESTRICTED) {
+		priv->authtype_auto = 0;
 		assoc_req->secinfo.auth_mode = IW_AUTH_ALG_SHARED_KEY;
 		assoc_req->secinfo.auth_mode = IW_AUTH_ALG_SHARED_KEY;
 	} else if (dwrq->flags & IW_ENCODE_OPEN) {
 	} else if (dwrq->flags & IW_ENCODE_OPEN) {
+		priv->authtype_auto = 0;
 		assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
 		assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
 	}
 	}
 
 
@@ -1620,8 +1622,10 @@ static int lbs_set_encodeext(struct net_device *dev,
 			goto out;
 			goto out;
 
 
 		if (dwrq->flags & IW_ENCODE_RESTRICTED) {
 		if (dwrq->flags & IW_ENCODE_RESTRICTED) {
+			priv->authtype_auto = 0;
 			assoc_req->secinfo.auth_mode = IW_AUTH_ALG_SHARED_KEY;
 			assoc_req->secinfo.auth_mode = IW_AUTH_ALG_SHARED_KEY;
 		} else if (dwrq->flags & IW_ENCODE_OPEN) {
 		} else if (dwrq->flags & IW_ENCODE_OPEN) {
+			priv->authtype_auto = 0;
 			assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
 			assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
 		}
 		}
 
 

+ 46 - 48
drivers/net/wireless/orinoco/wext.c

@@ -1505,46 +1505,44 @@ static const struct iw_priv_args orinoco_privtab[] = {
  * Structures to export the Wireless Handlers
  * Structures to export the Wireless Handlers
  */
  */
 
 
-#define STD_IW_HANDLER(id, func) \
-	[IW_IOCTL_IDX(id)] = (iw_handler) func
 static const iw_handler	orinoco_handler[] = {
 static const iw_handler	orinoco_handler[] = {
-	STD_IW_HANDLER(SIOCSIWCOMMIT,	orinoco_ioctl_commit),
-	STD_IW_HANDLER(SIOCGIWNAME,	cfg80211_wext_giwname),
-	STD_IW_HANDLER(SIOCSIWFREQ,	orinoco_ioctl_setfreq),
-	STD_IW_HANDLER(SIOCGIWFREQ,	orinoco_ioctl_getfreq),
-	STD_IW_HANDLER(SIOCSIWMODE,	cfg80211_wext_siwmode),
-	STD_IW_HANDLER(SIOCGIWMODE,	cfg80211_wext_giwmode),
-	STD_IW_HANDLER(SIOCSIWSENS,	orinoco_ioctl_setsens),
-	STD_IW_HANDLER(SIOCGIWSENS,	orinoco_ioctl_getsens),
-	STD_IW_HANDLER(SIOCGIWRANGE,	cfg80211_wext_giwrange),
-	STD_IW_HANDLER(SIOCSIWSPY,	iw_handler_set_spy),
-	STD_IW_HANDLER(SIOCGIWSPY,	iw_handler_get_spy),
-	STD_IW_HANDLER(SIOCSIWTHRSPY,	iw_handler_set_thrspy),
-	STD_IW_HANDLER(SIOCGIWTHRSPY,	iw_handler_get_thrspy),
-	STD_IW_HANDLER(SIOCSIWAP,	orinoco_ioctl_setwap),
-	STD_IW_HANDLER(SIOCGIWAP,	orinoco_ioctl_getwap),
-	STD_IW_HANDLER(SIOCSIWSCAN,	cfg80211_wext_siwscan),
-	STD_IW_HANDLER(SIOCGIWSCAN,	cfg80211_wext_giwscan),
-	STD_IW_HANDLER(SIOCSIWESSID,	orinoco_ioctl_setessid),
-	STD_IW_HANDLER(SIOCGIWESSID,	orinoco_ioctl_getessid),
-	STD_IW_HANDLER(SIOCSIWRATE,	orinoco_ioctl_setrate),
-	STD_IW_HANDLER(SIOCGIWRATE,	orinoco_ioctl_getrate),
-	STD_IW_HANDLER(SIOCSIWRTS,	orinoco_ioctl_setrts),
-	STD_IW_HANDLER(SIOCGIWRTS,	orinoco_ioctl_getrts),
-	STD_IW_HANDLER(SIOCSIWFRAG,	orinoco_ioctl_setfrag),
-	STD_IW_HANDLER(SIOCGIWFRAG,	orinoco_ioctl_getfrag),
-	STD_IW_HANDLER(SIOCGIWRETRY,	orinoco_ioctl_getretry),
-	STD_IW_HANDLER(SIOCSIWENCODE,	orinoco_ioctl_setiwencode),
-	STD_IW_HANDLER(SIOCGIWENCODE,	orinoco_ioctl_getiwencode),
-	STD_IW_HANDLER(SIOCSIWPOWER,	orinoco_ioctl_setpower),
-	STD_IW_HANDLER(SIOCGIWPOWER,	orinoco_ioctl_getpower),
-	STD_IW_HANDLER(SIOCSIWGENIE,	orinoco_ioctl_set_genie),
-	STD_IW_HANDLER(SIOCGIWGENIE,	orinoco_ioctl_get_genie),
-	STD_IW_HANDLER(SIOCSIWMLME,	orinoco_ioctl_set_mlme),
-	STD_IW_HANDLER(SIOCSIWAUTH,	orinoco_ioctl_set_auth),
-	STD_IW_HANDLER(SIOCGIWAUTH,	orinoco_ioctl_get_auth),
-	STD_IW_HANDLER(SIOCSIWENCODEEXT, orinoco_ioctl_set_encodeext),
-	STD_IW_HANDLER(SIOCGIWENCODEEXT, orinoco_ioctl_get_encodeext),
+	IW_HANDLER(SIOCSIWCOMMIT,	(iw_handler)orinoco_ioctl_commit),
+	IW_HANDLER(SIOCGIWNAME,		(iw_handler)cfg80211_wext_giwname),
+	IW_HANDLER(SIOCSIWFREQ,		(iw_handler)orinoco_ioctl_setfreq),
+	IW_HANDLER(SIOCGIWFREQ,		(iw_handler)orinoco_ioctl_getfreq),
+	IW_HANDLER(SIOCSIWMODE,		(iw_handler)cfg80211_wext_siwmode),
+	IW_HANDLER(SIOCGIWMODE,		(iw_handler)cfg80211_wext_giwmode),
+	IW_HANDLER(SIOCSIWSENS,		(iw_handler)orinoco_ioctl_setsens),
+	IW_HANDLER(SIOCGIWSENS,		(iw_handler)orinoco_ioctl_getsens),
+	IW_HANDLER(SIOCGIWRANGE,	(iw_handler)cfg80211_wext_giwrange),
+	IW_HANDLER(SIOCSIWSPY,		iw_handler_set_spy),
+	IW_HANDLER(SIOCGIWSPY,		iw_handler_get_spy),
+	IW_HANDLER(SIOCSIWTHRSPY,	iw_handler_set_thrspy),
+	IW_HANDLER(SIOCGIWTHRSPY,	iw_handler_get_thrspy),
+	IW_HANDLER(SIOCSIWAP,		(iw_handler)orinoco_ioctl_setwap),
+	IW_HANDLER(SIOCGIWAP,		(iw_handler)orinoco_ioctl_getwap),
+	IW_HANDLER(SIOCSIWSCAN,		(iw_handler)cfg80211_wext_siwscan),
+	IW_HANDLER(SIOCGIWSCAN,		(iw_handler)cfg80211_wext_giwscan),
+	IW_HANDLER(SIOCSIWESSID,	(iw_handler)orinoco_ioctl_setessid),
+	IW_HANDLER(SIOCGIWESSID,	(iw_handler)orinoco_ioctl_getessid),
+	IW_HANDLER(SIOCSIWRATE,		(iw_handler)orinoco_ioctl_setrate),
+	IW_HANDLER(SIOCGIWRATE,		(iw_handler)orinoco_ioctl_getrate),
+	IW_HANDLER(SIOCSIWRTS,		(iw_handler)orinoco_ioctl_setrts),
+	IW_HANDLER(SIOCGIWRTS,		(iw_handler)orinoco_ioctl_getrts),
+	IW_HANDLER(SIOCSIWFRAG,		(iw_handler)orinoco_ioctl_setfrag),
+	IW_HANDLER(SIOCGIWFRAG,		(iw_handler)orinoco_ioctl_getfrag),
+	IW_HANDLER(SIOCGIWRETRY,	(iw_handler)orinoco_ioctl_getretry),
+	IW_HANDLER(SIOCSIWENCODE,	(iw_handler)orinoco_ioctl_setiwencode),
+	IW_HANDLER(SIOCGIWENCODE,	(iw_handler)orinoco_ioctl_getiwencode),
+	IW_HANDLER(SIOCSIWPOWER,	(iw_handler)orinoco_ioctl_setpower),
+	IW_HANDLER(SIOCGIWPOWER,	(iw_handler)orinoco_ioctl_getpower),
+	IW_HANDLER(SIOCSIWGENIE,	orinoco_ioctl_set_genie),
+	IW_HANDLER(SIOCGIWGENIE,	orinoco_ioctl_get_genie),
+	IW_HANDLER(SIOCSIWMLME,		orinoco_ioctl_set_mlme),
+	IW_HANDLER(SIOCSIWAUTH,		orinoco_ioctl_set_auth),
+	IW_HANDLER(SIOCGIWAUTH,		orinoco_ioctl_get_auth),
+	IW_HANDLER(SIOCSIWENCODEEXT,	orinoco_ioctl_set_encodeext),
+	IW_HANDLER(SIOCGIWENCODEEXT,	orinoco_ioctl_get_encodeext),
 };
 };
 
 
 
 
@@ -1552,15 +1550,15 @@ static const iw_handler	orinoco_handler[] = {
   Added typecasting since we no longer use iwreq_data -- Moustafa
   Added typecasting since we no longer use iwreq_data -- Moustafa
  */
  */
 static const iw_handler	orinoco_private_handler[] = {
 static const iw_handler	orinoco_private_handler[] = {
-	[0] = (iw_handler) orinoco_ioctl_reset,
-	[1] = (iw_handler) orinoco_ioctl_reset,
-	[2] = (iw_handler) orinoco_ioctl_setport3,
-	[3] = (iw_handler) orinoco_ioctl_getport3,
-	[4] = (iw_handler) orinoco_ioctl_setpreamble,
-	[5] = (iw_handler) orinoco_ioctl_getpreamble,
-	[6] = (iw_handler) orinoco_ioctl_setibssport,
-	[7] = (iw_handler) orinoco_ioctl_getibssport,
-	[9] = (iw_handler) orinoco_ioctl_getrid,
+	[0] = (iw_handler)orinoco_ioctl_reset,
+	[1] = (iw_handler)orinoco_ioctl_reset,
+	[2] = (iw_handler)orinoco_ioctl_setport3,
+	[3] = (iw_handler)orinoco_ioctl_getport3,
+	[4] = (iw_handler)orinoco_ioctl_setpreamble,
+	[5] = (iw_handler)orinoco_ioctl_getpreamble,
+	[6] = (iw_handler)orinoco_ioctl_setibssport,
+	[7] = (iw_handler)orinoco_ioctl_getibssport,
+	[9] = (iw_handler)orinoco_ioctl_getrid,
 };
 };
 
 
 const struct iw_handler_def orinoco_handler_def = {
 const struct iw_handler_def orinoco_handler_def = {

+ 97 - 115
drivers/net/wireless/ray_cs.c

@@ -1113,10 +1113,10 @@ static const struct ethtool_ops netdev_ethtool_ops = {
 /*
 /*
  * Wireless Handler : get protocol name
  * Wireless Handler : get protocol name
  */
  */
-static int ray_get_name(struct net_device *dev,
-			struct iw_request_info *info, char *cwrq, char *extra)
+static int ray_get_name(struct net_device *dev, struct iw_request_info *info,
+			union iwreq_data *wrqu, char *extra)
 {
 {
-	strcpy(cwrq, "IEEE 802.11-FH");
+	strcpy(wrqu->name, "IEEE 802.11-FH");
 	return 0;
 	return 0;
 }
 }
 
 
@@ -1124,9 +1124,8 @@ static int ray_get_name(struct net_device *dev,
 /*
 /*
  * Wireless Handler : set frequency
  * Wireless Handler : set frequency
  */
  */
-static int ray_set_freq(struct net_device *dev,
-			struct iw_request_info *info,
-			struct iw_freq *fwrq, char *extra)
+static int ray_set_freq(struct net_device *dev, struct iw_request_info *info,
+			union iwreq_data *wrqu, char *extra)
 {
 {
 	ray_dev_t *local = netdev_priv(dev);
 	ray_dev_t *local = netdev_priv(dev);
 	int err = -EINPROGRESS;	/* Call commit handler */
 	int err = -EINPROGRESS;	/* Call commit handler */
@@ -1136,10 +1135,10 @@ static int ray_set_freq(struct net_device *dev,
 		return -EBUSY;
 		return -EBUSY;
 
 
 	/* Setting by channel number */
 	/* Setting by channel number */
-	if ((fwrq->m > USA_HOP_MOD) || (fwrq->e > 0))
+	if ((wrqu->freq.m > USA_HOP_MOD) || (wrqu->freq.e > 0))
 		err = -EOPNOTSUPP;
 		err = -EOPNOTSUPP;
 	else
 	else
-		local->sparm.b5.a_hop_pattern = fwrq->m;
+		local->sparm.b5.a_hop_pattern = wrqu->freq.m;
 
 
 	return err;
 	return err;
 }
 }
@@ -1148,14 +1147,13 @@ static int ray_set_freq(struct net_device *dev,
 /*
 /*
  * Wireless Handler : get frequency
  * Wireless Handler : get frequency
  */
  */
-static int ray_get_freq(struct net_device *dev,
-			struct iw_request_info *info,
-			struct iw_freq *fwrq, char *extra)
+static int ray_get_freq(struct net_device *dev, struct iw_request_info *info,
+			union iwreq_data *wrqu, char *extra)
 {
 {
 	ray_dev_t *local = netdev_priv(dev);
 	ray_dev_t *local = netdev_priv(dev);
 
 
-	fwrq->m = local->sparm.b5.a_hop_pattern;
-	fwrq->e = 0;
+	wrqu->freq.m = local->sparm.b5.a_hop_pattern;
+	wrqu->freq.e = 0;
 	return 0;
 	return 0;
 }
 }
 
 
@@ -1163,9 +1161,8 @@ static int ray_get_freq(struct net_device *dev,
 /*
 /*
  * Wireless Handler : set ESSID
  * Wireless Handler : set ESSID
  */
  */
-static int ray_set_essid(struct net_device *dev,
-			 struct iw_request_info *info,
-			 struct iw_point *dwrq, char *extra)
+static int ray_set_essid(struct net_device *dev, struct iw_request_info *info,
+			 union iwreq_data *wrqu, char *extra)
 {
 {
 	ray_dev_t *local = netdev_priv(dev);
 	ray_dev_t *local = netdev_priv(dev);
 
 
@@ -1174,19 +1171,17 @@ static int ray_set_essid(struct net_device *dev,
 		return -EBUSY;
 		return -EBUSY;
 
 
 	/* Check if we asked for `any' */
 	/* Check if we asked for `any' */
-	if (dwrq->flags == 0) {
+	if (wrqu->essid.flags == 0)
 		/* Corey : can you do that ? */
 		/* Corey : can you do that ? */
 		return -EOPNOTSUPP;
 		return -EOPNOTSUPP;
-	} else {
-		/* Check the size of the string */
-		if (dwrq->length > IW_ESSID_MAX_SIZE) {
-			return -E2BIG;
-		}
 
 
-		/* Set the ESSID in the card */
-		memset(local->sparm.b5.a_current_ess_id, 0, IW_ESSID_MAX_SIZE);
-		memcpy(local->sparm.b5.a_current_ess_id, extra, dwrq->length);
-	}
+	/* Check the size of the string */
+	if (wrqu->essid.length > IW_ESSID_MAX_SIZE)
+		return -E2BIG;
+
+	/* Set the ESSID in the card */
+	memset(local->sparm.b5.a_current_ess_id, 0, IW_ESSID_MAX_SIZE);
+	memcpy(local->sparm.b5.a_current_ess_id, extra, wrqu->essid.length);
 
 
 	return -EINPROGRESS;	/* Call commit handler */
 	return -EINPROGRESS;	/* Call commit handler */
 }
 }
@@ -1195,9 +1190,8 @@ static int ray_set_essid(struct net_device *dev,
 /*
 /*
  * Wireless Handler : get ESSID
  * Wireless Handler : get ESSID
  */
  */
-static int ray_get_essid(struct net_device *dev,
-			 struct iw_request_info *info,
-			 struct iw_point *dwrq, char *extra)
+static int ray_get_essid(struct net_device *dev, struct iw_request_info *info,
+			 union iwreq_data *wrqu, char *extra)
 {
 {
 	ray_dev_t *local = netdev_priv(dev);
 	ray_dev_t *local = netdev_priv(dev);
 
 
@@ -1205,8 +1199,8 @@ static int ray_get_essid(struct net_device *dev,
 	memcpy(extra, local->sparm.b5.a_current_ess_id, IW_ESSID_MAX_SIZE);
 	memcpy(extra, local->sparm.b5.a_current_ess_id, IW_ESSID_MAX_SIZE);
 
 
 	/* Push it out ! */
 	/* Push it out ! */
-	dwrq->length = strlen(extra);
-	dwrq->flags = 1;	/* active */
+	wrqu->essid.length = strlen(extra);
+	wrqu->essid.flags = 1;	/* active */
 
 
 	return 0;
 	return 0;
 }
 }
@@ -1215,14 +1209,13 @@ static int ray_get_essid(struct net_device *dev,
 /*
 /*
  * Wireless Handler : get AP address
  * Wireless Handler : get AP address
  */
  */
-static int ray_get_wap(struct net_device *dev,
-		       struct iw_request_info *info,
-		       struct sockaddr *awrq, char *extra)
+static int ray_get_wap(struct net_device *dev, struct iw_request_info *info,
+		       union iwreq_data *wrqu, char *extra)
 {
 {
 	ray_dev_t *local = netdev_priv(dev);
 	ray_dev_t *local = netdev_priv(dev);
 
 
-	memcpy(awrq->sa_data, local->bss_id, ETH_ALEN);
-	awrq->sa_family = ARPHRD_ETHER;
+	memcpy(wrqu->ap_addr.sa_data, local->bss_id, ETH_ALEN);
+	wrqu->ap_addr.sa_family = ARPHRD_ETHER;
 
 
 	return 0;
 	return 0;
 }
 }
@@ -1231,9 +1224,8 @@ static int ray_get_wap(struct net_device *dev,
 /*
 /*
  * Wireless Handler : set Bit-Rate
  * Wireless Handler : set Bit-Rate
  */
  */
-static int ray_set_rate(struct net_device *dev,
-			struct iw_request_info *info,
-			struct iw_param *vwrq, char *extra)
+static int ray_set_rate(struct net_device *dev, struct iw_request_info *info,
+			union iwreq_data *wrqu, char *extra)
 {
 {
 	ray_dev_t *local = netdev_priv(dev);
 	ray_dev_t *local = netdev_priv(dev);
 
 
@@ -1242,15 +1234,15 @@ static int ray_set_rate(struct net_device *dev,
 		return -EBUSY;
 		return -EBUSY;
 
 
 	/* Check if rate is in range */
 	/* Check if rate is in range */
-	if ((vwrq->value != 1000000) && (vwrq->value != 2000000))
+	if ((wrqu->bitrate.value != 1000000) && (wrqu->bitrate.value != 2000000))
 		return -EINVAL;
 		return -EINVAL;
 
 
 	/* Hack for 1.5 Mb/s instead of 2 Mb/s */
 	/* Hack for 1.5 Mb/s instead of 2 Mb/s */
 	if ((local->fw_ver == 0x55) &&	/* Please check */
 	if ((local->fw_ver == 0x55) &&	/* Please check */
-	    (vwrq->value == 2000000))
+	    (wrqu->bitrate.value == 2000000))
 		local->net_default_tx_rate = 3;
 		local->net_default_tx_rate = 3;
 	else
 	else
-		local->net_default_tx_rate = vwrq->value / 500000;
+		local->net_default_tx_rate = wrqu->bitrate.value / 500000;
 
 
 	return 0;
 	return 0;
 }
 }
@@ -1259,17 +1251,16 @@ static int ray_set_rate(struct net_device *dev,
 /*
 /*
  * Wireless Handler : get Bit-Rate
  * Wireless Handler : get Bit-Rate
  */
  */
-static int ray_get_rate(struct net_device *dev,
-			struct iw_request_info *info,
-			struct iw_param *vwrq, char *extra)
+static int ray_get_rate(struct net_device *dev, struct iw_request_info *info,
+			union iwreq_data *wrqu, char *extra)
 {
 {
 	ray_dev_t *local = netdev_priv(dev);
 	ray_dev_t *local = netdev_priv(dev);
 
 
 	if (local->net_default_tx_rate == 3)
 	if (local->net_default_tx_rate == 3)
-		vwrq->value = 2000000;	/* Hum... */
+		wrqu->bitrate.value = 2000000;	/* Hum... */
 	else
 	else
-		vwrq->value = local->net_default_tx_rate * 500000;
-	vwrq->fixed = 0;	/* We are in auto mode */
+		wrqu->bitrate.value = local->net_default_tx_rate * 500000;
+	wrqu->bitrate.fixed = 0;	/* We are in auto mode */
 
 
 	return 0;
 	return 0;
 }
 }
@@ -1278,19 +1269,18 @@ static int ray_get_rate(struct net_device *dev,
 /*
 /*
  * Wireless Handler : set RTS threshold
  * Wireless Handler : set RTS threshold
  */
  */
-static int ray_set_rts(struct net_device *dev,
-		       struct iw_request_info *info,
-		       struct iw_param *vwrq, char *extra)
+static int ray_set_rts(struct net_device *dev, struct iw_request_info *info,
+		       union iwreq_data *wrqu, char *extra)
 {
 {
 	ray_dev_t *local = netdev_priv(dev);
 	ray_dev_t *local = netdev_priv(dev);
-	int rthr = vwrq->value;
+	int rthr = wrqu->rts.value;
 
 
 	/* Reject if card is already initialised */
 	/* Reject if card is already initialised */
 	if (local->card_status != CARD_AWAITING_PARAM)
 	if (local->card_status != CARD_AWAITING_PARAM)
 		return -EBUSY;
 		return -EBUSY;
 
 
 	/* if(wrq->u.rts.fixed == 0) we should complain */
 	/* if(wrq->u.rts.fixed == 0) we should complain */
-	if (vwrq->disabled)
+	if (wrqu->rts.disabled)
 		rthr = 32767;
 		rthr = 32767;
 	else {
 	else {
 		if ((rthr < 0) || (rthr > 2347))   /* What's the max packet size ??? */
 		if ((rthr < 0) || (rthr > 2347))   /* What's the max packet size ??? */
@@ -1306,16 +1296,15 @@ static int ray_set_rts(struct net_device *dev,
 /*
 /*
  * Wireless Handler : get RTS threshold
  * Wireless Handler : get RTS threshold
  */
  */
-static int ray_get_rts(struct net_device *dev,
-		       struct iw_request_info *info,
-		       struct iw_param *vwrq, char *extra)
+static int ray_get_rts(struct net_device *dev, struct iw_request_info *info,
+		       union iwreq_data *wrqu, char *extra)
 {
 {
 	ray_dev_t *local = netdev_priv(dev);
 	ray_dev_t *local = netdev_priv(dev);
 
 
-	vwrq->value = (local->sparm.b5.a_rts_threshold[0] << 8)
+	wrqu->rts.value = (local->sparm.b5.a_rts_threshold[0] << 8)
 	    + local->sparm.b5.a_rts_threshold[1];
 	    + local->sparm.b5.a_rts_threshold[1];
-	vwrq->disabled = (vwrq->value == 32767);
-	vwrq->fixed = 1;
+	wrqu->rts.disabled = (wrqu->rts.value == 32767);
+	wrqu->rts.fixed = 1;
 
 
 	return 0;
 	return 0;
 }
 }
@@ -1324,19 +1313,18 @@ static int ray_get_rts(struct net_device *dev,
 /*
 /*
  * Wireless Handler : set Fragmentation threshold
  * Wireless Handler : set Fragmentation threshold
  */
  */
-static int ray_set_frag(struct net_device *dev,
-			struct iw_request_info *info,
-			struct iw_param *vwrq, char *extra)
+static int ray_set_frag(struct net_device *dev, struct iw_request_info *info,
+			union iwreq_data *wrqu, char *extra)
 {
 {
 	ray_dev_t *local = netdev_priv(dev);
 	ray_dev_t *local = netdev_priv(dev);
-	int fthr = vwrq->value;
+	int fthr = wrqu->frag.value;
 
 
 	/* Reject if card is already initialised */
 	/* Reject if card is already initialised */
 	if (local->card_status != CARD_AWAITING_PARAM)
 	if (local->card_status != CARD_AWAITING_PARAM)
 		return -EBUSY;
 		return -EBUSY;
 
 
 	/* if(wrq->u.frag.fixed == 0) should complain */
 	/* if(wrq->u.frag.fixed == 0) should complain */
-	if (vwrq->disabled)
+	if (wrqu->frag.disabled)
 		fthr = 32767;
 		fthr = 32767;
 	else {
 	else {
 		if ((fthr < 256) || (fthr > 2347))	/* To check out ! */
 		if ((fthr < 256) || (fthr > 2347))	/* To check out ! */
@@ -1352,16 +1340,15 @@ static int ray_set_frag(struct net_device *dev,
 /*
 /*
  * Wireless Handler : get Fragmentation threshold
  * Wireless Handler : get Fragmentation threshold
  */
  */
-static int ray_get_frag(struct net_device *dev,
-			struct iw_request_info *info,
-			struct iw_param *vwrq, char *extra)
+static int ray_get_frag(struct net_device *dev, struct iw_request_info *info,
+			union iwreq_data *wrqu, char *extra)
 {
 {
 	ray_dev_t *local = netdev_priv(dev);
 	ray_dev_t *local = netdev_priv(dev);
 
 
-	vwrq->value = (local->sparm.b5.a_frag_threshold[0] << 8)
+	wrqu->frag.value = (local->sparm.b5.a_frag_threshold[0] << 8)
 	    + local->sparm.b5.a_frag_threshold[1];
 	    + local->sparm.b5.a_frag_threshold[1];
-	vwrq->disabled = (vwrq->value == 32767);
-	vwrq->fixed = 1;
+	wrqu->frag.disabled = (wrqu->frag.value == 32767);
+	wrqu->frag.fixed = 1;
 
 
 	return 0;
 	return 0;
 }
 }
@@ -1370,8 +1357,8 @@ static int ray_get_frag(struct net_device *dev,
 /*
 /*
  * Wireless Handler : set Mode of Operation
  * Wireless Handler : set Mode of Operation
  */
  */
-static int ray_set_mode(struct net_device *dev,
-			struct iw_request_info *info, __u32 *uwrq, char *extra)
+static int ray_set_mode(struct net_device *dev, struct iw_request_info *info,
+			union iwreq_data *wrqu, char *extra)
 {
 {
 	ray_dev_t *local = netdev_priv(dev);
 	ray_dev_t *local = netdev_priv(dev);
 	int err = -EINPROGRESS;	/* Call commit handler */
 	int err = -EINPROGRESS;	/* Call commit handler */
@@ -1381,7 +1368,7 @@ static int ray_set_mode(struct net_device *dev,
 	if (local->card_status != CARD_AWAITING_PARAM)
 	if (local->card_status != CARD_AWAITING_PARAM)
 		return -EBUSY;
 		return -EBUSY;
 
 
-	switch (*uwrq) {
+	switch (wrqu->mode) {
 	case IW_MODE_ADHOC:
 	case IW_MODE_ADHOC:
 		card_mode = 0;
 		card_mode = 0;
 		/* Fall through */
 		/* Fall through */
@@ -1399,15 +1386,15 @@ static int ray_set_mode(struct net_device *dev,
 /*
 /*
  * Wireless Handler : get Mode of Operation
  * Wireless Handler : get Mode of Operation
  */
  */
-static int ray_get_mode(struct net_device *dev,
-			struct iw_request_info *info, __u32 *uwrq, char *extra)
+static int ray_get_mode(struct net_device *dev, struct iw_request_info *info,
+			union iwreq_data *wrqu, char *extra)
 {
 {
 	ray_dev_t *local = netdev_priv(dev);
 	ray_dev_t *local = netdev_priv(dev);
 
 
 	if (local->sparm.b5.a_network_type)
 	if (local->sparm.b5.a_network_type)
-		*uwrq = IW_MODE_INFRA;
+		wrqu->mode = IW_MODE_INFRA;
 	else
 	else
-		*uwrq = IW_MODE_ADHOC;
+		wrqu->mode = IW_MODE_ADHOC;
 
 
 	return 0;
 	return 0;
 }
 }
@@ -1416,16 +1403,15 @@ static int ray_get_mode(struct net_device *dev,
 /*
 /*
  * Wireless Handler : get range info
  * Wireless Handler : get range info
  */
  */
-static int ray_get_range(struct net_device *dev,
-			 struct iw_request_info *info,
-			 struct iw_point *dwrq, char *extra)
+static int ray_get_range(struct net_device *dev, struct iw_request_info *info,
+			 union iwreq_data *wrqu, char *extra)
 {
 {
 	struct iw_range *range = (struct iw_range *)extra;
 	struct iw_range *range = (struct iw_range *)extra;
 
 
-	memset((char *)range, 0, sizeof(struct iw_range));
+	memset(range, 0, sizeof(struct iw_range));
 
 
 	/* Set the length (very important for backward compatibility) */
 	/* Set the length (very important for backward compatibility) */
-	dwrq->length = sizeof(struct iw_range);
+	wrqu->data.length = sizeof(struct iw_range);
 
 
 	/* Set the Wireless Extension versions */
 	/* Set the Wireless Extension versions */
 	range->we_version_compiled = WIRELESS_EXT;
 	range->we_version_compiled = WIRELESS_EXT;
@@ -1448,8 +1434,7 @@ static int ray_get_range(struct net_device *dev,
 /*
 /*
  * Wireless Private Handler : set framing mode
  * Wireless Private Handler : set framing mode
  */
  */
-static int ray_set_framing(struct net_device *dev,
-			   struct iw_request_info *info,
+static int ray_set_framing(struct net_device *dev, struct iw_request_info *info,
 			   union iwreq_data *wrqu, char *extra)
 			   union iwreq_data *wrqu, char *extra)
 {
 {
 	translate = *(extra);	/* Set framing mode */
 	translate = *(extra);	/* Set framing mode */
@@ -1461,8 +1446,7 @@ static int ray_set_framing(struct net_device *dev,
 /*
 /*
  * Wireless Private Handler : get framing mode
  * Wireless Private Handler : get framing mode
  */
  */
-static int ray_get_framing(struct net_device *dev,
-			   struct iw_request_info *info,
+static int ray_get_framing(struct net_device *dev, struct iw_request_info *info,
 			   union iwreq_data *wrqu, char *extra)
 			   union iwreq_data *wrqu, char *extra)
 {
 {
 	*(extra) = translate;
 	*(extra) = translate;
@@ -1474,8 +1458,7 @@ static int ray_get_framing(struct net_device *dev,
 /*
 /*
  * Wireless Private Handler : get country
  * Wireless Private Handler : get country
  */
  */
-static int ray_get_country(struct net_device *dev,
-			   struct iw_request_info *info,
+static int ray_get_country(struct net_device *dev, struct iw_request_info *info,
 			   union iwreq_data *wrqu, char *extra)
 			   union iwreq_data *wrqu, char *extra)
 {
 {
 	*(extra) = country;
 	*(extra) = country;
@@ -1487,10 +1470,9 @@ static int ray_get_country(struct net_device *dev,
 /*
 /*
  * Commit handler : called after a bunch of SET operations
  * Commit handler : called after a bunch of SET operations
  */
  */
-static int ray_commit(struct net_device *dev, struct iw_request_info *info,	/* NULL */
-		      void *zwrq,	/* NULL */
-		      char *extra)
-{ /* NULL */
+static int ray_commit(struct net_device *dev, struct iw_request_info *info,
+		      union iwreq_data *wrqu, char *extra)
+{
 	return 0;
 	return 0;
 }
 }
 
 
@@ -1531,28 +1513,28 @@ static iw_stats *ray_get_wireless_stats(struct net_device *dev)
  */
  */
 
 
 static const iw_handler ray_handler[] = {
 static const iw_handler ray_handler[] = {
-	[SIOCSIWCOMMIT - SIOCIWFIRST] = (iw_handler) ray_commit,
-	[SIOCGIWNAME - SIOCIWFIRST] = (iw_handler) ray_get_name,
-	[SIOCSIWFREQ - SIOCIWFIRST] = (iw_handler) ray_set_freq,
-	[SIOCGIWFREQ - SIOCIWFIRST] = (iw_handler) ray_get_freq,
-	[SIOCSIWMODE - SIOCIWFIRST] = (iw_handler) ray_set_mode,
-	[SIOCGIWMODE - SIOCIWFIRST] = (iw_handler) ray_get_mode,
-	[SIOCGIWRANGE - SIOCIWFIRST] = (iw_handler) ray_get_range,
+	IW_HANDLER(SIOCSIWCOMMIT, ray_commit),
+	IW_HANDLER(SIOCGIWNAME, ray_get_name),
+	IW_HANDLER(SIOCSIWFREQ, ray_set_freq),
+	IW_HANDLER(SIOCGIWFREQ, ray_get_freq),
+	IW_HANDLER(SIOCSIWMODE, ray_set_mode),
+	IW_HANDLER(SIOCGIWMODE, ray_get_mode),
+	IW_HANDLER(SIOCGIWRANGE, ray_get_range),
 #ifdef WIRELESS_SPY
 #ifdef WIRELESS_SPY
-	[SIOCSIWSPY - SIOCIWFIRST] = (iw_handler) iw_handler_set_spy,
-	[SIOCGIWSPY - SIOCIWFIRST] = (iw_handler) iw_handler_get_spy,
-	[SIOCSIWTHRSPY - SIOCIWFIRST] = (iw_handler) iw_handler_set_thrspy,
-	[SIOCGIWTHRSPY - SIOCIWFIRST] = (iw_handler) iw_handler_get_thrspy,
+	IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy),
+	IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy),
+	IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy),
+	IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy),
 #endif /* WIRELESS_SPY */
 #endif /* WIRELESS_SPY */
-	[SIOCGIWAP - SIOCIWFIRST] = (iw_handler) ray_get_wap,
-	[SIOCSIWESSID - SIOCIWFIRST] = (iw_handler) ray_set_essid,
-	[SIOCGIWESSID - SIOCIWFIRST] = (iw_handler) ray_get_essid,
-	[SIOCSIWRATE - SIOCIWFIRST] = (iw_handler) ray_set_rate,
-	[SIOCGIWRATE - SIOCIWFIRST] = (iw_handler) ray_get_rate,
-	[SIOCSIWRTS - SIOCIWFIRST] = (iw_handler) ray_set_rts,
-	[SIOCGIWRTS - SIOCIWFIRST] = (iw_handler) ray_get_rts,
-	[SIOCSIWFRAG - SIOCIWFIRST] = (iw_handler) ray_set_frag,
-	[SIOCGIWFRAG - SIOCIWFIRST] = (iw_handler) ray_get_frag,
+	IW_HANDLER(SIOCGIWAP, ray_get_wap),
+	IW_HANDLER(SIOCSIWESSID, ray_set_essid),
+	IW_HANDLER(SIOCGIWESSID, ray_get_essid),
+	IW_HANDLER(SIOCSIWRATE, ray_set_rate),
+	IW_HANDLER(SIOCGIWRATE, ray_get_rate),
+	IW_HANDLER(SIOCSIWRTS, ray_set_rts),
+	IW_HANDLER(SIOCGIWRTS, ray_get_rts),
+	IW_HANDLER(SIOCSIWFRAG, ray_set_frag),
+	IW_HANDLER(SIOCGIWFRAG, ray_get_frag),
 };
 };
 
 
 #define SIOCSIPFRAMING	SIOCIWFIRSTPRIV	/* Set framing mode */
 #define SIOCSIPFRAMING	SIOCIWFIRSTPRIV	/* Set framing mode */
@@ -1560,9 +1542,9 @@ static const iw_handler ray_handler[] = {
 #define SIOCGIPCOUNTRY	SIOCIWFIRSTPRIV + 3	/* Get country code */
 #define SIOCGIPCOUNTRY	SIOCIWFIRSTPRIV + 3	/* Get country code */
 
 
 static const iw_handler ray_private_handler[] = {
 static const iw_handler ray_private_handler[] = {
-	[0] = (iw_handler) ray_set_framing,
-	[1] = (iw_handler) ray_get_framing,
-	[3] = (iw_handler) ray_get_country,
+	[0] = ray_set_framing,
+	[1] = ray_get_framing,
+	[3] = ray_get_country,
 };
 };
 
 
 static const struct iw_priv_args ray_private_args[] = {
 static const struct iw_priv_args ray_private_args[] = {

+ 7 - 0
drivers/net/wireless/wl12xx/wl1271.h

@@ -53,6 +53,8 @@ enum {
 	DEBUG_MAC80211	= BIT(11),
 	DEBUG_MAC80211	= BIT(11),
 	DEBUG_CMD	= BIT(12),
 	DEBUG_CMD	= BIT(12),
 	DEBUG_ACX	= BIT(13),
 	DEBUG_ACX	= BIT(13),
+	DEBUG_SDIO	= BIT(14),
+	DEBUG_FILTERS   = BIT(15),
 	DEBUG_ALL	= ~0,
 	DEBUG_ALL	= ~0,
 };
 };
 
 
@@ -344,12 +346,14 @@ struct wl1271_if_operations {
 		     bool fixed);
 		     bool fixed);
 	void (*reset)(struct wl1271 *wl);
 	void (*reset)(struct wl1271 *wl);
 	void (*init)(struct wl1271 *wl);
 	void (*init)(struct wl1271 *wl);
+	void (*power)(struct wl1271 *wl, bool enable);
 	struct device* (*dev)(struct wl1271 *wl);
 	struct device* (*dev)(struct wl1271 *wl);
 	void (*enable_irq)(struct wl1271 *wl);
 	void (*enable_irq)(struct wl1271 *wl);
 	void (*disable_irq)(struct wl1271 *wl);
 	void (*disable_irq)(struct wl1271 *wl);
 };
 };
 
 
 struct wl1271 {
 struct wl1271 {
+	struct platform_device *plat_dev;
 	struct ieee80211_hw *hw;
 	struct ieee80211_hw *hw;
 	bool mac80211_registered;
 	bool mac80211_registered;
 
 
@@ -456,6 +460,7 @@ struct wl1271 {
 	/* Default key (for WEP) */
 	/* Default key (for WEP) */
 	u32 default_key;
 	u32 default_key;
 
 
+	unsigned int filters;
 	unsigned int rx_config;
 	unsigned int rx_config;
 	unsigned int rx_filter;
 	unsigned int rx_filter;
 
 
@@ -483,6 +488,8 @@ struct wl1271 {
 	/* Current chipset configuration */
 	/* Current chipset configuration */
 	struct conf_drv_settings conf;
 	struct conf_drv_settings conf;
 
 
+	bool sg_enabled;
+
 	struct list_head list;
 	struct list_head list;
 };
 };
 
 

+ 9 - 16
drivers/net/wireless/wl12xx/wl1271_acx.c

@@ -534,7 +534,7 @@ out:
 }
 }
 
 
 
 
-int wl1271_acx_sg_enable(struct wl1271 *wl)
+int wl1271_acx_sg_enable(struct wl1271 *wl, bool enable)
 {
 {
 	struct acx_bt_wlan_coex *pta;
 	struct acx_bt_wlan_coex *pta;
 	int ret;
 	int ret;
@@ -547,7 +547,10 @@ int wl1271_acx_sg_enable(struct wl1271 *wl)
 		goto out;
 		goto out;
 	}
 	}
 
 
-	pta->enable = SG_ENABLE;
+	if (enable)
+		pta->enable = wl->conf.sg.state;
+	else
+		pta->enable = CONF_SG_DISABLE;
 
 
 	ret = wl1271_cmd_configure(wl, ACX_SG_ENABLE, pta, sizeof(*pta));
 	ret = wl1271_cmd_configure(wl, ACX_SG_ENABLE, pta, sizeof(*pta));
 	if (ret < 0) {
 	if (ret < 0) {
@@ -564,7 +567,7 @@ int wl1271_acx_sg_cfg(struct wl1271 *wl)
 {
 {
 	struct acx_bt_wlan_coex_param *param;
 	struct acx_bt_wlan_coex_param *param;
 	struct conf_sg_settings *c = &wl->conf.sg;
 	struct conf_sg_settings *c = &wl->conf.sg;
-	int ret;
+	int i, ret;
 
 
 	wl1271_debug(DEBUG_ACX, "acx sg cfg");
 	wl1271_debug(DEBUG_ACX, "acx sg cfg");
 
 
@@ -575,19 +578,9 @@ int wl1271_acx_sg_cfg(struct wl1271 *wl)
 	}
 	}
 
 
 	/* BT-WLAN coext parameters */
 	/* BT-WLAN coext parameters */
-	param->per_threshold = cpu_to_le32(c->per_threshold);
-	param->max_scan_compensation_time =
-		cpu_to_le32(c->max_scan_compensation_time);
-	param->nfs_sample_interval = cpu_to_le16(c->nfs_sample_interval);
-	param->load_ratio = c->load_ratio;
-	param->auto_ps_mode = c->auto_ps_mode;
-	param->probe_req_compensation = c->probe_req_compensation;
-	param->scan_window_compensation = c->scan_window_compensation;
-	param->antenna_config = c->antenna_config;
-	param->beacon_miss_threshold = c->beacon_miss_threshold;
-	param->rate_adaptation_threshold =
-		cpu_to_le32(c->rate_adaptation_threshold);
-	param->rate_adaptation_snr = c->rate_adaptation_snr;
+	for (i = 0; i < CONF_SG_PARAMS_MAX; i++)
+		param->params[i] = c->params[i];
+	param->param_idx = CONF_SG_PARAMS_ALL;
 
 
 	ret = wl1271_cmd_configure(wl, ACX_SG_CFG, param, sizeof(*param));
 	ret = wl1271_cmd_configure(wl, ACX_SG_CFG, param, sizeof(*param));
 	if (ret < 0) {
 	if (ret < 0) {

+ 7 - 61
drivers/net/wireless/wl12xx/wl1271_acx.h

@@ -392,81 +392,27 @@ struct acx_conn_monit_params {
        __le32 bss_lose_timeout; /* number of TU's from synch fail */
        __le32 bss_lose_timeout; /* number of TU's from synch fail */
 } __attribute__ ((packed));
 } __attribute__ ((packed));
 
 
-enum {
-	SG_ENABLE = 0,
-	SG_DISABLE,
-	SG_SENSE_NO_ACTIVITY,
-	SG_SENSE_ACTIVE
-};
-
 struct acx_bt_wlan_coex {
 struct acx_bt_wlan_coex {
 	struct acx_header header;
 	struct acx_header header;
 
 
-	/*
-	 * 0 -> PTA enabled
-	 * 1 -> PTA disabled
-	 * 2 -> sense no active mode, i.e.
-	 *      an interrupt is sent upon
-	 *      BT activity.
-	 * 3 -> PTA is switched on in response
-	 *      to the interrupt sending.
-	 */
 	u8 enable;
 	u8 enable;
 	u8 pad[3];
 	u8 pad[3];
 } __attribute__ ((packed));
 } __attribute__ ((packed));
 
 
-struct acx_dco_itrim_params {
+struct acx_bt_wlan_coex_param {
 	struct acx_header header;
 	struct acx_header header;
 
 
-	u8 enable;
+	__le32 params[CONF_SG_PARAMS_MAX];
+	u8 param_idx;
 	u8 padding[3];
 	u8 padding[3];
-	__le32 timeout;
 } __attribute__ ((packed));
 } __attribute__ ((packed));
 
 
-#define PTA_ANTENNA_TYPE_DEF		  (0)
-#define PTA_BT_HP_MAXTIME_DEF		  (2000)
-#define PTA_WLAN_HP_MAX_TIME_DEF	  (5000)
-#define PTA_SENSE_DISABLE_TIMER_DEF	  (1350)
-#define PTA_PROTECTIVE_RX_TIME_DEF	  (1500)
-#define PTA_PROTECTIVE_TX_TIME_DEF	  (1500)
-#define PTA_TIMEOUT_NEXT_BT_LP_PACKET_DEF (3000)
-#define PTA_SIGNALING_TYPE_DEF		  (1)
-#define PTA_AFH_LEVERAGE_ON_DEF		  (0)
-#define PTA_NUMBER_QUIET_CYCLE_DEF	  (0)
-#define PTA_MAX_NUM_CTS_DEF		  (3)
-#define PTA_NUMBER_OF_WLAN_PACKETS_DEF	  (2)
-#define PTA_NUMBER_OF_BT_PACKETS_DEF	  (2)
-#define PTA_PROTECTIVE_RX_TIME_FAST_DEF	  (1500)
-#define PTA_PROTECTIVE_TX_TIME_FAST_DEF	  (3000)
-#define PTA_CYCLE_TIME_FAST_DEF		  (8700)
-#define PTA_RX_FOR_AVALANCHE_DEF	  (5)
-#define PTA_ELP_HP_DEF			  (0)
-#define PTA_ANTI_STARVE_PERIOD_DEF	  (500)
-#define PTA_ANTI_STARVE_NUM_CYCLE_DEF	  (4)
-#define PTA_ALLOW_PA_SD_DEF		  (1)
-#define PTA_TIME_BEFORE_BEACON_DEF	  (6300)
-#define PTA_HPDM_MAX_TIME_DEF		  (1600)
-#define PTA_TIME_OUT_NEXT_WLAN_DEF	  (2550)
-#define PTA_AUTO_MODE_NO_CTS_DEF	  (0)
-#define PTA_BT_HP_RESPECTED_DEF		  (3)
-#define PTA_WLAN_RX_MIN_RATE_DEF	  (24)
-#define PTA_ACK_MODE_DEF		  (1)
-
-struct acx_bt_wlan_coex_param {
+struct acx_dco_itrim_params {
 	struct acx_header header;
 	struct acx_header header;
 
 
-	__le32 per_threshold;
-	__le32 max_scan_compensation_time;
-	__le16 nfs_sample_interval;
-	u8 load_ratio;
-	u8 auto_ps_mode;
-	u8 probe_req_compensation;
-	u8 scan_window_compensation;
-	u8 antenna_config;
-	u8 beacon_miss_threshold;
-	__le32 rate_adaptation_threshold;
-	s8 rate_adaptation_snr;
+	u8 enable;
 	u8 padding[3];
 	u8 padding[3];
+	__le32 timeout;
 } __attribute__ ((packed));
 } __attribute__ ((packed));
 
 
 struct acx_energy_detection {
 struct acx_energy_detection {
@@ -1059,7 +1005,7 @@ int wl1271_acx_dco_itrim_params(struct wl1271 *wl);
 int wl1271_acx_beacon_filter_opt(struct wl1271 *wl, bool enable_filter);
 int wl1271_acx_beacon_filter_opt(struct wl1271 *wl, bool enable_filter);
 int wl1271_acx_beacon_filter_table(struct wl1271 *wl);
 int wl1271_acx_beacon_filter_table(struct wl1271 *wl);
 int wl1271_acx_conn_monit_params(struct wl1271 *wl);
 int wl1271_acx_conn_monit_params(struct wl1271 *wl);
-int wl1271_acx_sg_enable(struct wl1271 *wl);
+int wl1271_acx_sg_enable(struct wl1271 *wl, bool enable);
 int wl1271_acx_sg_cfg(struct wl1271 *wl);
 int wl1271_acx_sg_cfg(struct wl1271 *wl);
 int wl1271_acx_cca_threshold(struct wl1271 *wl);
 int wl1271_acx_cca_threshold(struct wl1271 *wl);
 int wl1271_acx_bcn_dtim_options(struct wl1271 *wl);
 int wl1271_acx_bcn_dtim_options(struct wl1271 *wl);

+ 8 - 0
drivers/net/wireless/wl12xx/wl1271_boot.c

@@ -228,6 +228,14 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl)
 	nvs_len = sizeof(wl->nvs->nvs);
 	nvs_len = sizeof(wl->nvs->nvs);
 	nvs_ptr = (u8 *)wl->nvs->nvs;
 	nvs_ptr = (u8 *)wl->nvs->nvs;
 
 
+	/* update current MAC address to NVS */
+	nvs_ptr[11] = wl->mac_addr[0];
+	nvs_ptr[10] = wl->mac_addr[1];
+	nvs_ptr[6] = wl->mac_addr[2];
+	nvs_ptr[5] = wl->mac_addr[3];
+	nvs_ptr[4] = wl->mac_addr[4];
+	nvs_ptr[3] = wl->mac_addr[5];
+
 	/*
 	/*
 	 * Layout before the actual NVS tables:
 	 * Layout before the actual NVS tables:
 	 * 1 byte : burst length.
 	 * 1 byte : burst length.

+ 79 - 139
drivers/net/wireless/wl12xx/wl1271_cmd.c

@@ -26,6 +26,7 @@
 #include <linux/crc7.h>
 #include <linux/crc7.h>
 #include <linux/spi/spi.h>
 #include <linux/spi/spi.h>
 #include <linux/etherdevice.h>
 #include <linux/etherdevice.h>
+#include <linux/ieee80211.h>
 
 
 #include "wl1271.h"
 #include "wl1271.h"
 #include "wl1271_reg.h"
 #include "wl1271_reg.h"
@@ -280,15 +281,6 @@ int wl1271_cmd_join(struct wl1271 *wl, u8 bss_type)
 	join->rx_filter_options = cpu_to_le32(wl->rx_filter);
 	join->rx_filter_options = cpu_to_le32(wl->rx_filter);
 	join->bss_type = bss_type;
 	join->bss_type = bss_type;
 
 
-	/*
-	 * FIXME: disable temporarily all filters because after commit
-	 * 9cef8737 "mac80211: fix managed mode BSSID handling" broke
-	 * association. The filter logic needs to be implemented properly
-	 * and once that is done, this hack can be removed.
-	 */
-	join->rx_config_options = cpu_to_le32(0);
-	join->rx_filter_options = cpu_to_le32(WL1271_DEFAULT_RX_FILTER);
-
 	if (wl->band == IEEE80211_BAND_2GHZ)
 	if (wl->band == IEEE80211_BAND_2GHZ)
 		join->basic_rate_set = cpu_to_le32(CONF_HW_BIT_RATE_1MBPS   |
 		join->basic_rate_set = cpu_to_le32(CONF_HW_BIT_RATE_1MBPS   |
 						   CONF_HW_BIT_RATE_2MBPS   |
 						   CONF_HW_BIT_RATE_2MBPS   |
@@ -546,9 +538,9 @@ out:
 	return ret;
 	return ret;
 }
 }
 
 
-int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len,
-		    u8 active_scan, u8 high_prio, u8 band,
-		    u8 probe_requests)
+int wl1271_cmd_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len,
+		    const u8 *ie, size_t ie_len, u8 active_scan,
+		    u8 high_prio, u8 band, u8 probe_requests)
 {
 {
 
 
 	struct wl1271_cmd_trigger_scan_to *trigger = NULL;
 	struct wl1271_cmd_trigger_scan_to *trigger = NULL;
@@ -619,12 +611,13 @@ int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len,
 
 
 	params->params.num_channels = j;
 	params->params.num_channels = j;
 
 
-	if (len && ssid) {
-		params->params.ssid_len = len;
-		memcpy(params->params.ssid, ssid, len);
+	if (ssid_len && ssid) {
+		params->params.ssid_len = ssid_len;
+		memcpy(params->params.ssid, ssid, ssid_len);
 	}
 	}
 
 
-	ret = wl1271_cmd_build_probe_req(wl, ssid, len, ieee_band);
+	ret = wl1271_cmd_build_probe_req(wl, ssid, ssid_len,
+					 ie, ie_len, ieee_band);
 	if (ret < 0) {
 	if (ret < 0) {
 		wl1271_error("PROBE request template failed");
 		wl1271_error("PROBE request template failed");
 		goto out;
 		goto out;
@@ -655,9 +648,9 @@ int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len,
 			wl->scan.active = active_scan;
 			wl->scan.active = active_scan;
 			wl->scan.high_prio = high_prio;
 			wl->scan.high_prio = high_prio;
 			wl->scan.probe_requests = probe_requests;
 			wl->scan.probe_requests = probe_requests;
-			if (len && ssid) {
-				wl->scan.ssid_len = len;
-				memcpy(wl->scan.ssid, ssid, len);
+			if (ssid_len && ssid) {
+				wl->scan.ssid_len = ssid_len;
+				memcpy(wl->scan.ssid, ssid, ssid_len);
 			} else
 			} else
 				wl->scan.ssid_len = 0;
 				wl->scan.ssid_len = 0;
 		}
 		}
@@ -714,155 +707,102 @@ out:
 	return ret;
 	return ret;
 }
 }
 
 
-static int wl1271_build_basic_rates(u8 *rates, u8 band)
+int wl1271_cmd_build_null_data(struct wl1271 *wl)
 {
 {
-	u8 index = 0;
-
-	if (band == IEEE80211_BAND_2GHZ) {
-		rates[index++] =
-			IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_1MB;
-		rates[index++] =
-			IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_2MB;
-		rates[index++] =
-			IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_5MB;
-		rates[index++] =
-			IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_11MB;
-	} else if (band == IEEE80211_BAND_5GHZ) {
-		rates[index++] =
-			IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_6MB;
-		rates[index++] =
-			IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_12MB;
-		rates[index++] =
-			IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_24MB;
-	} else {
-		wl1271_error("build_basic_rates invalid band: %d", band);
-	}
+	struct sk_buff *skb = NULL;
+	int size;
+	void *ptr;
+	int ret = -ENOMEM;
 
 
-	return index;
-}
 
 
-static int wl1271_build_extended_rates(u8 *rates, u8 band)
-{
-	u8 index = 0;
-
-	if (band == IEEE80211_BAND_2GHZ) {
-		rates[index++] = IEEE80211_OFDM_RATE_6MB;
-		rates[index++] = IEEE80211_OFDM_RATE_9MB;
-		rates[index++] = IEEE80211_OFDM_RATE_12MB;
-		rates[index++] = IEEE80211_OFDM_RATE_18MB;
-		rates[index++] = IEEE80211_OFDM_RATE_24MB;
-		rates[index++] = IEEE80211_OFDM_RATE_36MB;
-		rates[index++] = IEEE80211_OFDM_RATE_48MB;
-		rates[index++] = IEEE80211_OFDM_RATE_54MB;
-	} else if (band == IEEE80211_BAND_5GHZ) {
-		rates[index++] =
-			IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_9MB;
-		rates[index++] =
-			IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_18MB;
-		rates[index++] =
-			IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_24MB;
-		rates[index++] =
-			IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_36MB;
-		rates[index++] =
-			IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_48MB;
-		rates[index++] =
-			IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_54MB;
+	if (wl->bss_type == BSS_TYPE_IBSS) {
+		size = sizeof(struct wl12xx_null_data_template);
+		ptr = NULL;
 	} else {
 	} else {
-		wl1271_error("build_basic_rates invalid band: %d", band);
+		skb = ieee80211_nullfunc_get(wl->hw, wl->vif);
+		if (!skb)
+			goto out;
+		size = skb->len;
+		ptr = skb->data;
 	}
 	}
 
 
-	return index;
-}
+	ret = wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, ptr, size);
 
 
-int wl1271_cmd_build_null_data(struct wl1271 *wl)
-{
-	struct wl12xx_null_data_template template;
-
-	if (!is_zero_ether_addr(wl->bssid)) {
-		memcpy(template.header.da, wl->bssid, ETH_ALEN);
-		memcpy(template.header.bssid, wl->bssid, ETH_ALEN);
-	} else {
-		memset(template.header.da, 0xff, ETH_ALEN);
-		memset(template.header.bssid, 0xff, ETH_ALEN);
-	}
-
-	memcpy(template.header.sa, wl->mac_addr, ETH_ALEN);
-	template.header.frame_ctl = cpu_to_le16(IEEE80211_FTYPE_DATA |
-						IEEE80211_STYPE_NULLFUNC |
-						IEEE80211_FCTL_TODS);
+out:
+	dev_kfree_skb(skb);
+	if (ret)
+		wl1271_warning("cmd buld null data failed %d", ret);
 
 
-	return wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, &template,
-				       sizeof(template));
+	return ret;
 
 
 }
 }
 
 
 int wl1271_cmd_build_ps_poll(struct wl1271 *wl, u16 aid)
 int wl1271_cmd_build_ps_poll(struct wl1271 *wl, u16 aid)
 {
 {
-	struct wl12xx_ps_poll_template template;
-
-	memcpy(template.bssid, wl->bssid, ETH_ALEN);
-	memcpy(template.ta, wl->mac_addr, ETH_ALEN);
-
-	/* aid in PS-Poll has its two MSBs each set to 1 */
-	template.aid = cpu_to_le16(1 << 15 | 1 << 14 | aid);
+	struct sk_buff *skb;
+	int ret = 0;
 
 
-	template.fc = cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL);
+	skb = ieee80211_pspoll_get(wl->hw, wl->vif);
+	if (!skb)
+		goto out;
 
 
-	return wl1271_cmd_template_set(wl, CMD_TEMPL_PS_POLL, &template,
-				       sizeof(template));
+	ret = wl1271_cmd_template_set(wl, CMD_TEMPL_PS_POLL, skb->data,
+				      skb->len);
 
 
+out:
+	dev_kfree_skb(skb);
+	return ret;
 }
 }
 
 
-int wl1271_cmd_build_probe_req(struct wl1271 *wl, u8 *ssid, size_t ssid_len,
-			       u8 band)
+int wl1271_cmd_build_probe_req(struct wl1271 *wl,
+			       const u8 *ssid, size_t ssid_len,
+			       const u8 *ie, size_t ie_len, u8 band)
 {
 {
-	struct wl12xx_probe_req_template template;
-	struct wl12xx_ie_rates *rates;
-	char *ptr;
-	u16 size;
+	struct sk_buff *skb;
 	int ret;
 	int ret;
 
 
-	ptr = (char *)&template;
-	size = sizeof(struct ieee80211_header);
-
-	memset(template.header.da, 0xff, ETH_ALEN);
-	memset(template.header.bssid, 0xff, ETH_ALEN);
-	memcpy(template.header.sa, wl->mac_addr, ETH_ALEN);
-	template.header.frame_ctl = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ);
-
-	/* IEs */
-	/* SSID */
-	template.ssid.header.id = WLAN_EID_SSID;
-	template.ssid.header.len = ssid_len;
-	if (ssid_len && ssid)
-		memcpy(template.ssid.ssid, ssid, ssid_len);
-	size += sizeof(struct wl12xx_ie_header) + ssid_len;
-	ptr += size;
-
-	/* Basic Rates */
-	rates = (struct wl12xx_ie_rates *)ptr;
-	rates->header.id = WLAN_EID_SUPP_RATES;
-	rates->header.len = wl1271_build_basic_rates(rates->rates, band);
-	size += sizeof(struct wl12xx_ie_header) + rates->header.len;
-	ptr += sizeof(struct wl12xx_ie_header) + rates->header.len;
-
-	/* Extended rates */
-	rates = (struct wl12xx_ie_rates *)ptr;
-	rates->header.id = WLAN_EID_EXT_SUPP_RATES;
-	rates->header.len = wl1271_build_extended_rates(rates->rates, band);
-	size += sizeof(struct wl12xx_ie_header) + rates->header.len;
-
-	wl1271_dump(DEBUG_SCAN, "PROBE REQ: ", &template, size);
+	skb = ieee80211_probereq_get(wl->hw, wl->vif, ssid, ssid_len,
+				     ie, ie_len);
+	if (!skb) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	wl1271_dump(DEBUG_SCAN, "PROBE REQ: ", skb->data, skb->len);
 
 
 	if (band == IEEE80211_BAND_2GHZ)
 	if (band == IEEE80211_BAND_2GHZ)
 		ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4,
 		ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4,
-					      &template, size);
+					      skb->data, skb->len);
 	else
 	else
 		ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_5,
 		ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_5,
-					      &template, size);
+					      skb->data, skb->len);
+
+out:
+	dev_kfree_skb(skb);
 	return ret;
 	return ret;
 }
 }
 
 
+int wl1271_build_qos_null_data(struct wl1271 *wl)
+{
+	struct ieee80211_qos_hdr template;
+
+	memset(&template, 0, sizeof(template));
+
+	memcpy(template.addr1, wl->bssid, ETH_ALEN);
+	memcpy(template.addr2, wl->mac_addr, ETH_ALEN);
+	memcpy(template.addr3, wl->bssid, ETH_ALEN);
+
+	template.frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA |
+					     IEEE80211_STYPE_QOS_NULLFUNC |
+					     IEEE80211_FCTL_TODS);
+
+	/* FIXME: not sure what priority to use here */
+	template.qos_ctrl = cpu_to_le16(0);
+
+	return wl1271_cmd_template_set(wl, CMD_TEMPL_QOS_NULL_DATA, &template,
+				       sizeof(template));
+}
+
 int wl1271_cmd_set_default_wep_key(struct wl1271 *wl, u8 id)
 int wl1271_cmd_set_default_wep_key(struct wl1271 *wl, u8 id)
 {
 {
 	struct wl1271_cmd_set_keys *cmd;
 	struct wl1271_cmd_set_keys *cmd;

+ 7 - 5
drivers/net/wireless/wl12xx/wl1271_cmd.h

@@ -41,15 +41,17 @@ int wl1271_cmd_data_path(struct wl1271 *wl, bool enable);
 int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode, bool send);
 int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode, bool send);
 int wl1271_cmd_read_memory(struct wl1271 *wl, u32 addr, void *answer,
 int wl1271_cmd_read_memory(struct wl1271 *wl, u32 addr, void *answer,
 			   size_t len);
 			   size_t len);
-int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len,
-		    u8 active_scan, u8 high_prio, u8 band,
-		    u8 probe_requests);
+int wl1271_cmd_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len,
+		    const u8 *ie, size_t ie_len, u8 active_scan,
+		    u8 high_prio, u8 band, u8 probe_requests);
 int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id,
 int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id,
 			    void *buf, size_t buf_len);
 			    void *buf, size_t buf_len);
 int wl1271_cmd_build_null_data(struct wl1271 *wl);
 int wl1271_cmd_build_null_data(struct wl1271 *wl);
 int wl1271_cmd_build_ps_poll(struct wl1271 *wl, u16 aid);
 int wl1271_cmd_build_ps_poll(struct wl1271 *wl, u16 aid);
-int wl1271_cmd_build_probe_req(struct wl1271 *wl, u8 *ssid, size_t ssid_len,
-			       u8 band);
+int wl1271_cmd_build_probe_req(struct wl1271 *wl,
+			       const u8 *ssid, size_t ssid_len,
+			       const u8 *ie, size_t ie_len, u8 band);
+int wl1271_build_qos_null_data(struct wl1271 *wl);
 int wl1271_cmd_set_default_wep_key(struct wl1271 *wl, u8 id);
 int wl1271_cmd_set_default_wep_key(struct wl1271 *wl, u8 id);
 int wl1271_cmd_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
 int wl1271_cmd_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
 		       u8 key_size, const u8 *key, const u8 *addr,
 		       u8 key_size, const u8 *key, const u8 *addr,

+ 269 - 61
drivers/net/wireless/wl12xx/wl1271_conf.h

@@ -65,110 +65,318 @@ enum {
 	CONF_HW_RATE_INDEX_MAX     = CONF_HW_RATE_INDEX_54MBPS,
 	CONF_HW_RATE_INDEX_MAX     = CONF_HW_RATE_INDEX_54MBPS,
 };
 };
 
 
-struct conf_sg_settings {
+enum {
+	CONF_SG_DISABLE = 0,
+	CONF_SG_PROTECTIVE,
+	CONF_SG_OPPORTUNISTIC
+};
+
+enum {
 	/*
 	/*
-	 * Defines the PER threshold in PPM of the BT voice of which reaching
-	 * this value will trigger raising the priority of the BT voice by
-	 * the BT IP until next NFS sample interval time as defined in
-	 * nfs_sample_interval.
+	 * PER threshold in PPM of the BT voice
 	 *
 	 *
-	 * Unit: PER value in PPM (parts per million)
-	 * #Error_packets / #Total_packets
+	 * Range: 0 - 10000000
+	 */
+	CONF_SG_BT_PER_THRESHOLD = 0,
 
 
-	 * Range: u32
+	/*
+	 * Number of consequent RX_ACTIVE activities to override BT voice
+	 * frames to ensure WLAN connection
+	 *
+	 * Range: 0 - 100
+	 */
+	CONF_SG_HV3_MAX_OVERRIDE,
+
+	/*
+	 * Defines the PER threshold of the BT voice
+	 *
+	 * Range: 0 - 65000
+	 */
+	CONF_SG_BT_NFS_SAMPLE_INTERVAL,
+
+	/*
+	 * Defines the load ratio of BT
+	 *
+	 * Range: 0 - 100 (%)
+	 */
+	CONF_SG_BT_LOAD_RATIO,
+
+	/*
+	 * Defines whether the SG will force WLAN host to enter/exit PSM
+	 *
+	 * Range: 1 - SG can force, 0 - host handles PSM
+	 */
+	CONF_SG_AUTO_PS_MODE,
+
+	/*
+	 * Compensation percentage of probe requests when scan initiated
+	 * during BT voice/ACL link.
+	 *
+	 * Range: 0 - 255 (%)
+	 */
+	CONF_SG_AUTO_SCAN_PROBE_REQ,
+
+	/*
+	 * Compensation percentage of probe requests when active scan initiated
+	 * during BT voice
+	 *
+	 * Range: 0 - 255 (%)
+	 */
+	CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3,
+
+	/*
+	 * Defines antenna configuration (single/dual antenna)
+	 *
+	 * Range: 0 - single antenna, 1 - dual antenna
+	 */
+	CONF_SG_ANTENNA_CONFIGURATION,
+
+	/*
+	 * The threshold (percent) of max consequtive beacon misses before
+	 * increasing priority of beacon reception.
+	 *
+	 * Range: 0 - 100 (%)
+	 */
+	CONF_SG_BEACON_MISS_PERCENT,
+
+	/*
+	 * The rate threshold below which receiving a data frame from the AP
+	 * will increase the priority of the data frame above BT traffic.
+	 *
+	 * Range: 0,2, 5(=5.5), 6, 9, 11, 12, 18, 24, 36, 48, 54
+	 */
+	CONF_SG_RATE_ADAPT_THRESH,
+
+	/*
+	 * Not used currently.
+	 *
+	 * Range: 0
+	 */
+	CONF_SG_RATE_ADAPT_SNR,
+
+	/*
+	 * Configure the min and max time BT gains the antenna
+	 * in WLAN PSM / BT master basic rate
+	 *
+	 * Range: 0 - 255 (ms)
+	 */
+	CONF_SG_WLAN_PS_BT_ACL_MASTER_MIN_BR,
+	CONF_SG_WLAN_PS_BT_ACL_MASTER_MAX_BR,
+
+	/*
+	 * The time after it expires no new WLAN trigger frame is trasmitted
+	 * in WLAN PSM / BT master basic rate
+	 *
+	 * Range: 0 - 255 (ms)
+	 */
+	CONF_SG_WLAN_PS_MAX_BT_ACL_MASTER_BR,
+
+	/*
+	 * Configure the min and max time BT gains the antenna
+	 * in WLAN PSM / BT slave basic rate
+	 *
+	 * Range: 0 - 255 (ms)
 	 */
 	 */
-	u32 per_threshold;
+	CONF_SG_WLAN_PS_BT_ACL_SLAVE_MIN_BR,
+	CONF_SG_WLAN_PS_BT_ACL_SLAVE_MAX_BR,
 
 
 	/*
 	/*
-	 * This value is an absolute time in micro-seconds to limit the
-	 * maximum scan duration compensation while in SG
+	 * The time after it expires no new WLAN trigger frame is trasmitted
+	 * in WLAN PSM / BT slave basic rate
+	 *
+	 * Range: 0 - 255 (ms)
 	 */
 	 */
-	u32 max_scan_compensation_time;
+	CONF_SG_WLAN_PS_MAX_BT_ACL_SLAVE_BR,
 
 
-	/* Defines the PER threshold of the BT voice of which reaching this
-	 * value will trigger raising the priority of the BT voice until next
-	 * NFS sample interval time as defined in sample_interval.
+	/*
+	 * Configure the min and max time BT gains the antenna
+	 * in WLAN PSM / BT master EDR
 	 *
 	 *
-	 * Unit: msec
-	 * Range: 1-65000
+	 * Range: 0 - 255 (ms)
 	 */
 	 */
-	u16 nfs_sample_interval;
+	CONF_SG_WLAN_PS_BT_ACL_MASTER_MIN_EDR,
+	CONF_SG_WLAN_PS_BT_ACL_MASTER_MAX_EDR,
 
 
 	/*
 	/*
-	 * Defines the load ratio for the BT.
-	 * The WLAN ratio is: 100 - load_ratio
+	 * The time after it expires no new WLAN trigger frame is trasmitted
+	 * in WLAN PSM / BT master EDR
 	 *
 	 *
-	 * Unit: Percent
-	 * Range: 0-100
+	 * Range: 0 - 255 (ms)
 	 */
 	 */
-	u8 load_ratio;
+	CONF_SG_WLAN_PS_MAX_BT_ACL_MASTER_EDR,
 
 
 	/*
 	/*
-	 * true - Co-ex is allowed to enter/exit P.S automatically and
-	 *        transparently to the host
+	 * Configure the min and max time BT gains the antenna
+	 * in WLAN PSM / BT slave EDR
 	 *
 	 *
-	 * false - Co-ex is disallowed to enter/exit P.S and will trigger an
-	 *         event to the host to notify for the need to enter/exit P.S
-	 *         due to BT change state
+	 * Range: 0 - 255 (ms)
+	 */
+	CONF_SG_WLAN_PS_BT_ACL_SLAVE_MIN_EDR,
+	CONF_SG_WLAN_PS_BT_ACL_SLAVE_MAX_EDR,
+
+	/*
+	 * The time after it expires no new WLAN trigger frame is trasmitted
+	 * in WLAN PSM / BT slave EDR
 	 *
 	 *
+	 * Range: 0 - 255 (ms)
 	 */
 	 */
-	u8 auto_ps_mode;
+	CONF_SG_WLAN_PS_MAX_BT_ACL_SLAVE_EDR,
 
 
 	/*
 	/*
-	 * This parameter defines the compensation percentage of num of probe
-	 * requests in case scan is initiated during BT voice/BT ACL
-	 * guaranteed link.
+	 * RX guard time before the beginning of a new BT voice frame during
+	 * which no new WLAN trigger frame is transmitted.
 	 *
 	 *
-	 * Unit: Percent
-	 * Range: 0-255 (0 - No compensation)
+	 * Range: 0 - 100000 (us)
 	 */
 	 */
-	u8 probe_req_compensation;
+	CONF_SG_RXT,
 
 
 	/*
 	/*
-	 * This parameter defines the compensation percentage of scan window
-	 * size in case scan is initiated during BT voice/BT ACL Guaranteed
-	 * link.
+	 * TX guard time before the beginning of a new BT voice frame during
+	 * which no new WLAN frame is transmitted.
 	 *
 	 *
-	 * Unit: Percent
-	 * Range: 0-255 (0 - No compensation)
+	 * Range: 0 - 100000 (us)
 	 */
 	 */
-	u8 scan_window_compensation;
+
+	CONF_SG_TXT,
 
 
 	/*
 	/*
-	 * Defines the antenna configuration.
+	 * Enable adaptive RXT/TXT algorithm. If disabled, the host values
+	 * will be utilized.
 	 *
 	 *
-	 * Range: 0 - Single Antenna; 1 - Dual Antenna
+	 * Range: 0 - disable, 1 - enable
 	 */
 	 */
-	u8 antenna_config;
+	CONF_SG_ADAPTIVE_RXT_TXT,
 
 
 	/*
 	/*
-	 * The percent out of the Max consecutive beacon miss roaming trigger
-	 * which is the threshold for raising the priority of beacon
-	 * reception.
+	 * The used WLAN legacy service period during active BT ACL link
 	 *
 	 *
-	 * Range: 1-100
-	 * N = MaxConsecutiveBeaconMiss
-	 * P = coexMaxConsecutiveBeaconMissPrecent
-	 * Threshold = MIN( N-1, round(N * P / 100))
+	 * Range: 0 - 255 (ms)
 	 */
 	 */
-	u8 beacon_miss_threshold;
+	CONF_SG_PS_POLL_TIMEOUT,
 
 
 	/*
 	/*
-	 * The RX rate threshold below which rate adaptation is assumed to be
-	 * occurring at the AP which will raise priority for ACTIVE_RX and RX
-	 * SP.
+	 * The used WLAN UPSD service period during active BT ACL link
 	 *
 	 *
-	 * Range: HW_BIT_RATE_*
+	 * Range: 0 - 255 (ms)
 	 */
 	 */
-	u32 rate_adaptation_threshold;
+	CONF_SG_UPSD_TIMEOUT,
 
 
 	/*
 	/*
-	 * The SNR above which the RX rate threshold indicating AP rate
-	 * adaptation is valid
+	 * Configure the min and max time BT gains the antenna
+	 * in WLAN Active / BT master EDR
 	 *
 	 *
-	 * Range: -128 - 127
+	 * Range: 0 - 255 (ms)
 	 */
 	 */
-	s8 rate_adaptation_snr;
+	CONF_SG_WLAN_ACTIVE_BT_ACL_MASTER_MIN_EDR,
+	CONF_SG_WLAN_ACTIVE_BT_ACL_MASTER_MAX_EDR,
+
+	/*
+	 * The maximum time WLAN can gain the antenna for
+	 * in WLAN Active / BT master EDR
+	 *
+	 * Range: 0 - 255 (ms)
+	 */
+	CONF_SG_WLAN_ACTIVE_MAX_BT_ACL_MASTER_EDR,
+
+	/*
+	 * Configure the min and max time BT gains the antenna
+	 * in WLAN Active / BT slave EDR
+	 *
+	 * Range: 0 - 255 (ms)
+	 */
+	CONF_SG_WLAN_ACTIVE_BT_ACL_SLAVE_MIN_EDR,
+	CONF_SG_WLAN_ACTIVE_BT_ACL_SLAVE_MAX_EDR,
+
+	/*
+	 * The maximum time WLAN can gain the antenna for
+	 * in WLAN Active / BT slave EDR
+	 *
+	 * Range: 0 - 255 (ms)
+	 */
+	CONF_SG_WLAN_ACTIVE_MAX_BT_ACL_SLAVE_EDR,
+
+	/*
+	 * Configure the min and max time BT gains the antenna
+	 * in WLAN Active / BT basic rate
+	 *
+	 * Range: 0 - 255 (ms)
+	 */
+	CONF_SG_WLAN_ACTIVE_BT_ACL_MIN_BR,
+	CONF_SG_WLAN_ACTIVE_BT_ACL_MAX_BR,
+
+	/*
+	 * The maximum time WLAN can gain the antenna for
+	 * in WLAN Active / BT basic rate
+	 *
+	 * Range: 0 - 255 (ms)
+	 */
+	CONF_SG_WLAN_ACTIVE_MAX_BT_ACL_BR,
+
+	/*
+	 * Compensation percentage of WLAN passive scan window if initiated
+	 * during BT voice
+	 *
+	 * Range: 0 - 1000 (%)
+	 */
+	CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_HV3,
+
+	/*
+	 * Compensation percentage of WLAN passive scan window if initiated
+	 * during BT A2DP
+	 *
+	 * Range: 0 - 1000 (%)
+	 */
+	CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP,
+
+	/*
+	 * Fixed time ensured for BT traffic to gain the antenna during WLAN
+	 * passive scan.
+	 *
+	 * Range: 0 - 1000 ms
+	 */
+	CONF_SG_PASSIVE_SCAN_A2DP_BT_TIME,
+
+	/*
+	 * Fixed time ensured for WLAN traffic to gain the antenna during WLAN
+	 * passive scan.
+	 *
+	 * Range: 0 - 1000 ms
+	 */
+	CONF_SG_PASSIVE_SCAN_A2DP_WLAN_TIME,
+
+	/*
+	 * Number of consequent BT voice frames not interrupted by WLAN
+	 *
+	 * Range: 0 - 100
+	 */
+	CONF_SG_HV3_MAX_SERVED,
+
+	/*
+	 * Protection time of the DHCP procedure.
+	 *
+	 * Range: 0 - 100000 (ms)
+	 */
+	CONF_SG_DHCP_TIME,
+
+	/*
+	 * Compensation percentage of WLAN active scan window if initiated
+	 * during BT A2DP
+	 *
+	 * Range: 0 - 1000 (%)
+	 */
+	CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_A2DP,
+	CONF_SG_TEMP_PARAM_1,
+	CONF_SG_TEMP_PARAM_2,
+	CONF_SG_TEMP_PARAM_3,
+	CONF_SG_TEMP_PARAM_4,
+	CONF_SG_TEMP_PARAM_5,
+	CONF_SG_PARAMS_MAX,
+	CONF_SG_PARAMS_ALL = 0xff
+};
+
+struct conf_sg_settings {
+	__le32 params[CONF_SG_PARAMS_MAX];
+	u8 state;
 };
 };
 
 
 enum conf_rx_queue_type {
 enum conf_rx_queue_type {

+ 5 - 7
drivers/net/wireless/wl12xx/wl1271_debugfs.c

@@ -28,6 +28,7 @@
 #include "wl1271.h"
 #include "wl1271.h"
 #include "wl1271_acx.h"
 #include "wl1271_acx.h"
 #include "wl1271_ps.h"
 #include "wl1271_ps.h"
+#include "wl1271_io.h"
 
 
 /* ms */
 /* ms */
 #define WL1271_DEBUGFS_STATS_LIFETIME 1000
 #define WL1271_DEBUGFS_STATS_LIFETIME 1000
@@ -276,13 +277,10 @@ static ssize_t gpio_power_write(struct file *file,
 		goto out;
 		goto out;
 	}
 	}
 
 
-	if (value) {
-		wl->set_power(true);
-		set_bit(WL1271_FLAG_GPIO_POWER, &wl->flags);
-	} else {
-		wl->set_power(false);
-		clear_bit(WL1271_FLAG_GPIO_POWER, &wl->flags);
-	}
+	if (value)
+		wl1271_power_on(wl);
+	else
+		wl1271_power_off(wl);
 
 
 out:
 out:
 	mutex_unlock(&wl->mutex);
 	mutex_unlock(&wl->mutex);

+ 2 - 0
drivers/net/wireless/wl12xx/wl1271_event.c

@@ -44,7 +44,9 @@ static int wl1271_event_scan_complete(struct wl1271 *wl,
 			 * scanning as it checks that.
 			 * scanning as it checks that.
 			 */
 			 */
 			clear_bit(WL1271_FLAG_SCANNING, &wl->flags);
 			clear_bit(WL1271_FLAG_SCANNING, &wl->flags);
+			/* FIXME: ie missing! */
 			wl1271_cmd_scan(wl, wl->scan.ssid, wl->scan.ssid_len,
 			wl1271_cmd_scan(wl, wl->scan.ssid, wl->scan.ssid_len,
+						NULL, 0,
 						wl->scan.active,
 						wl->scan.active,
 						wl->scan.high_prio,
 						wl->scan.high_prio,
 						WL1271_SCAN_BAND_5_GHZ,
 						WL1271_SCAN_BAND_5_GHZ,

+ 2 - 2
drivers/net/wireless/wl12xx/wl1271_init.c

@@ -160,11 +160,11 @@ int wl1271_init_pta(struct wl1271 *wl)
 {
 {
 	int ret;
 	int ret;
 
 
-	ret = wl1271_acx_sg_enable(wl);
+	ret = wl1271_acx_sg_cfg(wl);
 	if (ret < 0)
 	if (ret < 0)
 		return ret;
 		return ret;
 
 
-	ret = wl1271_acx_sg_cfg(wl);
+	ret = wl1271_acx_sg_enable(wl, wl->sg_enabled);
 	if (ret < 0)
 	if (ret < 0)
 		return ret;
 		return ret;
 
 

+ 13 - 0
drivers/net/wireless/wl12xx/wl1271_io.h

@@ -138,6 +138,18 @@ static inline void wl1271_write32(struct wl1271 *wl, int addr, u32 val)
 	wl1271_raw_write32(wl, wl1271_translate_addr(wl, addr), val);
 	wl1271_raw_write32(wl, wl1271_translate_addr(wl, addr), val);
 }
 }
 
 
+static inline void wl1271_power_off(struct wl1271 *wl)
+{
+	wl->if_ops->power(wl, false);
+	clear_bit(WL1271_FLAG_GPIO_POWER, &wl->flags);
+}
+
+static inline void wl1271_power_on(struct wl1271 *wl)
+{
+	wl->if_ops->power(wl, true);
+	set_bit(WL1271_FLAG_GPIO_POWER, &wl->flags);
+}
+
 
 
 /* Top Register IO */
 /* Top Register IO */
 void wl1271_top_reg_write(struct wl1271 *wl, int addr, u16 val);
 void wl1271_top_reg_write(struct wl1271 *wl, int addr, u16 val);
@@ -149,6 +161,7 @@ int wl1271_set_partition(struct wl1271 *wl,
 /* Functions from wl1271_main.c */
 /* Functions from wl1271_main.c */
 
 
 int wl1271_register_hw(struct wl1271 *wl);
 int wl1271_register_hw(struct wl1271 *wl);
+void wl1271_unregister_hw(struct wl1271 *wl);
 int wl1271_init_ieee80211(struct wl1271 *wl);
 int wl1271_init_ieee80211(struct wl1271 *wl);
 struct ieee80211_hw *wl1271_alloc_hw(void);
 struct ieee80211_hw *wl1271_alloc_hw(void);
 int wl1271_free_hw(struct wl1271 *wl);
 int wl1271_free_hw(struct wl1271 *wl);

+ 303 - 193
drivers/net/wireless/wl12xx/wl1271_main.c

@@ -29,6 +29,7 @@
 #include <linux/etherdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/vmalloc.h>
 #include <linux/vmalloc.h>
 #include <linux/inetdevice.h>
 #include <linux/inetdevice.h>
+#include <linux/platform_device.h>
 
 
 #include "wl1271.h"
 #include "wl1271.h"
 #include "wl12xx_80211.h"
 #include "wl12xx_80211.h"
@@ -48,17 +49,57 @@
 
 
 static struct conf_drv_settings default_conf = {
 static struct conf_drv_settings default_conf = {
 	.sg = {
 	.sg = {
-		.per_threshold               = 7500,
-		.max_scan_compensation_time  = 120000,
-		.nfs_sample_interval         = 400,
-		.load_ratio                  = 50,
-		.auto_ps_mode                = 0,
-		.probe_req_compensation      = 170,
-		.scan_window_compensation    = 50,
-		.antenna_config              = 0,
-		.beacon_miss_threshold       = 60,
-		.rate_adaptation_threshold   = CONF_HW_BIT_RATE_12MBPS,
-		.rate_adaptation_snr         = 0
+		.params = {
+			[CONF_SG_BT_PER_THRESHOLD]                  = 7500,
+			[CONF_SG_HV3_MAX_OVERRIDE]                  = 0,
+			[CONF_SG_BT_NFS_SAMPLE_INTERVAL]            = 400,
+			[CONF_SG_BT_LOAD_RATIO]                     = 50,
+			[CONF_SG_AUTO_PS_MODE]                      = 0,
+			[CONF_SG_AUTO_SCAN_PROBE_REQ]               = 170,
+			[CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3]   = 50,
+			[CONF_SG_ANTENNA_CONFIGURATION]             = 0,
+			[CONF_SG_BEACON_MISS_PERCENT]               = 60,
+			[CONF_SG_RATE_ADAPT_THRESH]                 = 12,
+			[CONF_SG_RATE_ADAPT_SNR]                    = 0,
+			[CONF_SG_WLAN_PS_BT_ACL_MASTER_MIN_BR]      = 10,
+			[CONF_SG_WLAN_PS_BT_ACL_MASTER_MAX_BR]      = 30,
+			[CONF_SG_WLAN_PS_MAX_BT_ACL_MASTER_BR]      = 8,
+			[CONF_SG_WLAN_PS_BT_ACL_SLAVE_MIN_BR]       = 20,
+			[CONF_SG_WLAN_PS_BT_ACL_SLAVE_MAX_BR]       = 50,
+			/* Note: with UPSD, this should be 4 */
+			[CONF_SG_WLAN_PS_MAX_BT_ACL_SLAVE_BR]       = 8,
+			[CONF_SG_WLAN_PS_BT_ACL_MASTER_MIN_EDR]     = 7,
+			[CONF_SG_WLAN_PS_BT_ACL_MASTER_MAX_EDR]     = 25,
+			[CONF_SG_WLAN_PS_MAX_BT_ACL_MASTER_EDR]     = 20,
+			/* Note: with UPDS, this should be 15 */
+			[CONF_SG_WLAN_PS_BT_ACL_SLAVE_MIN_EDR]      = 8,
+			/* Note: with UPDS, this should be 50 */
+			[CONF_SG_WLAN_PS_BT_ACL_SLAVE_MAX_EDR]      = 40,
+			/* Note: with UPDS, this should be 10 */
+			[CONF_SG_WLAN_PS_MAX_BT_ACL_SLAVE_EDR]      = 20,
+			[CONF_SG_RXT]                               = 1200,
+			[CONF_SG_TXT]                               = 1000,
+			[CONF_SG_ADAPTIVE_RXT_TXT]                  = 1,
+			[CONF_SG_PS_POLL_TIMEOUT]                   = 10,
+			[CONF_SG_UPSD_TIMEOUT]                      = 10,
+			[CONF_SG_WLAN_ACTIVE_BT_ACL_MASTER_MIN_EDR] = 7,
+			[CONF_SG_WLAN_ACTIVE_BT_ACL_MASTER_MAX_EDR] = 15,
+			[CONF_SG_WLAN_ACTIVE_MAX_BT_ACL_MASTER_EDR] = 15,
+			[CONF_SG_WLAN_ACTIVE_BT_ACL_SLAVE_MIN_EDR]  = 8,
+			[CONF_SG_WLAN_ACTIVE_BT_ACL_SLAVE_MAX_EDR]  = 20,
+			[CONF_SG_WLAN_ACTIVE_MAX_BT_ACL_SLAVE_EDR]  = 15,
+			[CONF_SG_WLAN_ACTIVE_BT_ACL_MIN_BR]         = 20,
+			[CONF_SG_WLAN_ACTIVE_BT_ACL_MAX_BR]         = 50,
+			[CONF_SG_WLAN_ACTIVE_MAX_BT_ACL_BR]         = 10,
+			[CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_HV3]  = 200,
+			[CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP] = 800,
+			[CONF_SG_PASSIVE_SCAN_A2DP_BT_TIME]         = 75,
+			[CONF_SG_PASSIVE_SCAN_A2DP_WLAN_TIME]       = 15,
+			[CONF_SG_HV3_MAX_SERVED]                    = 6,
+			[CONF_SG_DHCP_TIME]                         = 5000,
+			[CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_A2DP]  = 100,
+		},
+		.state = CONF_SG_PROTECTIVE,
 	},
 	},
 	.rx = {
 	.rx = {
 		.rx_msdu_life_time           = 512000,
 		.rx_msdu_life_time           = 512000,
@@ -240,6 +281,21 @@ static struct conf_drv_settings default_conf = {
 	}
 	}
 };
 };
 
 
+static void wl1271_device_release(struct device *dev)
+{
+
+}
+
+static struct platform_device wl1271_device = {
+	.name           = "wl1271",
+	.id             = -1,
+
+	/* device model insists to have a release function */
+	.dev            = {
+		.release = wl1271_device_release,
+	},
+};
+
 static LIST_HEAD(wl_list);
 static LIST_HEAD(wl_list);
 
 
 static void wl1271_conf_init(struct wl1271 *wl)
 static void wl1271_conf_init(struct wl1271 *wl)
@@ -359,18 +415,6 @@ static int wl1271_plt_init(struct wl1271 *wl)
 	return ret;
 	return ret;
 }
 }
 
 
-static void wl1271_power_off(struct wl1271 *wl)
-{
-	wl->set_power(false);
-	clear_bit(WL1271_FLAG_GPIO_POWER, &wl->flags);
-}
-
-static void wl1271_power_on(struct wl1271 *wl)
-{
-	wl->set_power(true);
-	set_bit(WL1271_FLAG_GPIO_POWER, &wl->flags);
-}
-
 static void wl1271_fw_status(struct wl1271 *wl,
 static void wl1271_fw_status(struct wl1271 *wl,
 			     struct wl1271_fw_status *status)
 			     struct wl1271_fw_status *status)
 {
 {
@@ -526,40 +570,6 @@ out:
 	return ret;
 	return ret;
 }
 }
 
 
-static int wl1271_update_mac_addr(struct wl1271 *wl)
-{
-	int ret = 0;
-	u8 *nvs_ptr = (u8 *)wl->nvs->nvs;
-
-	/* get mac address from the NVS */
-	wl->mac_addr[0] = nvs_ptr[11];
-	wl->mac_addr[1] = nvs_ptr[10];
-	wl->mac_addr[2] = nvs_ptr[6];
-	wl->mac_addr[3] = nvs_ptr[5];
-	wl->mac_addr[4] = nvs_ptr[4];
-	wl->mac_addr[5] = nvs_ptr[3];
-
-	/* FIXME: if it is a zero-address, we should bail out. Now, instead,
-	   we randomize an address */
-	if (is_zero_ether_addr(wl->mac_addr)) {
-		static const u8 nokia_oui[3] = {0x00, 0x1f, 0xdf};
-		memcpy(wl->mac_addr, nokia_oui, 3);
-		get_random_bytes(wl->mac_addr + 3, 3);
-
-		/* update this address to the NVS */
-		nvs_ptr[11] = wl->mac_addr[0];
-		nvs_ptr[10] = wl->mac_addr[1];
-		nvs_ptr[6] = wl->mac_addr[2];
-		nvs_ptr[5] = wl->mac_addr[3];
-		nvs_ptr[4] = wl->mac_addr[4];
-		nvs_ptr[3] = wl->mac_addr[5];
-	}
-
-	SET_IEEE80211_PERM_ADDR(wl->hw, wl->mac_addr);
-
-	return ret;
-}
-
 static int wl1271_fetch_nvs(struct wl1271 *wl)
 static int wl1271_fetch_nvs(struct wl1271 *wl)
 {
 {
 	const struct firmware *fw;
 	const struct firmware *fw;
@@ -589,8 +599,6 @@ static int wl1271_fetch_nvs(struct wl1271 *wl)
 
 
 	memcpy(wl->nvs, fw->data, sizeof(struct wl1271_nvs_file));
 	memcpy(wl->nvs, fw->data, sizeof(struct wl1271_nvs_file));
 
 
-	ret = wl1271_update_mac_addr(wl);
-
 out:
 out:
 	release_firmware(fw);
 	release_firmware(fw);
 
 
@@ -907,14 +915,59 @@ static struct notifier_block wl1271_dev_notifier = {
 
 
 
 
 static int wl1271_op_start(struct ieee80211_hw *hw)
 static int wl1271_op_start(struct ieee80211_hw *hw)
+{
+	wl1271_debug(DEBUG_MAC80211, "mac80211 start");
+
+	/*
+	 * We have to delay the booting of the hardware because
+	 * we need to know the local MAC address before downloading and
+	 * initializing the firmware. The MAC address cannot be changed
+	 * after boot, and without the proper MAC address, the firmware
+	 * will not function properly.
+	 *
+	 * The MAC address is first known when the corresponding interface
+	 * is added. That is where we will initialize the hardware.
+	 */
+
+	return 0;
+}
+
+static void wl1271_op_stop(struct ieee80211_hw *hw)
+{
+	wl1271_debug(DEBUG_MAC80211, "mac80211 stop");
+}
+
+static int wl1271_op_add_interface(struct ieee80211_hw *hw,
+				   struct ieee80211_vif *vif)
 {
 {
 	struct wl1271 *wl = hw->priv;
 	struct wl1271 *wl = hw->priv;
 	int retries = WL1271_BOOT_RETRIES;
 	int retries = WL1271_BOOT_RETRIES;
 	int ret = 0;
 	int ret = 0;
 
 
-	wl1271_debug(DEBUG_MAC80211, "mac80211 start");
+	wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM",
+		     vif->type, vif->addr);
 
 
 	mutex_lock(&wl->mutex);
 	mutex_lock(&wl->mutex);
+	if (wl->vif) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	wl->vif = vif;
+
+	switch (vif->type) {
+	case NL80211_IFTYPE_STATION:
+		wl->bss_type = BSS_TYPE_STA_BSS;
+		break;
+	case NL80211_IFTYPE_ADHOC:
+		wl->bss_type = BSS_TYPE_IBSS;
+		break;
+	default:
+		ret = -EOPNOTSUPP;
+		goto out;
+	}
+
+	memcpy(wl->mac_addr, vif->addr, ETH_ALEN);
 
 
 	if (wl->state != WL1271_STATE_OFF) {
 	if (wl->state != WL1271_STATE_OFF) {
 		wl1271_error("cannot start because not in off state: %d",
 		wl1271_error("cannot start because not in off state: %d",
@@ -970,19 +1023,20 @@ out:
 	return ret;
 	return ret;
 }
 }
 
 
-static void wl1271_op_stop(struct ieee80211_hw *hw)
+static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
+				       struct ieee80211_vif *vif)
 {
 {
 	struct wl1271 *wl = hw->priv;
 	struct wl1271 *wl = hw->priv;
 	int i;
 	int i;
 
 
-	wl1271_info("down");
-
-	wl1271_debug(DEBUG_MAC80211, "mac80211 stop");
-
 	unregister_inetaddr_notifier(&wl1271_dev_notifier);
 	unregister_inetaddr_notifier(&wl1271_dev_notifier);
-	list_del(&wl->list);
 
 
 	mutex_lock(&wl->mutex);
 	mutex_lock(&wl->mutex);
+	wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface");
+
+	wl1271_info("down");
+
+	list_del(&wl->list);
 
 
 	WARN_ON(wl->state != WL1271_STATE_ON);
 	WARN_ON(wl->state != WL1271_STATE_ON);
 
 
@@ -1026,6 +1080,8 @@ static void wl1271_op_stop(struct ieee80211_hw *hw)
 	wl->rate_set = CONF_TX_RATE_MASK_BASIC;
 	wl->rate_set = CONF_TX_RATE_MASK_BASIC;
 	wl->sta_rate_set = 0;
 	wl->sta_rate_set = 0;
 	wl->flags = 0;
 	wl->flags = 0;
+	wl->vif = NULL;
+	wl->filters = 0;
 
 
 	for (i = 0; i < NUM_TX_QUEUES; i++)
 	for (i = 0; i < NUM_TX_QUEUES; i++)
 		wl->tx_blocks_freed[i] = 0;
 		wl->tx_blocks_freed[i] = 0;
@@ -1034,119 +1090,39 @@ static void wl1271_op_stop(struct ieee80211_hw *hw)
 	mutex_unlock(&wl->mutex);
 	mutex_unlock(&wl->mutex);
 }
 }
 
 
-static int wl1271_op_add_interface(struct ieee80211_hw *hw,
-				   struct ieee80211_vif *vif)
+static void wl1271_configure_filters(struct wl1271 *wl, unsigned int filters)
 {
 {
-	struct wl1271 *wl = hw->priv;
-	int ret = 0;
+	wl->rx_config = WL1271_DEFAULT_RX_CONFIG;
+	wl->rx_filter = WL1271_DEFAULT_RX_FILTER;
 
 
-	wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM",
-		     vif->type, vif->addr);
+	/* combine requested filters with current filter config */
+	filters = wl->filters | filters;
 
 
-	mutex_lock(&wl->mutex);
-	if (wl->vif) {
-		ret = -EBUSY;
-		goto out;
-	}
+	wl1271_debug(DEBUG_FILTERS, "RX filters set: ");
 
 
-	wl->vif = vif;
-
-	switch (vif->type) {
-	case NL80211_IFTYPE_STATION:
-		wl->bss_type = BSS_TYPE_STA_BSS;
-		break;
-	case NL80211_IFTYPE_ADHOC:
-		wl->bss_type = BSS_TYPE_IBSS;
-		break;
-	default:
-		ret = -EOPNOTSUPP;
-		goto out;
+	if (filters & FIF_PROMISC_IN_BSS) {
+		wl1271_debug(DEBUG_FILTERS, " - FIF_PROMISC_IN_BSS");
+		wl->rx_config &= ~CFG_UNI_FILTER_EN;
+		wl->rx_config |= CFG_BSSID_FILTER_EN;
 	}
 	}
-
-	/* FIXME: what if conf->mac_addr changes? */
-
-out:
-	mutex_unlock(&wl->mutex);
-	return ret;
-}
-
-static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
-					 struct ieee80211_vif *vif)
-{
-	struct wl1271 *wl = hw->priv;
-
-	mutex_lock(&wl->mutex);
-	wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface");
-	wl->vif = NULL;
-	mutex_unlock(&wl->mutex);
-}
-
-#if 0
-static int wl1271_op_config_interface(struct ieee80211_hw *hw,
-				      struct ieee80211_vif *vif,
-				      struct ieee80211_if_conf *conf)
-{
-	struct wl1271 *wl = hw->priv;
-	struct sk_buff *beacon;
-	int ret;
-
-	wl1271_debug(DEBUG_MAC80211, "mac80211 config_interface bssid %pM",
-		     conf->bssid);
-	wl1271_dump_ascii(DEBUG_MAC80211, "ssid: ", conf->ssid,
-			  conf->ssid_len);
-
-	mutex_lock(&wl->mutex);
-
-	ret = wl1271_ps_elp_wakeup(wl, false);
-	if (ret < 0)
-		goto out;
-
-	if (memcmp(wl->bssid, conf->bssid, ETH_ALEN)) {
-		wl1271_debug(DEBUG_MAC80211, "bssid changed");
-
-		memcpy(wl->bssid, conf->bssid, ETH_ALEN);
-
-		ret = wl1271_cmd_join(wl, wl->bss_type);
-		if (ret < 0)
-			goto out_sleep;
-
-		ret = wl1271_cmd_build_null_data(wl);
-		if (ret < 0)
-			goto out_sleep;
+	if (filters & FIF_BCN_PRBRESP_PROMISC) {
+		wl1271_debug(DEBUG_FILTERS, " - FIF_BCN_PRBRESP_PROMISC");
+		wl->rx_config &= ~CFG_BSSID_FILTER_EN;
+		wl->rx_config &= ~CFG_SSID_FILTER_EN;
 	}
 	}
-
-	wl->ssid_len = conf->ssid_len;
-	if (wl->ssid_len)
-		memcpy(wl->ssid, conf->ssid, wl->ssid_len);
-
-	if (conf->changed & IEEE80211_IFCC_BEACON) {
-		beacon = ieee80211_beacon_get(hw, vif);
-		ret = wl1271_cmd_template_set(wl, CMD_TEMPL_BEACON,
-					      beacon->data, beacon->len);
-
-		if (ret < 0) {
-			dev_kfree_skb(beacon);
-			goto out_sleep;
-		}
-
-		ret = wl1271_cmd_template_set(wl, CMD_TEMPL_PROBE_RESPONSE,
-					      beacon->data, beacon->len);
-
-		dev_kfree_skb(beacon);
-
-		if (ret < 0)
-			goto out_sleep;
+	if (filters & FIF_OTHER_BSS) {
+		wl1271_debug(DEBUG_FILTERS, " - FIF_OTHER_BSS");
+		wl->rx_config &= ~CFG_BSSID_FILTER_EN;
+	}
+	if (filters & FIF_CONTROL) {
+		wl1271_debug(DEBUG_FILTERS, " - FIF_CONTROL");
+		wl->rx_filter |= CFG_RX_CTL_EN;
+	}
+	if (filters & FIF_FCSFAIL) {
+		wl1271_debug(DEBUG_FILTERS, " - FIF_FCSFAIL");
+		wl->rx_filter |= CFG_RX_FCS_ERROR;
 	}
 	}
-
-out_sleep:
-	wl1271_ps_elp_sleep(wl);
-
-out:
-	mutex_unlock(&wl->mutex);
-
-	return ret;
 }
 }
-#endif
 
 
 static int wl1271_join_channel(struct wl1271 *wl, int channel)
 static int wl1271_join_channel(struct wl1271 *wl, int channel)
 {
 {
@@ -1155,12 +1131,12 @@ static int wl1271_join_channel(struct wl1271 *wl, int channel)
 	static const u8 dummy_bssid[ETH_ALEN] = { 0x0b, 0xad, 0xde,
 	static const u8 dummy_bssid[ETH_ALEN] = { 0x0b, 0xad, 0xde,
 						  0xad, 0xbe, 0xef };
 						  0xad, 0xbe, 0xef };
 
 
-	/* disable mac filter, so we hear everything */
-	wl->rx_config &= ~CFG_BSSID_FILTER_EN;
-
 	wl->channel = channel;
 	wl->channel = channel;
 	memcpy(wl->bssid, dummy_bssid, ETH_ALEN);
 	memcpy(wl->bssid, dummy_bssid, ETH_ALEN);
 
 
+	/* pass through frames from all BSS */
+	wl1271_configure_filters(wl, FIF_OTHER_BSS);
+
 	/* the dummy join is performed always with STATION BSS type to allow
 	/* the dummy join is performed always with STATION BSS type to allow
 	   also ad-hoc mode to listen to the surroundings without sending any
 	   also ad-hoc mode to listen to the surroundings without sending any
 	   beacons yet. */
 	   beacons yet. */
@@ -1186,7 +1162,9 @@ static int wl1271_unjoin_channel(struct wl1271 *wl)
 	clear_bit(WL1271_FLAG_JOINED, &wl->flags);
 	clear_bit(WL1271_FLAG_JOINED, &wl->flags);
 	wl->channel = 0;
 	wl->channel = 0;
 	memset(wl->bssid, 0, ETH_ALEN);
 	memset(wl->bssid, 0, ETH_ALEN);
-	wl->rx_config = WL1271_DEFAULT_RX_CONFIG;
+
+	/* stop filterting packets based on bssid */
+	wl1271_configure_filters(wl, FIF_OTHER_BSS);
 
 
 out:
 out:
 	return ret;
 	return ret;
@@ -1359,14 +1337,14 @@ static void wl1271_op_configure_filter(struct ieee80211_hw *hw,
 	if (ret < 0)
 	if (ret < 0)
 		goto out_sleep;
 		goto out_sleep;
 
 
-	kfree(fp);
-
-	/* FIXME: We still need to set our filters properly */
-
 	/* determine, whether supported filter values have changed */
 	/* determine, whether supported filter values have changed */
 	if (changed == 0)
 	if (changed == 0)
 		goto out_sleep;
 		goto out_sleep;
 
 
+	/* configure filters */
+	wl->filters = *total;
+	wl1271_configure_filters(wl, 0);
+
 	/* apply configured filters */
 	/* apply configured filters */
 	ret = wl1271_acx_rx_config(wl, wl->rx_config, wl->rx_filter);
 	ret = wl1271_acx_rx_config(wl, wl->rx_config, wl->rx_filter);
 	if (ret < 0)
 	if (ret < 0)
@@ -1377,6 +1355,7 @@ out_sleep:
 
 
 out:
 out:
 	mutex_unlock(&wl->mutex);
 	mutex_unlock(&wl->mutex);
+	kfree(fp);
 }
 }
 
 
 static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
 static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
@@ -1522,10 +1501,12 @@ static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
 		goto out;
 		goto out;
 
 
 	if (wl1271_11a_enabled())
 	if (wl1271_11a_enabled())
-		ret = wl1271_cmd_scan(hw->priv, ssid, len, 1, 0,
+		ret = wl1271_cmd_scan(hw->priv, ssid, len,
+				      req->ie, req->ie_len, 1, 0,
 				      WL1271_SCAN_BAND_DUAL, 3);
 				      WL1271_SCAN_BAND_DUAL, 3);
 	else
 	else
-		ret = wl1271_cmd_scan(hw->priv, ssid, len, 1, 0,
+		ret = wl1271_cmd_scan(hw->priv, ssid, len,
+				      req->ie, req->ie_len, 1, 0,
 				      WL1271_SCAN_BAND_2_4_GHZ, 3);
 				      WL1271_SCAN_BAND_2_4_GHZ, 3);
 
 
 	wl1271_ps_elp_sleep(wl);
 	wl1271_ps_elp_sleep(wl);
@@ -1638,14 +1619,14 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
 	     * and enable the BSSID filter
 	     * and enable the BSSID filter
 	     */
 	     */
 	    memcmp(wl->bssid, bss_conf->bssid, ETH_ALEN)) {
 	    memcmp(wl->bssid, bss_conf->bssid, ETH_ALEN)) {
-			wl->rx_config |= CFG_BSSID_FILTER_EN;
 			memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN);
 			memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN);
+
 			ret = wl1271_cmd_build_null_data(wl);
 			ret = wl1271_cmd_build_null_data(wl);
-			if (ret < 0) {
-				wl1271_warning("cmd buld null data failed %d",
-					       ret);
+			if (ret < 0)
 				goto out_sleep;
 				goto out_sleep;
-			}
+
+			/* filter out all packets not from this BSSID */
+			wl1271_configure_filters(wl, 0);
 
 
 			/* Need to update the BSSID (for filtering etc) */
 			/* Need to update the BSSID (for filtering etc) */
 			do_join = true;
 			do_join = true;
@@ -1735,6 +1716,7 @@ static int wl1271_op_conf_tx(struct ieee80211_hw *hw, u16 queue,
 			     const struct ieee80211_tx_queue_params *params)
 			     const struct ieee80211_tx_queue_params *params)
 {
 {
 	struct wl1271 *wl = hw->priv;
 	struct wl1271 *wl = hw->priv;
+	u8 ps_scheme;
 	int ret;
 	int ret;
 
 
 	mutex_lock(&wl->mutex);
 	mutex_lock(&wl->mutex);
@@ -1745,17 +1727,22 @@ static int wl1271_op_conf_tx(struct ieee80211_hw *hw, u16 queue,
 	if (ret < 0)
 	if (ret < 0)
 		goto out;
 		goto out;
 
 
+	/* the txop is confed in units of 32us by the mac80211, we need us */
 	ret = wl1271_acx_ac_cfg(wl, wl1271_tx_get_queue(queue),
 	ret = wl1271_acx_ac_cfg(wl, wl1271_tx_get_queue(queue),
 				params->cw_min, params->cw_max,
 				params->cw_min, params->cw_max,
-				params->aifs, params->txop);
+				params->aifs, params->txop << 5);
 	if (ret < 0)
 	if (ret < 0)
 		goto out_sleep;
 		goto out_sleep;
 
 
+	if (params->uapsd)
+		ps_scheme = CONF_PS_SCHEME_UPSD_TRIGGER;
+	else
+		ps_scheme = CONF_PS_SCHEME_LEGACY;
+
 	ret = wl1271_acx_tid_cfg(wl, wl1271_tx_get_queue(queue),
 	ret = wl1271_acx_tid_cfg(wl, wl1271_tx_get_queue(queue),
 				 CONF_CHANNEL_TYPE_EDCF,
 				 CONF_CHANNEL_TYPE_EDCF,
 				 wl1271_tx_get_queue(queue),
 				 wl1271_tx_get_queue(queue),
-				 CONF_PS_SCHEME_LEGACY_PSPOLL,
-				 CONF_ACK_POLICY_LEGACY, 0, 0);
+				 ps_scheme, CONF_ACK_POLICY_LEGACY, 0, 0);
 	if (ret < 0)
 	if (ret < 0)
 		goto out_sleep;
 		goto out_sleep;
 
 
@@ -1925,7 +1912,6 @@ static const struct ieee80211_ops wl1271_ops = {
 	.add_interface = wl1271_op_add_interface,
 	.add_interface = wl1271_op_add_interface,
 	.remove_interface = wl1271_op_remove_interface,
 	.remove_interface = wl1271_op_remove_interface,
 	.config = wl1271_op_config,
 	.config = wl1271_op_config,
-/* 	.config_interface = wl1271_op_config_interface, */
 	.prepare_multicast = wl1271_op_prepare_multicast,
 	.prepare_multicast = wl1271_op_prepare_multicast,
 	.configure_filter = wl1271_op_configure_filter,
 	.configure_filter = wl1271_op_configure_filter,
 	.tx = wl1271_op_tx,
 	.tx = wl1271_op_tx,
@@ -1937,6 +1923,68 @@ static const struct ieee80211_ops wl1271_ops = {
 	CFG80211_TESTMODE_CMD(wl1271_tm_cmd)
 	CFG80211_TESTMODE_CMD(wl1271_tm_cmd)
 };
 };
 
 
+static ssize_t wl1271_sysfs_show_bt_coex_state(struct device *dev,
+					       struct device_attribute *attr,
+					       char *buf)
+{
+	struct wl1271 *wl = dev_get_drvdata(dev);
+	ssize_t len;
+
+	/* FIXME: what's the maximum length of buf? page size?*/
+	len = 500;
+
+	mutex_lock(&wl->mutex);
+	len = snprintf(buf, len, "%d\n\n0 - off\n1 - on\n",
+		       wl->sg_enabled);
+	mutex_unlock(&wl->mutex);
+
+	return len;
+
+}
+
+static ssize_t wl1271_sysfs_store_bt_coex_state(struct device *dev,
+						struct device_attribute *attr,
+						const char *buf, size_t count)
+{
+	struct wl1271 *wl = dev_get_drvdata(dev);
+	unsigned long res;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &res);
+
+	if (ret < 0) {
+		wl1271_warning("incorrect value written to bt_coex_mode");
+		return count;
+	}
+
+	mutex_lock(&wl->mutex);
+
+	res = !!res;
+
+	if (res == wl->sg_enabled)
+		goto out;
+
+	wl->sg_enabled = res;
+
+	if (wl->state == WL1271_STATE_OFF)
+		goto out;
+
+	ret = wl1271_ps_elp_wakeup(wl, false);
+	if (ret < 0)
+		goto out;
+
+	wl1271_acx_sg_enable(wl, wl->sg_enabled);
+	wl1271_ps_elp_sleep(wl);
+
+ out:
+	mutex_unlock(&wl->mutex);
+	return count;
+}
+
+static DEVICE_ATTR(bt_coex_state, S_IRUGO | S_IWUSR,
+		   wl1271_sysfs_show_bt_coex_state,
+		   wl1271_sysfs_store_bt_coex_state);
+
 int wl1271_register_hw(struct wl1271 *wl)
 int wl1271_register_hw(struct wl1271 *wl)
 {
 {
 	int ret;
 	int ret;
@@ -1960,6 +2008,14 @@ int wl1271_register_hw(struct wl1271 *wl)
 }
 }
 EXPORT_SYMBOL_GPL(wl1271_register_hw);
 EXPORT_SYMBOL_GPL(wl1271_register_hw);
 
 
+void wl1271_unregister_hw(struct wl1271 *wl)
+{
+	ieee80211_unregister_hw(wl->hw);
+	wl->mac80211_registered = false;
+
+}
+EXPORT_SYMBOL_GPL(wl1271_unregister_hw);
+
 int wl1271_init_ieee80211(struct wl1271 *wl)
 int wl1271_init_ieee80211(struct wl1271 *wl)
 {
 {
 	/* The tx descriptor buffer and the TKIP space. */
 	/* The tx descriptor buffer and the TKIP space. */
@@ -1974,6 +2030,7 @@ int wl1271_init_ieee80211(struct wl1271 *wl)
 		IEEE80211_HW_NOISE_DBM |
 		IEEE80211_HW_NOISE_DBM |
 		IEEE80211_HW_BEACON_FILTER |
 		IEEE80211_HW_BEACON_FILTER |
 		IEEE80211_HW_SUPPORTS_PS |
 		IEEE80211_HW_SUPPORTS_PS |
+		IEEE80211_HW_SUPPORTS_UAPSD |
 		IEEE80211_HW_HAS_RATE_CONTROL;
 		IEEE80211_HW_HAS_RATE_CONTROL;
 
 
 	wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
 	wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
@@ -1984,6 +2041,8 @@ int wl1271_init_ieee80211(struct wl1271 *wl)
 	if (wl1271_11a_enabled())
 	if (wl1271_11a_enabled())
 		wl->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &wl1271_band_5ghz;
 		wl->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &wl1271_band_5ghz;
 
 
+	wl->hw->queues = 4;
+
 	SET_IEEE80211_DEV(wl->hw, wl1271_wl_to_dev(wl));
 	SET_IEEE80211_DEV(wl->hw, wl1271_wl_to_dev(wl));
 
 
 	return 0;
 	return 0;
@@ -1995,21 +2054,34 @@ EXPORT_SYMBOL_GPL(wl1271_init_ieee80211);
 struct ieee80211_hw *wl1271_alloc_hw(void)
 struct ieee80211_hw *wl1271_alloc_hw(void)
 {
 {
 	struct ieee80211_hw *hw;
 	struct ieee80211_hw *hw;
+	struct platform_device *plat_dev = NULL;
 	struct wl1271 *wl;
 	struct wl1271 *wl;
-	int i;
+	int i, ret;
+	static const u8 nokia_oui[3] = {0x00, 0x1f, 0xdf};
 
 
 	hw = ieee80211_alloc_hw(sizeof(*wl), &wl1271_ops);
 	hw = ieee80211_alloc_hw(sizeof(*wl), &wl1271_ops);
 	if (!hw) {
 	if (!hw) {
 		wl1271_error("could not alloc ieee80211_hw");
 		wl1271_error("could not alloc ieee80211_hw");
-		return ERR_PTR(-ENOMEM);
+		ret = -ENOMEM;
+		goto err_hw_alloc;
 	}
 	}
 
 
+	plat_dev = kmalloc(sizeof(wl1271_device), GFP_KERNEL);
+	if (!plat_dev) {
+		wl1271_error("could not allocate platform_device");
+		ret = -ENOMEM;
+		goto err_plat_alloc;
+	}
+
+	memcpy(plat_dev, &wl1271_device, sizeof(wl1271_device));
+
 	wl = hw->priv;
 	wl = hw->priv;
 	memset(wl, 0, sizeof(*wl));
 	memset(wl, 0, sizeof(*wl));
 
 
 	INIT_LIST_HEAD(&wl->list);
 	INIT_LIST_HEAD(&wl->list);
 
 
 	wl->hw = hw;
 	wl->hw = hw;
+	wl->plat_dev = plat_dev;
 
 
 	skb_queue_head_init(&wl->tx_queue);
 	skb_queue_head_init(&wl->tx_queue);
 
 
@@ -2027,6 +2099,7 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
 	wl->band = IEEE80211_BAND_2GHZ;
 	wl->band = IEEE80211_BAND_2GHZ;
 	wl->vif = NULL;
 	wl->vif = NULL;
 	wl->flags = 0;
 	wl->flags = 0;
+	wl->sg_enabled = true;
 
 
 	for (i = 0; i < ACX_TX_DESCRIPTORS; i++)
 	for (i = 0; i < ACX_TX_DESCRIPTORS; i++)
 		wl->tx_frames[i] = NULL;
 		wl->tx_frames[i] = NULL;
@@ -2036,18 +2109,55 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
 	wl->state = WL1271_STATE_OFF;
 	wl->state = WL1271_STATE_OFF;
 	mutex_init(&wl->mutex);
 	mutex_init(&wl->mutex);
 
 
+	/*
+	 * FIXME: we should use a zero MAC address here, but for now we
+	 * generate a random Nokia address.
+	 */
+	memcpy(wl->mac_addr, nokia_oui, 3);
+	get_random_bytes(wl->mac_addr + 3, 3);
+
 	/* Apply default driver configuration. */
 	/* Apply default driver configuration. */
 	wl1271_conf_init(wl);
 	wl1271_conf_init(wl);
 
 
 	wl1271_debugfs_init(wl);
 	wl1271_debugfs_init(wl);
 
 
+	/* Register platform device */
+	ret = platform_device_register(wl->plat_dev);
+	if (ret) {
+		wl1271_error("couldn't register platform device");
+		goto err_hw;
+	}
+	dev_set_drvdata(&wl->plat_dev->dev, wl);
+
+	/* Create sysfs file to control bt coex state */
+	ret = device_create_file(&wl->plat_dev->dev, &dev_attr_bt_coex_state);
+	if (ret < 0) {
+		wl1271_error("failed to create sysfs file bt_coex_state");
+		goto err_platform;
+	}
+
 	return hw;
 	return hw;
+
+err_platform:
+	platform_device_unregister(wl->plat_dev);
+
+err_hw:
+	wl1271_debugfs_exit(wl);
+	kfree(plat_dev);
+
+err_plat_alloc:
+	ieee80211_free_hw(hw);
+
+err_hw_alloc:
+
+	return ERR_PTR(ret);
 }
 }
 EXPORT_SYMBOL_GPL(wl1271_alloc_hw);
 EXPORT_SYMBOL_GPL(wl1271_alloc_hw);
 
 
 int wl1271_free_hw(struct wl1271 *wl)
 int wl1271_free_hw(struct wl1271 *wl)
 {
 {
-	ieee80211_unregister_hw(wl->hw);
+	platform_device_unregister(wl->plat_dev);
+	kfree(wl->plat_dev);
 
 
 	wl1271_debugfs_exit(wl);
 	wl1271_debugfs_exit(wl);
 
 

+ 29 - 45
drivers/net/wireless/wl12xx/wl1271_sdio.c

@@ -102,15 +102,14 @@ static void wl1271_sdio_init(struct wl1271 *wl)
 }
 }
 
 
 static void wl1271_sdio_raw_read(struct wl1271 *wl, int addr, void *buf,
 static void wl1271_sdio_raw_read(struct wl1271 *wl, int addr, void *buf,
-			 size_t len, bool fixed)
+				 size_t len, bool fixed)
 {
 {
 	int ret;
 	int ret;
 	struct sdio_func *func = wl_to_func(wl);
 	struct sdio_func *func = wl_to_func(wl);
 
 
-	sdio_claim_host(func);
 	if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG_ADDR)) {
 	if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG_ADDR)) {
 		((u8 *)buf)[0] = sdio_f0_readb(func, addr, &ret);
 		((u8 *)buf)[0] = sdio_f0_readb(func, addr, &ret);
-		wl1271_debug(DEBUG_SPI, "sdio read 52 addr 0x%x, byte 0x%02x",
+		wl1271_debug(DEBUG_SDIO, "sdio read 52 addr 0x%x, byte 0x%02x",
 			     addr, ((u8 *)buf)[0]);
 			     addr, ((u8 *)buf)[0]);
 	} else {
 	} else {
 		if (fixed)
 		if (fixed)
@@ -118,32 +117,30 @@ static void wl1271_sdio_raw_read(struct wl1271 *wl, int addr, void *buf,
 		else
 		else
 			ret = sdio_memcpy_fromio(func, buf, addr, len);
 			ret = sdio_memcpy_fromio(func, buf, addr, len);
 
 
-		wl1271_debug(DEBUG_SPI, "sdio read 53 addr 0x%x, %d bytes",
+		wl1271_debug(DEBUG_SDIO, "sdio read 53 addr 0x%x, %d bytes",
 			     addr, len);
 			     addr, len);
-		wl1271_dump_ascii(DEBUG_SPI, "data: ", buf, len);
+		wl1271_dump_ascii(DEBUG_SDIO, "data: ", buf, len);
 	}
 	}
 
 
 	if (ret)
 	if (ret)
 		wl1271_error("sdio read failed (%d)", ret);
 		wl1271_error("sdio read failed (%d)", ret);
 
 
-	sdio_release_host(func);
 }
 }
 
 
 static void wl1271_sdio_raw_write(struct wl1271 *wl, int addr, void *buf,
 static void wl1271_sdio_raw_write(struct wl1271 *wl, int addr, void *buf,
-			  size_t len, bool fixed)
+				  size_t len, bool fixed)
 {
 {
 	int ret;
 	int ret;
 	struct sdio_func *func = wl_to_func(wl);
 	struct sdio_func *func = wl_to_func(wl);
 
 
-	sdio_claim_host(func);
 	if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG_ADDR)) {
 	if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG_ADDR)) {
 		sdio_f0_writeb(func, ((u8 *)buf)[0], addr, &ret);
 		sdio_f0_writeb(func, ((u8 *)buf)[0], addr, &ret);
-		wl1271_debug(DEBUG_SPI, "sdio write 52 addr 0x%x, byte 0x%02x",
+		wl1271_debug(DEBUG_SDIO, "sdio write 52 addr 0x%x, byte 0x%02x",
 			     addr, ((u8 *)buf)[0]);
 			     addr, ((u8 *)buf)[0]);
 	} else {
 	} else {
-		wl1271_debug(DEBUG_SPI, "sdio write 53 addr 0x%x, %d bytes",
+		wl1271_debug(DEBUG_SDIO, "sdio write 53 addr 0x%x, %d bytes",
 			     addr, len);
 			     addr, len);
-		wl1271_dump_ascii(DEBUG_SPI, "data: ", buf, len);
+		wl1271_dump_ascii(DEBUG_SDIO, "data: ", buf, len);
 
 
 		if (fixed)
 		if (fixed)
 			ret = sdio_writesb(func, addr, buf, len);
 			ret = sdio_writesb(func, addr, buf, len);
@@ -153,7 +150,23 @@ static void wl1271_sdio_raw_write(struct wl1271 *wl, int addr, void *buf,
 	if (ret)
 	if (ret)
 		wl1271_error("sdio write failed (%d)", ret);
 		wl1271_error("sdio write failed (%d)", ret);
 
 
-	sdio_release_host(func);
+}
+
+static void wl1271_sdio_set_power(struct wl1271 *wl, bool enable)
+{
+	struct sdio_func *func = wl_to_func(wl);
+
+	/* Let the SDIO stack handle wlan_enable control, so we
+	 * keep host claimed while wlan is in use to keep wl1271
+	 * alive.
+	 */
+	if (enable) {
+		sdio_claim_host(func);
+		sdio_enable_func(func);
+	} else {
+		sdio_disable_func(func);
+		sdio_release_host(func);
+	}
 }
 }
 
 
 static struct wl1271_if_operations sdio_ops = {
 static struct wl1271_if_operations sdio_ops = {
@@ -161,15 +174,12 @@ static struct wl1271_if_operations sdio_ops = {
 	.write		= wl1271_sdio_raw_write,
 	.write		= wl1271_sdio_raw_write,
 	.reset		= wl1271_sdio_reset,
 	.reset		= wl1271_sdio_reset,
 	.init		= wl1271_sdio_init,
 	.init		= wl1271_sdio_init,
+	.power		= wl1271_sdio_set_power,
 	.dev		= wl1271_sdio_wl_to_dev,
 	.dev		= wl1271_sdio_wl_to_dev,
 	.enable_irq	= wl1271_sdio_enable_interrupts,
 	.enable_irq	= wl1271_sdio_enable_interrupts,
 	.disable_irq	= wl1271_sdio_disable_interrupts
 	.disable_irq	= wl1271_sdio_disable_interrupts
 };
 };
 
 
-static void wl1271_sdio_set_power(bool enable)
-{
-}
-
 static int __devinit wl1271_probe(struct sdio_func *func,
 static int __devinit wl1271_probe(struct sdio_func *func,
 				  const struct sdio_device_id *id)
 				  const struct sdio_device_id *id)
 {
 {
@@ -190,8 +200,6 @@ static int __devinit wl1271_probe(struct sdio_func *func,
 	wl->if_priv = func;
 	wl->if_priv = func;
 	wl->if_ops = &sdio_ops;
 	wl->if_ops = &sdio_ops;
 
 
-	wl->set_power = wl1271_sdio_set_power;
-
 	/* Grab access to FN0 for ELP reg. */
 	/* Grab access to FN0 for ELP reg. */
 	func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
 	func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
 
 
@@ -220,28 +228,18 @@ static int __devinit wl1271_probe(struct sdio_func *func,
 	if (ret)
 	if (ret)
 		goto out_irq;
 		goto out_irq;
 
 
-	sdio_claim_host(func);
 	sdio_set_drvdata(func, wl);
 	sdio_set_drvdata(func, wl);
 
 
-	ret = sdio_enable_func(func);
-	if (ret)
-		goto out_release;
-
-	sdio_release_host(func);
-
 	wl1271_notice("initialized");
 	wl1271_notice("initialized");
 
 
 	return 0;
 	return 0;
 
 
- out_release:
-	sdio_release_host(func);
-
  out_irq:
  out_irq:
 	free_irq(wl->irq, wl);
 	free_irq(wl->irq, wl);
 
 
 
 
  out_free:
  out_free:
-	ieee80211_free_hw(hw);
+	wl1271_free_hw(wl);
 
 
 	return ret;
 	return ret;
 }
 }
@@ -250,24 +248,10 @@ static void __devexit wl1271_remove(struct sdio_func *func)
 {
 {
 	struct wl1271 *wl = sdio_get_drvdata(func);
 	struct wl1271 *wl = sdio_get_drvdata(func);
 
 
-	ieee80211_unregister_hw(wl->hw);
-
-	sdio_claim_host(func);
-	sdio_disable_func(func);
-	sdio_release_host(func);
-
 	free_irq(wl->irq, wl);
 	free_irq(wl->irq, wl);
 
 
-	kfree(wl->target_mem_map);
-	vfree(wl->fw);
-	wl->fw = NULL;
-	kfree(wl->nvs);
-	wl->nvs = NULL;
-
-	kfree(wl->fw_status);
-	kfree(wl->tx_res_if);
-
-	ieee80211_free_hw(wl->hw);
+	wl1271_unregister_hw(wl);
+	wl1271_free_hw(wl);
 }
 }
 
 
 static struct sdio_driver wl1271_sdio_driver = {
 static struct sdio_driver wl1271_sdio_driver = {

+ 8 - 27
drivers/net/wireless/wl12xx/wl1271_spi.c

@@ -23,7 +23,6 @@
 
 
 #include <linux/irq.h>
 #include <linux/irq.h>
 #include <linux/module.h>
 #include <linux/module.h>
-#include <linux/platform_device.h>
 #include <linux/crc7.h>
 #include <linux/crc7.h>
 #include <linux/spi/spi.h>
 #include <linux/spi/spi.h>
 #include <linux/spi/wl12xx.h>
 #include <linux/spi/wl12xx.h>
@@ -332,26 +331,18 @@ static irqreturn_t wl1271_irq(int irq, void *cookie)
 	return IRQ_HANDLED;
 	return IRQ_HANDLED;
 }
 }
 
 
-static void wl1271_device_release(struct device *dev)
+static void wl1271_spi_set_power(struct wl1271 *wl, bool enable)
 {
 {
-
+	if (wl->set_power)
+		wl->set_power(enable);
 }
 }
 
 
-static struct platform_device wl1271_device = {
-	.name           = "wl1271",
-	.id             = -1,
-
-	/* device model insists to have a release function */
-	.dev            = {
-		.release = wl1271_device_release,
-	},
-};
-
 static struct wl1271_if_operations spi_ops = {
 static struct wl1271_if_operations spi_ops = {
 	.read		= wl1271_spi_raw_read,
 	.read		= wl1271_spi_raw_read,
 	.write		= wl1271_spi_raw_write,
 	.write		= wl1271_spi_raw_write,
 	.reset		= wl1271_spi_reset,
 	.reset		= wl1271_spi_reset,
 	.init		= wl1271_spi_init,
 	.init		= wl1271_spi_init,
+	.power		= wl1271_spi_set_power,
 	.dev		= wl1271_spi_wl_to_dev,
 	.dev		= wl1271_spi_wl_to_dev,
 	.enable_irq	= wl1271_spi_enable_interrupts,
 	.enable_irq	= wl1271_spi_enable_interrupts,
 	.disable_irq	= wl1271_spi_disable_interrupts
 	.disable_irq	= wl1271_spi_disable_interrupts
@@ -415,33 +406,23 @@ static int __devinit wl1271_probe(struct spi_device *spi)
 
 
 	disable_irq(wl->irq);
 	disable_irq(wl->irq);
 
 
-	ret = platform_device_register(&wl1271_device);
-	if (ret) {
-		wl1271_error("couldn't register platform device");
-		goto out_irq;
-	}
-	dev_set_drvdata(&wl1271_device.dev, wl);
-
 	ret = wl1271_init_ieee80211(wl);
 	ret = wl1271_init_ieee80211(wl);
 	if (ret)
 	if (ret)
-		goto out_platform;
+		goto out_irq;
 
 
 	ret = wl1271_register_hw(wl);
 	ret = wl1271_register_hw(wl);
 	if (ret)
 	if (ret)
-		goto out_platform;
+		goto out_irq;
 
 
 	wl1271_notice("initialized");
 	wl1271_notice("initialized");
 
 
 	return 0;
 	return 0;
 
 
- out_platform:
-	platform_device_unregister(&wl1271_device);
-
  out_irq:
  out_irq:
 	free_irq(wl->irq, wl);
 	free_irq(wl->irq, wl);
 
 
  out_free:
  out_free:
-	ieee80211_free_hw(hw);
+	wl1271_free_hw(wl);
 
 
 	return ret;
 	return ret;
 }
 }
@@ -450,9 +431,9 @@ static int __devexit wl1271_remove(struct spi_device *spi)
 {
 {
 	struct wl1271 *wl = dev_get_drvdata(&spi->dev);
 	struct wl1271 *wl = dev_get_drvdata(&spi->dev);
 
 
-	platform_device_unregister(&wl1271_device);
 	free_irq(wl->irq, wl);
 	free_irq(wl->irq, wl);
 
 
+	wl1271_unregister_hw(wl);
 	wl1271_free_hw(wl);
 	wl1271_free_hw(wl);
 
 
 	return 0;
 	return 0;

+ 0 - 3
drivers/net/wireless/wl12xx/wl1271_tx.h

@@ -125,9 +125,6 @@ struct wl1271_tx_hw_res_if {
 
 
 static inline int wl1271_tx_get_queue(int queue)
 static inline int wl1271_tx_get_queue(int queue)
 {
 {
-	/* FIXME: use best effort until WMM is enabled */
-	return CONF_TX_AC_BE;
-
 	switch (queue) {
 	switch (queue) {
 	case 0:
 	case 0:
 		return CONF_TX_AC_VO;
 		return CONF_TX_AC_VO;

+ 26 - 26
drivers/net/wireless/wl3501_cs.c

@@ -1834,32 +1834,32 @@ out:
 }
 }
 
 
 static const iw_handler	wl3501_handler[] = {
 static const iw_handler	wl3501_handler[] = {
-	[SIOCGIWNAME	- SIOCIWFIRST] = wl3501_get_name,
-	[SIOCSIWFREQ	- SIOCIWFIRST] = wl3501_set_freq,
-	[SIOCGIWFREQ	- SIOCIWFIRST] = wl3501_get_freq,
-	[SIOCSIWMODE	- SIOCIWFIRST] = wl3501_set_mode,
-	[SIOCGIWMODE	- SIOCIWFIRST] = wl3501_get_mode,
-	[SIOCGIWSENS	- SIOCIWFIRST] = wl3501_get_sens,
-	[SIOCGIWRANGE	- SIOCIWFIRST] = wl3501_get_range,
-	[SIOCSIWSPY	- SIOCIWFIRST] = iw_handler_set_spy,
-	[SIOCGIWSPY	- SIOCIWFIRST] = iw_handler_get_spy,
-	[SIOCSIWTHRSPY	- SIOCIWFIRST] = iw_handler_set_thrspy,
-	[SIOCGIWTHRSPY	- SIOCIWFIRST] = iw_handler_get_thrspy,
-	[SIOCSIWAP	- SIOCIWFIRST] = wl3501_set_wap,
-	[SIOCGIWAP	- SIOCIWFIRST] = wl3501_get_wap,
-	[SIOCSIWSCAN	- SIOCIWFIRST] = wl3501_set_scan,
-	[SIOCGIWSCAN	- SIOCIWFIRST] = wl3501_get_scan,
-	[SIOCSIWESSID	- SIOCIWFIRST] = wl3501_set_essid,
-	[SIOCGIWESSID	- SIOCIWFIRST] = wl3501_get_essid,
-	[SIOCSIWNICKN	- SIOCIWFIRST] = wl3501_set_nick,
-	[SIOCGIWNICKN	- SIOCIWFIRST] = wl3501_get_nick,
-	[SIOCGIWRATE	- SIOCIWFIRST] = wl3501_get_rate,
-	[SIOCGIWRTS	- SIOCIWFIRST] = wl3501_get_rts_threshold,
-	[SIOCGIWFRAG	- SIOCIWFIRST] = wl3501_get_frag_threshold,
-	[SIOCGIWTXPOW	- SIOCIWFIRST] = wl3501_get_txpow,
-	[SIOCGIWRETRY	- SIOCIWFIRST] = wl3501_get_retry,
-	[SIOCGIWENCODE	- SIOCIWFIRST] = wl3501_get_encode,
-	[SIOCGIWPOWER	- SIOCIWFIRST] = wl3501_get_power,
+	IW_HANDLER(SIOCGIWNAME, wl3501_get_name),
+	IW_HANDLER(SIOCSIWFREQ, wl3501_set_freq),
+	IW_HANDLER(SIOCGIWFREQ, wl3501_get_freq),
+	IW_HANDLER(SIOCSIWMODE, wl3501_set_mode),
+	IW_HANDLER(SIOCGIWMODE, wl3501_get_mode),
+	IW_HANDLER(SIOCGIWSENS, wl3501_get_sens),
+	IW_HANDLER(SIOCGIWRANGE, wl3501_get_range),
+	IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy),
+	IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy),
+	IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy),
+	IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy),
+	IW_HANDLER(SIOCSIWAP, wl3501_set_wap),
+	IW_HANDLER(SIOCGIWAP, wl3501_get_wap),
+	IW_HANDLER(SIOCSIWSCAN, wl3501_set_scan),
+	IW_HANDLER(SIOCGIWSCAN, wl3501_get_scan),
+	IW_HANDLER(SIOCSIWESSID, wl3501_set_essid),
+	IW_HANDLER(SIOCGIWESSID, wl3501_get_essid),
+	IW_HANDLER(SIOCSIWNICKN, wl3501_set_nick),
+	IW_HANDLER(SIOCGIWNICKN, wl3501_get_nick),
+	IW_HANDLER(SIOCGIWRATE, wl3501_get_rate),
+	IW_HANDLER(SIOCGIWRTS, wl3501_get_rts_threshold),
+	IW_HANDLER(SIOCGIWFRAG, wl3501_get_frag_threshold),
+	IW_HANDLER(SIOCGIWTXPOW, wl3501_get_txpow),
+	IW_HANDLER(SIOCGIWRETRY, wl3501_get_retry),
+	IW_HANDLER(SIOCGIWENCODE, wl3501_get_encode),
+	IW_HANDLER(SIOCGIWPOWER, wl3501_get_power),
 };
 };
 
 
 static const struct iw_handler_def wl3501_handler_def = {
 static const struct iw_handler_def wl3501_handler_def = {

+ 50 - 0
include/linux/nl80211.h

@@ -323,6 +323,12 @@
  *	the TX command and %NL80211_ATTR_FRAME includes the contents of the
  *	the TX command and %NL80211_ATTR_FRAME includes the contents of the
  *	frame. %NL80211_ATTR_ACK flag is included if the recipient acknowledged
  *	frame. %NL80211_ATTR_ACK flag is included if the recipient acknowledged
  *	the frame.
  *	the frame.
+ * @NL80211_CMD_SET_CQM: Connection quality monitor configuration. This command
+ *	is used to configure connection quality monitoring notification trigger
+ *	levels.
+ * @NL80211_CMD_NOTIFY_CQM: Connection quality monitor notification. This
+ *	command is used as an event to indicate the that a trigger level was
+ *	reached.
  *
  *
  * @NL80211_CMD_MAX: highest used command number
  * @NL80211_CMD_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
  * @__NL80211_CMD_AFTER_LAST: internal use
@@ -419,6 +425,9 @@ enum nl80211_commands {
 	NL80211_CMD_SET_POWER_SAVE,
 	NL80211_CMD_SET_POWER_SAVE,
 	NL80211_CMD_GET_POWER_SAVE,
 	NL80211_CMD_GET_POWER_SAVE,
 
 
+	NL80211_CMD_SET_CQM,
+	NL80211_CMD_NOTIFY_CQM,
+
 	/* add new commands above here */
 	/* add new commands above here */
 
 
 	/* used to define NL80211_CMD_MAX below */
 	/* used to define NL80211_CMD_MAX below */
@@ -691,6 +700,9 @@ enum nl80211_commands {
  * @NL80211_ATTR_ACK: Flag attribute indicating that the frame was
  * @NL80211_ATTR_ACK: Flag attribute indicating that the frame was
  *	acknowledged by the recipient.
  *	acknowledged by the recipient.
  *
  *
+ * @NL80211_ATTR_CQM: connection quality monitor configuration in a
+ *	nested attribute with %NL80211_ATTR_CQM_* sub-attributes.
+ *
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
  */
@@ -842,6 +854,8 @@ enum nl80211_attrs {
 
 
 	NL80211_ATTR_PS_STATE,
 	NL80211_ATTR_PS_STATE,
 
 
+	NL80211_ATTR_CQM,
+
 	/* add attributes here, update the policy in nl80211.c */
 	/* add attributes here, update the policy in nl80211.c */
 
 
 	__NL80211_ATTR_AFTER_LAST,
 	__NL80211_ATTR_AFTER_LAST,
@@ -1583,4 +1597,40 @@ enum nl80211_ps_state {
 	NL80211_PS_ENABLED,
 	NL80211_PS_ENABLED,
 };
 };
 
 
+/**
+ * enum nl80211_attr_cqm - connection quality monitor attributes
+ * @__NL80211_ATTR_CQM_INVALID: invalid
+ * @NL80211_ATTR_CQM_RSSI_THOLD: RSSI threshold in dBm. This value specifies
+ *	the threshold for the RSSI level at which an event will be sent. Zero
+ *	to disable.
+ * @NL80211_ATTR_CQM_RSSI_HYST: RSSI hysteresis in dBm. This value specifies
+ *	the minimum amount the RSSI level must change after an event before a
+ *	new event may be issued (to reduce effects of RSSI oscillation).
+ * @NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT: RSSI threshold event
+ * @__NL80211_ATTR_CQM_AFTER_LAST: internal
+ * @NL80211_ATTR_CQM_MAX: highest key attribute
+ */
+enum nl80211_attr_cqm {
+	__NL80211_ATTR_CQM_INVALID,
+	NL80211_ATTR_CQM_RSSI_THOLD,
+	NL80211_ATTR_CQM_RSSI_HYST,
+	NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
+
+	/* keep last */
+	__NL80211_ATTR_CQM_AFTER_LAST,
+	NL80211_ATTR_CQM_MAX = __NL80211_ATTR_CQM_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_cqm_rssi_threshold_event - RSSI threshold event
+ * @NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW - The RSSI level is lower than the
+ *      configured threshold
+ * @NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH - The RSSI is higher than the
+ *      configured threshold
+ */
+enum nl80211_cqm_rssi_threshold_event {
+	NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
+	NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
+};
+
 #endif /* __LINUX_NL80211_H */
 #endif /* __LINUX_NL80211_H */

+ 3 - 1
include/linux/wireless.h

@@ -346,6 +346,8 @@
 #define SIOCIWFIRST	0x8B00
 #define SIOCIWFIRST	0x8B00
 #define SIOCIWLAST	SIOCIWLASTPRIV		/* 0x8BFF */
 #define SIOCIWLAST	SIOCIWLASTPRIV		/* 0x8BFF */
 #define IW_IOCTL_IDX(cmd)	((cmd) - SIOCIWFIRST)
 #define IW_IOCTL_IDX(cmd)	((cmd) - SIOCIWFIRST)
+#define IW_HANDLER(id, func)			\
+	[IW_IOCTL_IDX(id)] = func
 
 
 /* Odd : get (world access), even : set (root access) */
 /* Odd : get (world access), even : set (root access) */
 #define IW_IS_SET(cmd)	(!((cmd) & 0x1))
 #define IW_IS_SET(cmd)	(!((cmd) & 0x1))
@@ -648,7 +650,7 @@
  * 32 bit bitmasks. Note : 32 bits = 0x20 = 2^5. */
  * 32 bit bitmasks. Note : 32 bits = 0x20 = 2^5. */
 #define IW_EVENT_CAPA_BASE(cmd)		((cmd >= SIOCIWFIRSTPRIV) ? \
 #define IW_EVENT_CAPA_BASE(cmd)		((cmd >= SIOCIWFIRSTPRIV) ? \
 					 (cmd - SIOCIWFIRSTPRIV + 0x60) : \
 					 (cmd - SIOCIWFIRSTPRIV + 0x60) : \
-					 (cmd - SIOCSIWCOMMIT))
+					 (cmd - SIOCIWFIRST))
 #define IW_EVENT_CAPA_INDEX(cmd)	(IW_EVENT_CAPA_BASE(cmd) >> 5)
 #define IW_EVENT_CAPA_INDEX(cmd)	(IW_EVENT_CAPA_BASE(cmd) >> 5)
 #define IW_EVENT_CAPA_MASK(cmd)		(1 << (IW_EVENT_CAPA_BASE(cmd) & 0x1F))
 #define IW_EVENT_CAPA_MASK(cmd)		(1 << (IW_EVENT_CAPA_BASE(cmd) & 0x1F))
 /* Event capability constants - event autogenerated by the kernel
 /* Event capability constants - event autogenerated by the kernel

+ 19 - 0
include/net/cfg80211.h

@@ -1007,6 +1007,7 @@ struct cfg80211_pmksa {
  *	RSN IE. It allows for faster roaming between WPA2 BSSIDs.
  *	RSN IE. It allows for faster roaming between WPA2 BSSIDs.
  * @del_pmksa: Delete a cached PMKID.
  * @del_pmksa: Delete a cached PMKID.
  * @flush_pmksa: Flush all cached PMKIDs.
  * @flush_pmksa: Flush all cached PMKIDs.
+ * @set_cqm_rssi_config: Configure connection quality monitor RSSI threshold.
  *
  *
  */
  */
 struct cfg80211_ops {
 struct cfg80211_ops {
@@ -1152,6 +1153,10 @@ struct cfg80211_ops {
 
 
 	int	(*set_power_mgmt)(struct wiphy *wiphy, struct net_device *dev,
 	int	(*set_power_mgmt)(struct wiphy *wiphy, struct net_device *dev,
 				  bool enabled, int timeout);
 				  bool enabled, int timeout);
+
+	int	(*set_cqm_rssi_config)(struct wiphy *wiphy,
+				       struct net_device *dev,
+				       s32 rssi_thold, u32 rssi_hyst);
 };
 };
 
 
 /*
 /*
@@ -2337,4 +2342,18 @@ bool cfg80211_rx_action(struct net_device *dev, int freq, const u8 *buf,
 void cfg80211_action_tx_status(struct net_device *dev, u64 cookie,
 void cfg80211_action_tx_status(struct net_device *dev, u64 cookie,
 			       const u8 *buf, size_t len, bool ack, gfp_t gfp);
 			       const u8 *buf, size_t len, bool ack, gfp_t gfp);
 
 
+
+/**
+ * cfg80211_cqm_rssi_notify - connection quality monitoring rssi event
+ * @dev: network device
+ * @rssi_event: the triggered RSSI event
+ * @gfp: context flags
+ *
+ * This function is called when a configured connection quality monitoring
+ * rssi threshold reached event occurs.
+ */
+void cfg80211_cqm_rssi_notify(struct net_device *dev,
+			      enum nl80211_cqm_rssi_threshold_event rssi_event,
+			      gfp_t gfp);
+
 #endif /* __NET_CFG80211_H */
 #endif /* __NET_CFG80211_H */

+ 52 - 2
include/net/mac80211.h

@@ -144,6 +144,7 @@ struct ieee80211_low_level_stats {
  *	new beacon (beaconing modes)
  *	new beacon (beaconing modes)
  * @BSS_CHANGED_BEACON_ENABLED: Beaconing should be
  * @BSS_CHANGED_BEACON_ENABLED: Beaconing should be
  *	enabled/disabled (beaconing modes)
  *	enabled/disabled (beaconing modes)
+ * @BSS_CHANGED_CQM: Connection quality monitor config changed
  */
  */
 enum ieee80211_bss_change {
 enum ieee80211_bss_change {
 	BSS_CHANGED_ASSOC		= 1<<0,
 	BSS_CHANGED_ASSOC		= 1<<0,
@@ -156,6 +157,7 @@ enum ieee80211_bss_change {
 	BSS_CHANGED_BSSID		= 1<<7,
 	BSS_CHANGED_BSSID		= 1<<7,
 	BSS_CHANGED_BEACON		= 1<<8,
 	BSS_CHANGED_BEACON		= 1<<8,
 	BSS_CHANGED_BEACON_ENABLED	= 1<<9,
 	BSS_CHANGED_BEACON_ENABLED	= 1<<9,
+	BSS_CHANGED_CQM			= 1<<10,
 };
 };
 
 
 /**
 /**
@@ -185,6 +187,9 @@ enum ieee80211_bss_change {
  * @enable_beacon: whether beaconing should be enabled or not
  * @enable_beacon: whether beaconing should be enabled or not
  * @ht_operation_mode: HT operation mode (like in &struct ieee80211_ht_info).
  * @ht_operation_mode: HT operation mode (like in &struct ieee80211_ht_info).
  *	This field is only valid when the channel type is one of the HT types.
  *	This field is only valid when the channel type is one of the HT types.
+ * @cqm_rssi_thold: Connection quality monitor RSSI threshold, a zero value
+ *	implies disabled
+ * @cqm_rssi_hyst: Connection quality monitor RSSI hysteresis
  */
  */
 struct ieee80211_bss_conf {
 struct ieee80211_bss_conf {
 	const u8 *bssid;
 	const u8 *bssid;
@@ -202,6 +207,8 @@ struct ieee80211_bss_conf {
 	u64 timestamp;
 	u64 timestamp;
 	u32 basic_rates;
 	u32 basic_rates;
 	u16 ht_operation_mode;
 	u16 ht_operation_mode;
+	s32 cqm_rssi_thold;
+	u32 cqm_rssi_hyst;
 };
 };
 
 
 /**
 /**
@@ -954,6 +961,17 @@ enum ieee80211_tkip_key_type {
  *	Hardware can provide ack status reports of Tx frames to
  *	Hardware can provide ack status reports of Tx frames to
  *	the stack.
  *	the stack.
  *
  *
+ * @IEEE80211_HW_CONNECTION_MONITOR:
+ *      The hardware performs its own connection monitoring, including
+ *      periodic keep-alives to the AP and probing the AP on beacon loss.
+ *      When this flag is set, signaling beacon-loss will cause an immediate
+ *      change to disassociated state.
+ *
+ * @IEEE80211_HW_SUPPORTS_CQM_RSSI:
+ *	Hardware can do connection quality monitoring - i.e. it can monitor
+ *	connection quality related parameters, such as the RSSI level and
+ *	provide notifications if configured trigger levels are reached.
+ *
  */
  */
 enum ieee80211_hw_flags {
 enum ieee80211_hw_flags {
 	IEEE80211_HW_HAS_RATE_CONTROL			= 1<<0,
 	IEEE80211_HW_HAS_RATE_CONTROL			= 1<<0,
@@ -975,6 +993,8 @@ enum ieee80211_hw_flags {
 	IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS		= 1<<16,
 	IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS		= 1<<16,
 	IEEE80211_HW_SUPPORTS_UAPSD			= 1<<17,
 	IEEE80211_HW_SUPPORTS_UAPSD			= 1<<17,
 	IEEE80211_HW_REPORTS_TX_ACK_STATUS		= 1<<18,
 	IEEE80211_HW_REPORTS_TX_ACK_STATUS		= 1<<18,
+	IEEE80211_HW_CONNECTION_MONITOR			= 1<<19,
+	IEEE80211_HW_SUPPORTS_CQM_RSSI			= 1<<20,
 };
 };
 
 
 /**
 /**
@@ -2364,12 +2384,42 @@ void ieee80211_sta_block_awake(struct ieee80211_hw *hw,
  *
  *
  * @vif: &struct ieee80211_vif pointer from the add_interface callback.
  * @vif: &struct ieee80211_vif pointer from the add_interface callback.
  *
  *
- * When beacon filtering is enabled with IEEE80211_HW_BEACON_FILTERING and
- * IEEE80211_CONF_PS is set, the driver needs to inform whenever the
+ * When beacon filtering is enabled with %IEEE80211_HW_BEACON_FILTERING and
+ * %IEEE80211_CONF_PS is set, the driver needs to inform whenever the
  * hardware is not receiving beacons with this function.
  * hardware is not receiving beacons with this function.
  */
  */
 void ieee80211_beacon_loss(struct ieee80211_vif *vif);
 void ieee80211_beacon_loss(struct ieee80211_vif *vif);
 
 
+/**
+ * ieee80211_connection_loss - inform hardware has lost connection to the AP
+ *
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ *
+ * When beacon filtering is enabled with %IEEE80211_HW_BEACON_FILTERING, and
+ * %IEEE80211_CONF_PS and %IEEE80211_HW_CONNECTION_MONITOR are set, the driver
+ * needs to inform if the connection to the AP has been lost.
+ *
+ * This function will cause immediate change to disassociated state,
+ * without connection recovery attempts.
+ */
+void ieee80211_connection_loss(struct ieee80211_vif *vif);
+
+/**
+ * ieee80211_cqm_rssi_notify - inform a configured connection quality monitoring
+ *	rssi threshold triggered
+ *
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ * @rssi_event: the RSSI trigger event type
+ * @gfp: context flags
+ *
+ * When the %IEEE80211_HW_SUPPORTS_CQM_RSSI is set, and a connection quality
+ * monitoring is configured with an rssi threshold, the driver will inform
+ * whenever the rssi level reaches the threshold.
+ */
+void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif,
+			       enum nl80211_cqm_rssi_threshold_event rssi_event,
+			       gfp_t gfp);
+
 /* Rate control API */
 /* Rate control API */
 
 
 /**
 /**

+ 27 - 0
net/mac80211/cfg.c

@@ -1402,6 +1402,32 @@ static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,
 	return 0;
 	return 0;
 }
 }
 
 
+static int ieee80211_set_cqm_rssi_config(struct wiphy *wiphy,
+					 struct net_device *dev,
+					 s32 rssi_thold, u32 rssi_hyst)
+{
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+	struct ieee80211_vif *vif = &sdata->vif;
+	struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
+
+	if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI))
+		return -EOPNOTSUPP;
+
+	if (rssi_thold == bss_conf->cqm_rssi_thold &&
+	    rssi_hyst == bss_conf->cqm_rssi_hyst)
+		return 0;
+
+	bss_conf->cqm_rssi_thold = rssi_thold;
+	bss_conf->cqm_rssi_hyst = rssi_hyst;
+
+	/* tell the driver upon association, unless already associated */
+	if (sdata->u.mgd.associated)
+		ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_CQM);
+
+	return 0;
+}
+
 static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,
 static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,
 				      struct net_device *dev,
 				      struct net_device *dev,
 				      const u8 *addr,
 				      const u8 *addr,
@@ -1506,4 +1532,5 @@ struct cfg80211_ops mac80211_config_ops = {
 	.remain_on_channel = ieee80211_remain_on_channel,
 	.remain_on_channel = ieee80211_remain_on_channel,
 	.cancel_remain_on_channel = ieee80211_cancel_remain_on_channel,
 	.cancel_remain_on_channel = ieee80211_cancel_remain_on_channel,
 	.action = ieee80211_action,
 	.action = ieee80211_action,
+	.set_cqm_rssi_config = ieee80211_set_cqm_rssi_config,
 };
 };

+ 2 - 2
net/mac80211/ieee80211_i.h

@@ -327,7 +327,7 @@ struct ieee80211_if_managed {
 	struct work_struct work;
 	struct work_struct work;
 	struct work_struct monitor_work;
 	struct work_struct monitor_work;
 	struct work_struct chswitch_work;
 	struct work_struct chswitch_work;
-	struct work_struct beacon_loss_work;
+	struct work_struct beacon_connection_loss_work;
 
 
 	unsigned long probe_timeout;
 	unsigned long probe_timeout;
 	int probe_send_count;
 	int probe_send_count;
@@ -1156,7 +1156,7 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local,
 			     int powersave);
 			     int powersave);
 void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
 void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
 			     struct ieee80211_hdr *hdr);
 			     struct ieee80211_hdr *hdr);
-void ieee80211_beacon_loss_work(struct work_struct *work);
+void ieee80211_beacon_connection_loss_work(struct work_struct *work);
 
 
 void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
 void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
 				     enum queue_stop_reason reason);
 				     enum queue_stop_reason reason);

+ 1 - 1
net/mac80211/iface.c

@@ -486,7 +486,7 @@ static int ieee80211_stop(struct net_device *dev)
 		cancel_work_sync(&sdata->u.mgd.work);
 		cancel_work_sync(&sdata->u.mgd.work);
 		cancel_work_sync(&sdata->u.mgd.chswitch_work);
 		cancel_work_sync(&sdata->u.mgd.chswitch_work);
 		cancel_work_sync(&sdata->u.mgd.monitor_work);
 		cancel_work_sync(&sdata->u.mgd.monitor_work);
-		cancel_work_sync(&sdata->u.mgd.beacon_loss_work);
+		cancel_work_sync(&sdata->u.mgd.beacon_connection_loss_work);
 
 
 		/*
 		/*
 		 * When we get here, the interface is marked down.
 		 * When we get here, the interface is marked down.

+ 72 - 7
net/mac80211/mlme.c

@@ -753,6 +753,11 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
 	/* And the BSSID changed - we're associated now */
 	/* And the BSSID changed - we're associated now */
 	bss_info_changed |= BSS_CHANGED_BSSID;
 	bss_info_changed |= BSS_CHANGED_BSSID;
 
 
+	/* Tell the driver to monitor connection quality (if supported) */
+	if ((local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI) &&
+	    sdata->vif.bss_conf.cqm_rssi_thold)
+		bss_info_changed |= BSS_CHANGED_CQM;
+
 	ieee80211_bss_info_change_notify(sdata, bss_info_changed);
 	ieee80211_bss_info_change_notify(sdata, bss_info_changed);
 
 
 	mutex_lock(&local->iflist_mtx);
 	mutex_lock(&local->iflist_mtx);
@@ -854,6 +859,9 @@ void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
 	if (is_multicast_ether_addr(hdr->addr1))
 	if (is_multicast_ether_addr(hdr->addr1))
 		return;
 		return;
 
 
+	if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
+		return;
+
 	mod_timer(&sdata->u.mgd.conn_mon_timer,
 	mod_timer(&sdata->u.mgd.conn_mon_timer,
 		  round_jiffies_up(jiffies + IEEE80211_CONNECTION_IDLE_TIME));
 		  round_jiffies_up(jiffies + IEEE80211_CONNECTION_IDLE_TIME));
 }
 }
@@ -931,23 +939,68 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
 	mutex_unlock(&ifmgd->mtx);
 	mutex_unlock(&ifmgd->mtx);
 }
 }
 
 
-void ieee80211_beacon_loss_work(struct work_struct *work)
+static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata)
+{
+	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+	struct ieee80211_local *local = sdata->local;
+	u8 bssid[ETH_ALEN];
+
+	mutex_lock(&ifmgd->mtx);
+	if (!ifmgd->associated) {
+		mutex_unlock(&ifmgd->mtx);
+		return;
+	}
+
+	memcpy(bssid, ifmgd->associated->bssid, ETH_ALEN);
+
+	printk(KERN_DEBUG "Connection to AP %pM lost.\n", bssid);
+
+	ieee80211_set_disassoc(sdata);
+	ieee80211_recalc_idle(local);
+	mutex_unlock(&ifmgd->mtx);
+	/*
+	 * must be outside lock due to cfg80211,
+	 * but that's not a problem.
+	 */
+	ieee80211_send_deauth_disassoc(sdata, bssid,
+				       IEEE80211_STYPE_DEAUTH,
+				       WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
+				       NULL);
+}
+
+void ieee80211_beacon_connection_loss_work(struct work_struct *work)
 {
 {
 	struct ieee80211_sub_if_data *sdata =
 	struct ieee80211_sub_if_data *sdata =
 		container_of(work, struct ieee80211_sub_if_data,
 		container_of(work, struct ieee80211_sub_if_data,
-			     u.mgd.beacon_loss_work);
+			     u.mgd.beacon_connection_loss_work);
 
 
-	ieee80211_mgd_probe_ap(sdata, true);
+	if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
+		__ieee80211_connection_loss(sdata);
+	else
+		ieee80211_mgd_probe_ap(sdata, true);
 }
 }
 
 
 void ieee80211_beacon_loss(struct ieee80211_vif *vif)
 void ieee80211_beacon_loss(struct ieee80211_vif *vif)
 {
 {
 	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
 	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+	struct ieee80211_hw *hw = &sdata->local->hw;
 
 
-	ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.beacon_loss_work);
+	WARN_ON(hw->flags & IEEE80211_HW_CONNECTION_MONITOR);
+	ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work);
 }
 }
 EXPORT_SYMBOL(ieee80211_beacon_loss);
 EXPORT_SYMBOL(ieee80211_beacon_loss);
 
 
+void ieee80211_connection_loss(struct ieee80211_vif *vif)
+{
+	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+	struct ieee80211_hw *hw = &sdata->local->hw;
+
+	WARN_ON(!(hw->flags & IEEE80211_HW_CONNECTION_MONITOR));
+	ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work);
+}
+EXPORT_SYMBOL(ieee80211_connection_loss);
+
+
 static enum rx_mgmt_action __must_check
 static enum rx_mgmt_action __must_check
 ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
 ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
 			 struct ieee80211_mgmt *mgmt, size_t len)
 			 struct ieee80211_mgmt *mgmt, size_t len)
@@ -1637,7 +1690,8 @@ static void ieee80211_sta_bcn_mon_timer(unsigned long data)
 	if (local->quiescing)
 	if (local->quiescing)
 		return;
 		return;
 
 
-	ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.beacon_loss_work);
+	ieee80211_queue_work(&sdata->local->hw,
+			     &sdata->u.mgd.beacon_connection_loss_work);
 }
 }
 
 
 static void ieee80211_sta_conn_mon_timer(unsigned long data)
 static void ieee80211_sta_conn_mon_timer(unsigned long data)
@@ -1689,7 +1743,7 @@ void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata)
 	 */
 	 */
 
 
 	cancel_work_sync(&ifmgd->work);
 	cancel_work_sync(&ifmgd->work);
-	cancel_work_sync(&ifmgd->beacon_loss_work);
+	cancel_work_sync(&ifmgd->beacon_connection_loss_work);
 	if (del_timer_sync(&ifmgd->timer))
 	if (del_timer_sync(&ifmgd->timer))
 		set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running);
 		set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running);
 
 
@@ -1723,7 +1777,8 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
 	INIT_WORK(&ifmgd->work, ieee80211_sta_work);
 	INIT_WORK(&ifmgd->work, ieee80211_sta_work);
 	INIT_WORK(&ifmgd->monitor_work, ieee80211_sta_monitor_work);
 	INIT_WORK(&ifmgd->monitor_work, ieee80211_sta_monitor_work);
 	INIT_WORK(&ifmgd->chswitch_work, ieee80211_chswitch_work);
 	INIT_WORK(&ifmgd->chswitch_work, ieee80211_chswitch_work);
-	INIT_WORK(&ifmgd->beacon_loss_work, ieee80211_beacon_loss_work);
+	INIT_WORK(&ifmgd->beacon_connection_loss_work,
+		  ieee80211_beacon_connection_loss_work);
 	setup_timer(&ifmgd->timer, ieee80211_sta_timer,
 	setup_timer(&ifmgd->timer, ieee80211_sta_timer,
 		    (unsigned long) sdata);
 		    (unsigned long) sdata);
 	setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer,
 	setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer,
@@ -2135,3 +2190,13 @@ int ieee80211_mgd_action(struct ieee80211_sub_if_data *sdata,
 	*cookie = (unsigned long) skb;
 	*cookie = (unsigned long) skb;
 	return 0;
 	return 0;
 }
 }
+
+void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif,
+			       enum nl80211_cqm_rssi_threshold_event rssi_event,
+			       gfp_t gfp)
+{
+	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+
+	cfg80211_cqm_rssi_notify(sdata->dev, rssi_event, gfp);
+}
+EXPORT_SYMBOL(ieee80211_cqm_rssi_notify);

+ 0 - 2
net/mac80211/tx.c

@@ -2010,14 +2010,12 @@ void ieee80211_tx_pending(unsigned long data)
 		while (!skb_queue_empty(&local->pending[i])) {
 		while (!skb_queue_empty(&local->pending[i])) {
 			struct sk_buff *skb = __skb_dequeue(&local->pending[i]);
 			struct sk_buff *skb = __skb_dequeue(&local->pending[i]);
 			struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
 			struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-			struct ieee80211_sub_if_data *sdata;
 
 
 			if (WARN_ON(!info->control.vif)) {
 			if (WARN_ON(!info->control.vif)) {
 				kfree_skb(skb);
 				kfree_skb(skb);
 				continue;
 				continue;
 			}
 			}
 
 
-			sdata = vif_to_sdata(info->control.vif);
 			spin_unlock_irqrestore(&local->queue_stop_reason_lock,
 			spin_unlock_irqrestore(&local->queue_stop_reason_lock,
 						flags);
 						flags);
 
 

+ 13 - 0
net/wireless/mlme.c

@@ -894,3 +894,16 @@ void cfg80211_action_tx_status(struct net_device *dev, u64 cookie,
 	nl80211_send_action_tx_status(rdev, dev, cookie, buf, len, ack, gfp);
 	nl80211_send_action_tx_status(rdev, dev, cookie, buf, len, ack, gfp);
 }
 }
 EXPORT_SYMBOL(cfg80211_action_tx_status);
 EXPORT_SYMBOL(cfg80211_action_tx_status);
+
+void cfg80211_cqm_rssi_notify(struct net_device *dev,
+			      enum nl80211_cqm_rssi_threshold_event rssi_event,
+			      gfp_t gfp)
+{
+	struct wireless_dev *wdev = dev->ieee80211_ptr;
+	struct wiphy *wiphy = wdev->wiphy;
+	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+	/* Indicate roaming trigger event to user space */
+	nl80211_send_cqm_rssi_notify(rdev, dev, rssi_event, gfp);
+}
+EXPORT_SYMBOL(cfg80211_cqm_rssi_notify);

+ 131 - 0
net/wireless/nl80211.c

@@ -149,6 +149,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
 				 .len = IEEE80211_MAX_DATA_LEN },
 				 .len = IEEE80211_MAX_DATA_LEN },
 	[NL80211_ATTR_FRAME_MATCH] = { .type = NLA_BINARY, },
 	[NL80211_ATTR_FRAME_MATCH] = { .type = NLA_BINARY, },
 	[NL80211_ATTR_PS_STATE] = { .type = NLA_U32 },
 	[NL80211_ATTR_PS_STATE] = { .type = NLA_U32 },
+	[NL80211_ATTR_CQM] = { .type = NLA_NESTED, },
 };
 };
 
 
 /* policy for the attributes */
 /* policy for the attributes */
@@ -4778,6 +4779,84 @@ unlock_rtnl:
 	return err;
 	return err;
 }
 }
 
 
+static struct nla_policy
+nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] __read_mostly = {
+	[NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 },
+	[NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 },
+	[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
+};
+
+static int nl80211_set_cqm_rssi(struct genl_info *info,
+				s32 threshold, u32 hysteresis)
+{
+	struct cfg80211_registered_device *rdev;
+	struct wireless_dev *wdev;
+	struct net_device *dev;
+	int err;
+
+	if (threshold > 0)
+		return -EINVAL;
+
+	rtnl_lock();
+
+	err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
+	if (err)
+		goto unlock_rdev;
+
+	wdev = dev->ieee80211_ptr;
+
+	if (!rdev->ops->set_cqm_rssi_config) {
+		err = -EOPNOTSUPP;
+		goto unlock_rdev;
+	}
+
+	if (wdev->iftype != NL80211_IFTYPE_STATION) {
+		err = -EOPNOTSUPP;
+		goto unlock_rdev;
+	}
+
+	err = rdev->ops->set_cqm_rssi_config(wdev->wiphy, dev,
+					     threshold, hysteresis);
+
+unlock_rdev:
+	cfg80211_unlock_rdev(rdev);
+	dev_put(dev);
+	rtnl_unlock();
+
+	return err;
+}
+
+static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info)
+{
+	struct nlattr *attrs[NL80211_ATTR_CQM_MAX + 1];
+	struct nlattr *cqm;
+	int err;
+
+	cqm = info->attrs[NL80211_ATTR_CQM];
+	if (!cqm) {
+		err = -EINVAL;
+		goto out;
+	}
+
+	err = nla_parse_nested(attrs, NL80211_ATTR_CQM_MAX, cqm,
+			       nl80211_attr_cqm_policy);
+	if (err)
+		goto out;
+
+	if (attrs[NL80211_ATTR_CQM_RSSI_THOLD] &&
+	    attrs[NL80211_ATTR_CQM_RSSI_HYST]) {
+		s32 threshold;
+		u32 hysteresis;
+		threshold = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_THOLD]);
+		hysteresis = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]);
+		err = nl80211_set_cqm_rssi(info, threshold, hysteresis);
+	} else
+		err = -EINVAL;
+
+out:
+	return err;
+}
+
 static struct genl_ops nl80211_ops[] = {
 static struct genl_ops nl80211_ops[] = {
 	{
 	{
 		.cmd = NL80211_CMD_GET_WIPHY,
 		.cmd = NL80211_CMD_GET_WIPHY,
@@ -5082,6 +5161,12 @@ static struct genl_ops nl80211_ops[] = {
 		.policy = nl80211_policy,
 		.policy = nl80211_policy,
 		/* can be retrieved by unprivileged users */
 		/* can be retrieved by unprivileged users */
 	},
 	},
+	{
+		.cmd = NL80211_CMD_SET_CQM,
+		.doit = nl80211_set_cqm,
+		.policy = nl80211_policy,
+		.flags = GENL_ADMIN_PERM,
+	},
 };
 };
 
 
 static struct genl_multicast_group nl80211_mlme_mcgrp = {
 static struct genl_multicast_group nl80211_mlme_mcgrp = {
@@ -5832,6 +5917,52 @@ void nl80211_send_action_tx_status(struct cfg80211_registered_device *rdev,
 	nlmsg_free(msg);
 	nlmsg_free(msg);
 }
 }
 
 
+void
+nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,
+			     struct net_device *netdev,
+			     enum nl80211_cqm_rssi_threshold_event rssi_event,
+			     gfp_t gfp)
+{
+	struct sk_buff *msg;
+	struct nlattr *pinfoattr;
+	void *hdr;
+
+	msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
+	if (!msg)
+		return;
+
+	hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
+	if (!hdr) {
+		nlmsg_free(msg);
+		return;
+	}
+
+	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
+	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
+
+	pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
+	if (!pinfoattr)
+		goto nla_put_failure;
+
+	NLA_PUT_U32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
+		    rssi_event);
+
+	nla_nest_end(msg, pinfoattr);
+
+	if (genlmsg_end(msg, hdr) < 0) {
+		nlmsg_free(msg);
+		return;
+	}
+
+	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 int nl80211_netlink_notify(struct notifier_block * nb,
 static int nl80211_netlink_notify(struct notifier_block * nb,
 				  unsigned long state,
 				  unsigned long state,
 				  void *_notify)
 				  void *_notify)

+ 6 - 0
net/wireless/nl80211.h

@@ -82,4 +82,10 @@ void nl80211_send_action_tx_status(struct cfg80211_registered_device *rdev,
 				   const u8 *buf, size_t len, bool ack,
 				   const u8 *buf, size_t len, bool ack,
 				   gfp_t gfp);
 				   gfp_t gfp);
 
 
+void
+nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,
+			     struct net_device *netdev,
+			     enum nl80211_cqm_rssi_threshold_event rssi_event,
+			     gfp_t gfp);
+
 #endif /* __NET_WIRELESS_NL80211_H */
 #endif /* __NET_WIRELESS_NL80211_H */

+ 67 - 67
net/wireless/wext-core.c

@@ -28,226 +28,226 @@ typedef int (*wext_ioctl_func)(struct net_device *, struct iwreq *,
  * know about.
  * know about.
  */
  */
 static const struct iw_ioctl_description standard_ioctl[] = {
 static const struct iw_ioctl_description standard_ioctl[] = {
-	[SIOCSIWCOMMIT	- SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCSIWCOMMIT)] = {
 		.header_type	= IW_HEADER_TYPE_NULL,
 		.header_type	= IW_HEADER_TYPE_NULL,
 	},
 	},
-	[SIOCGIWNAME	- SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCGIWNAME)] = {
 		.header_type	= IW_HEADER_TYPE_CHAR,
 		.header_type	= IW_HEADER_TYPE_CHAR,
 		.flags		= IW_DESCR_FLAG_DUMP,
 		.flags		= IW_DESCR_FLAG_DUMP,
 	},
 	},
-	[SIOCSIWNWID	- SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCSIWNWID)] = {
 		.header_type	= IW_HEADER_TYPE_PARAM,
 		.header_type	= IW_HEADER_TYPE_PARAM,
 		.flags		= IW_DESCR_FLAG_EVENT,
 		.flags		= IW_DESCR_FLAG_EVENT,
 	},
 	},
-	[SIOCGIWNWID	- SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCGIWNWID)] = {
 		.header_type	= IW_HEADER_TYPE_PARAM,
 		.header_type	= IW_HEADER_TYPE_PARAM,
 		.flags		= IW_DESCR_FLAG_DUMP,
 		.flags		= IW_DESCR_FLAG_DUMP,
 	},
 	},
-	[SIOCSIWFREQ	- SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCSIWFREQ)] = {
 		.header_type	= IW_HEADER_TYPE_FREQ,
 		.header_type	= IW_HEADER_TYPE_FREQ,
 		.flags		= IW_DESCR_FLAG_EVENT,
 		.flags		= IW_DESCR_FLAG_EVENT,
 	},
 	},
-	[SIOCGIWFREQ	- SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCGIWFREQ)] = {
 		.header_type	= IW_HEADER_TYPE_FREQ,
 		.header_type	= IW_HEADER_TYPE_FREQ,
 		.flags		= IW_DESCR_FLAG_DUMP,
 		.flags		= IW_DESCR_FLAG_DUMP,
 	},
 	},
-	[SIOCSIWMODE	- SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCSIWMODE)] = {
 		.header_type	= IW_HEADER_TYPE_UINT,
 		.header_type	= IW_HEADER_TYPE_UINT,
 		.flags		= IW_DESCR_FLAG_EVENT,
 		.flags		= IW_DESCR_FLAG_EVENT,
 	},
 	},
-	[SIOCGIWMODE	- SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCGIWMODE)] = {
 		.header_type	= IW_HEADER_TYPE_UINT,
 		.header_type	= IW_HEADER_TYPE_UINT,
 		.flags		= IW_DESCR_FLAG_DUMP,
 		.flags		= IW_DESCR_FLAG_DUMP,
 	},
 	},
-	[SIOCSIWSENS	- SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCSIWSENS)] = {
 		.header_type	= IW_HEADER_TYPE_PARAM,
 		.header_type	= IW_HEADER_TYPE_PARAM,
 	},
 	},
-	[SIOCGIWSENS	- SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCGIWSENS)] = {
 		.header_type	= IW_HEADER_TYPE_PARAM,
 		.header_type	= IW_HEADER_TYPE_PARAM,
 	},
 	},
-	[SIOCSIWRANGE	- SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCSIWRANGE)] = {
 		.header_type	= IW_HEADER_TYPE_NULL,
 		.header_type	= IW_HEADER_TYPE_NULL,
 	},
 	},
-	[SIOCGIWRANGE	- SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCGIWRANGE)] = {
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.token_size	= 1,
 		.token_size	= 1,
 		.max_tokens	= sizeof(struct iw_range),
 		.max_tokens	= sizeof(struct iw_range),
 		.flags		= IW_DESCR_FLAG_DUMP,
 		.flags		= IW_DESCR_FLAG_DUMP,
 	},
 	},
-	[SIOCSIWPRIV	- SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCSIWPRIV)] = {
 		.header_type	= IW_HEADER_TYPE_NULL,
 		.header_type	= IW_HEADER_TYPE_NULL,
 	},
 	},
-	[SIOCGIWPRIV	- SIOCIWFIRST] = { /* (handled directly by us) */
+	[IW_IOCTL_IDX(SIOCGIWPRIV)] = { /* (handled directly by us) */
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.token_size	= sizeof(struct iw_priv_args),
 		.token_size	= sizeof(struct iw_priv_args),
 		.max_tokens	= 16,
 		.max_tokens	= 16,
 		.flags		= IW_DESCR_FLAG_NOMAX,
 		.flags		= IW_DESCR_FLAG_NOMAX,
 	},
 	},
-	[SIOCSIWSTATS	- SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCSIWSTATS)] = {
 		.header_type	= IW_HEADER_TYPE_NULL,
 		.header_type	= IW_HEADER_TYPE_NULL,
 	},
 	},
-	[SIOCGIWSTATS	- SIOCIWFIRST] = { /* (handled directly by us) */
+	[IW_IOCTL_IDX(SIOCGIWSTATS)] = { /* (handled directly by us) */
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.token_size	= 1,
 		.token_size	= 1,
 		.max_tokens	= sizeof(struct iw_statistics),
 		.max_tokens	= sizeof(struct iw_statistics),
 		.flags		= IW_DESCR_FLAG_DUMP,
 		.flags		= IW_DESCR_FLAG_DUMP,
 	},
 	},
-	[SIOCSIWSPY	- SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCSIWSPY)] = {
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.token_size	= sizeof(struct sockaddr),
 		.token_size	= sizeof(struct sockaddr),
 		.max_tokens	= IW_MAX_SPY,
 		.max_tokens	= IW_MAX_SPY,
 	},
 	},
-	[SIOCGIWSPY	- SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCGIWSPY)] = {
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.token_size	= sizeof(struct sockaddr) +
 		.token_size	= sizeof(struct sockaddr) +
 				  sizeof(struct iw_quality),
 				  sizeof(struct iw_quality),
 		.max_tokens	= IW_MAX_SPY,
 		.max_tokens	= IW_MAX_SPY,
 	},
 	},
-	[SIOCSIWTHRSPY	- SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCSIWTHRSPY)] = {
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.token_size	= sizeof(struct iw_thrspy),
 		.token_size	= sizeof(struct iw_thrspy),
 		.min_tokens	= 1,
 		.min_tokens	= 1,
 		.max_tokens	= 1,
 		.max_tokens	= 1,
 	},
 	},
-	[SIOCGIWTHRSPY	- SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCGIWTHRSPY)] = {
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.token_size	= sizeof(struct iw_thrspy),
 		.token_size	= sizeof(struct iw_thrspy),
 		.min_tokens	= 1,
 		.min_tokens	= 1,
 		.max_tokens	= 1,
 		.max_tokens	= 1,
 	},
 	},
-	[SIOCSIWAP	- SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCSIWAP)] = {
 		.header_type	= IW_HEADER_TYPE_ADDR,
 		.header_type	= IW_HEADER_TYPE_ADDR,
 	},
 	},
-	[SIOCGIWAP	- SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCGIWAP)] = {
 		.header_type	= IW_HEADER_TYPE_ADDR,
 		.header_type	= IW_HEADER_TYPE_ADDR,
 		.flags		= IW_DESCR_FLAG_DUMP,
 		.flags		= IW_DESCR_FLAG_DUMP,
 	},
 	},
-	[SIOCSIWMLME	- SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCSIWMLME)] = {
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.token_size	= 1,
 		.token_size	= 1,
 		.min_tokens	= sizeof(struct iw_mlme),
 		.min_tokens	= sizeof(struct iw_mlme),
 		.max_tokens	= sizeof(struct iw_mlme),
 		.max_tokens	= sizeof(struct iw_mlme),
 	},
 	},
-	[SIOCGIWAPLIST	- SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCGIWAPLIST)] = {
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.token_size	= sizeof(struct sockaddr) +
 		.token_size	= sizeof(struct sockaddr) +
 				  sizeof(struct iw_quality),
 				  sizeof(struct iw_quality),
 		.max_tokens	= IW_MAX_AP,
 		.max_tokens	= IW_MAX_AP,
 		.flags		= IW_DESCR_FLAG_NOMAX,
 		.flags		= IW_DESCR_FLAG_NOMAX,
 	},
 	},
-	[SIOCSIWSCAN	- SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCSIWSCAN)] = {
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.token_size	= 1,
 		.token_size	= 1,
 		.min_tokens	= 0,
 		.min_tokens	= 0,
 		.max_tokens	= sizeof(struct iw_scan_req),
 		.max_tokens	= sizeof(struct iw_scan_req),
 	},
 	},
-	[SIOCGIWSCAN	- SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCGIWSCAN)] = {
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.token_size	= 1,
 		.token_size	= 1,
 		.max_tokens	= IW_SCAN_MAX_DATA,
 		.max_tokens	= IW_SCAN_MAX_DATA,
 		.flags		= IW_DESCR_FLAG_NOMAX,
 		.flags		= IW_DESCR_FLAG_NOMAX,
 	},
 	},
-	[SIOCSIWESSID	- SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCSIWESSID)] = {
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.token_size	= 1,
 		.token_size	= 1,
 		.max_tokens	= IW_ESSID_MAX_SIZE,
 		.max_tokens	= IW_ESSID_MAX_SIZE,
 		.flags		= IW_DESCR_FLAG_EVENT,
 		.flags		= IW_DESCR_FLAG_EVENT,
 	},
 	},
-	[SIOCGIWESSID	- SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCGIWESSID)] = {
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.token_size	= 1,
 		.token_size	= 1,
 		.max_tokens	= IW_ESSID_MAX_SIZE,
 		.max_tokens	= IW_ESSID_MAX_SIZE,
 		.flags		= IW_DESCR_FLAG_DUMP,
 		.flags		= IW_DESCR_FLAG_DUMP,
 	},
 	},
-	[SIOCSIWNICKN	- SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCSIWNICKN)] = {
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.token_size	= 1,
 		.token_size	= 1,
 		.max_tokens	= IW_ESSID_MAX_SIZE,
 		.max_tokens	= IW_ESSID_MAX_SIZE,
 	},
 	},
-	[SIOCGIWNICKN	- SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCGIWNICKN)] = {
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.token_size	= 1,
 		.token_size	= 1,
 		.max_tokens	= IW_ESSID_MAX_SIZE,
 		.max_tokens	= IW_ESSID_MAX_SIZE,
 	},
 	},
-	[SIOCSIWRATE	- SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCSIWRATE)] = {
 		.header_type	= IW_HEADER_TYPE_PARAM,
 		.header_type	= IW_HEADER_TYPE_PARAM,
 	},
 	},
-	[SIOCGIWRATE	- SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCGIWRATE)] = {
 		.header_type	= IW_HEADER_TYPE_PARAM,
 		.header_type	= IW_HEADER_TYPE_PARAM,
 	},
 	},
-	[SIOCSIWRTS	- SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCSIWRTS)] = {
 		.header_type	= IW_HEADER_TYPE_PARAM,
 		.header_type	= IW_HEADER_TYPE_PARAM,
 	},
 	},
-	[SIOCGIWRTS	- SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCGIWRTS)] = {
 		.header_type	= IW_HEADER_TYPE_PARAM,
 		.header_type	= IW_HEADER_TYPE_PARAM,
 	},
 	},
-	[SIOCSIWFRAG	- SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCSIWFRAG)] = {
 		.header_type	= IW_HEADER_TYPE_PARAM,
 		.header_type	= IW_HEADER_TYPE_PARAM,
 	},
 	},
-	[SIOCGIWFRAG	- SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCGIWFRAG)] = {
 		.header_type	= IW_HEADER_TYPE_PARAM,
 		.header_type	= IW_HEADER_TYPE_PARAM,
 	},
 	},
-	[SIOCSIWTXPOW	- SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCSIWTXPOW)] = {
 		.header_type	= IW_HEADER_TYPE_PARAM,
 		.header_type	= IW_HEADER_TYPE_PARAM,
 	},
 	},
-	[SIOCGIWTXPOW	- SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCGIWTXPOW)] = {
 		.header_type	= IW_HEADER_TYPE_PARAM,
 		.header_type	= IW_HEADER_TYPE_PARAM,
 	},
 	},
-	[SIOCSIWRETRY	- SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCSIWRETRY)] = {
 		.header_type	= IW_HEADER_TYPE_PARAM,
 		.header_type	= IW_HEADER_TYPE_PARAM,
 	},
 	},
-	[SIOCGIWRETRY	- SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCGIWRETRY)] = {
 		.header_type	= IW_HEADER_TYPE_PARAM,
 		.header_type	= IW_HEADER_TYPE_PARAM,
 	},
 	},
-	[SIOCSIWENCODE	- SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCSIWENCODE)] = {
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.token_size	= 1,
 		.token_size	= 1,
 		.max_tokens	= IW_ENCODING_TOKEN_MAX,
 		.max_tokens	= IW_ENCODING_TOKEN_MAX,
 		.flags		= IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT,
 		.flags		= IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT,
 	},
 	},
-	[SIOCGIWENCODE	- SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCGIWENCODE)] = {
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.token_size	= 1,
 		.token_size	= 1,
 		.max_tokens	= IW_ENCODING_TOKEN_MAX,
 		.max_tokens	= IW_ENCODING_TOKEN_MAX,
 		.flags		= IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT,
 		.flags		= IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT,
 	},
 	},
-	[SIOCSIWPOWER	- SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCSIWPOWER)] = {
 		.header_type	= IW_HEADER_TYPE_PARAM,
 		.header_type	= IW_HEADER_TYPE_PARAM,
 	},
 	},
-	[SIOCGIWPOWER	- SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCGIWPOWER)] = {
 		.header_type	= IW_HEADER_TYPE_PARAM,
 		.header_type	= IW_HEADER_TYPE_PARAM,
 	},
 	},
-	[SIOCSIWGENIE	- SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCSIWGENIE)] = {
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.token_size	= 1,
 		.token_size	= 1,
 		.max_tokens	= IW_GENERIC_IE_MAX,
 		.max_tokens	= IW_GENERIC_IE_MAX,
 	},
 	},
-	[SIOCGIWGENIE	- SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCGIWGENIE)] = {
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.token_size	= 1,
 		.token_size	= 1,
 		.max_tokens	= IW_GENERIC_IE_MAX,
 		.max_tokens	= IW_GENERIC_IE_MAX,
 	},
 	},
-	[SIOCSIWAUTH	- SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCSIWAUTH)] = {
 		.header_type	= IW_HEADER_TYPE_PARAM,
 		.header_type	= IW_HEADER_TYPE_PARAM,
 	},
 	},
-	[SIOCGIWAUTH	- SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCGIWAUTH)] = {
 		.header_type	= IW_HEADER_TYPE_PARAM,
 		.header_type	= IW_HEADER_TYPE_PARAM,
 	},
 	},
-	[SIOCSIWENCODEEXT - SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCSIWENCODEEXT)] = {
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.token_size	= 1,
 		.token_size	= 1,
 		.min_tokens	= sizeof(struct iw_encode_ext),
 		.min_tokens	= sizeof(struct iw_encode_ext),
 		.max_tokens	= sizeof(struct iw_encode_ext) +
 		.max_tokens	= sizeof(struct iw_encode_ext) +
 				  IW_ENCODING_TOKEN_MAX,
 				  IW_ENCODING_TOKEN_MAX,
 	},
 	},
-	[SIOCGIWENCODEEXT - SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCGIWENCODEEXT)] = {
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.token_size	= 1,
 		.token_size	= 1,
 		.min_tokens	= sizeof(struct iw_encode_ext),
 		.min_tokens	= sizeof(struct iw_encode_ext),
 		.max_tokens	= sizeof(struct iw_encode_ext) +
 		.max_tokens	= sizeof(struct iw_encode_ext) +
 				  IW_ENCODING_TOKEN_MAX,
 				  IW_ENCODING_TOKEN_MAX,
 	},
 	},
-	[SIOCSIWPMKSA - SIOCIWFIRST] = {
+	[IW_IOCTL_IDX(SIOCSIWPMKSA)] = {
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.token_size	= 1,
 		.token_size	= 1,
 		.min_tokens	= sizeof(struct iw_pmksa),
 		.min_tokens	= sizeof(struct iw_pmksa),
@@ -261,44 +261,44 @@ static const unsigned standard_ioctl_num = ARRAY_SIZE(standard_ioctl);
  * we know about.
  * we know about.
  */
  */
 static const struct iw_ioctl_description standard_event[] = {
 static const struct iw_ioctl_description standard_event[] = {
-	[IWEVTXDROP	- IWEVFIRST] = {
+	[IW_EVENT_IDX(IWEVTXDROP)] = {
 		.header_type	= IW_HEADER_TYPE_ADDR,
 		.header_type	= IW_HEADER_TYPE_ADDR,
 	},
 	},
-	[IWEVQUAL	- IWEVFIRST] = {
+	[IW_EVENT_IDX(IWEVQUAL)] = {
 		.header_type	= IW_HEADER_TYPE_QUAL,
 		.header_type	= IW_HEADER_TYPE_QUAL,
 	},
 	},
-	[IWEVCUSTOM	- IWEVFIRST] = {
+	[IW_EVENT_IDX(IWEVCUSTOM)] = {
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.token_size	= 1,
 		.token_size	= 1,
 		.max_tokens	= IW_CUSTOM_MAX,
 		.max_tokens	= IW_CUSTOM_MAX,
 	},
 	},
-	[IWEVREGISTERED	- IWEVFIRST] = {
+	[IW_EVENT_IDX(IWEVREGISTERED)] = {
 		.header_type	= IW_HEADER_TYPE_ADDR,
 		.header_type	= IW_HEADER_TYPE_ADDR,
 	},
 	},
-	[IWEVEXPIRED	- IWEVFIRST] = {
+	[IW_EVENT_IDX(IWEVEXPIRED)] = {
 		.header_type	= IW_HEADER_TYPE_ADDR,
 		.header_type	= IW_HEADER_TYPE_ADDR,
 	},
 	},
-	[IWEVGENIE	- IWEVFIRST] = {
+	[IW_EVENT_IDX(IWEVGENIE)] = {
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.token_size	= 1,
 		.token_size	= 1,
 		.max_tokens	= IW_GENERIC_IE_MAX,
 		.max_tokens	= IW_GENERIC_IE_MAX,
 	},
 	},
-	[IWEVMICHAELMICFAILURE	- IWEVFIRST] = {
+	[IW_EVENT_IDX(IWEVMICHAELMICFAILURE)] = {
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.token_size	= 1,
 		.token_size	= 1,
 		.max_tokens	= sizeof(struct iw_michaelmicfailure),
 		.max_tokens	= sizeof(struct iw_michaelmicfailure),
 	},
 	},
-	[IWEVASSOCREQIE	- IWEVFIRST] = {
+	[IW_EVENT_IDX(IWEVASSOCREQIE)] = {
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.token_size	= 1,
 		.token_size	= 1,
 		.max_tokens	= IW_GENERIC_IE_MAX,
 		.max_tokens	= IW_GENERIC_IE_MAX,
 	},
 	},
-	[IWEVASSOCRESPIE	- IWEVFIRST] = {
+	[IW_EVENT_IDX(IWEVASSOCRESPIE)] = {
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.token_size	= 1,
 		.token_size	= 1,
 		.max_tokens	= IW_GENERIC_IE_MAX,
 		.max_tokens	= IW_GENERIC_IE_MAX,
 	},
 	},
-	[IWEVPMKIDCAND	- IWEVFIRST] = {
+	[IW_EVENT_IDX(IWEVPMKIDCAND)] = {
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.header_type	= IW_HEADER_TYPE_POINT,
 		.token_size	= 1,
 		.token_size	= 1,
 		.max_tokens	= sizeof(struct iw_pmkid_cand),
 		.max_tokens	= sizeof(struct iw_pmkid_cand),
@@ -449,11 +449,11 @@ void wireless_send_event(struct net_device *	dev,
 
 
 	/* Get the description of the Event */
 	/* Get the description of the Event */
 	if (cmd <= SIOCIWLAST) {
 	if (cmd <= SIOCIWLAST) {
-		cmd_index = cmd - SIOCIWFIRST;
+		cmd_index = IW_IOCTL_IDX(cmd);
 		if (cmd_index < standard_ioctl_num)
 		if (cmd_index < standard_ioctl_num)
 			descr = &(standard_ioctl[cmd_index]);
 			descr = &(standard_ioctl[cmd_index]);
 	} else {
 	} else {
-		cmd_index = cmd - IWEVFIRST;
+		cmd_index = IW_EVENT_IDX(cmd);
 		if (cmd_index < standard_event_num)
 		if (cmd_index < standard_event_num)
 			descr = &(standard_event[cmd_index]);
 			descr = &(standard_event[cmd_index]);
 	}
 	}
@@ -662,7 +662,7 @@ static iw_handler get_handler(struct net_device *dev, unsigned int cmd)
 		return NULL;
 		return NULL;
 
 
 	/* Try as a standard command */
 	/* Try as a standard command */
-	index = cmd - SIOCIWFIRST;
+	index = IW_IOCTL_IDX(cmd);
 	if (index < handlers->num_standard)
 	if (index < handlers->num_standard)
 		return handlers->standard[index];
 		return handlers->standard[index];
 
 
@@ -954,9 +954,9 @@ static int ioctl_standard_call(struct net_device *	dev,
 	int					ret = -EINVAL;
 	int					ret = -EINVAL;
 
 
 	/* Get the description of the IOCTL */
 	/* Get the description of the IOCTL */
-	if ((cmd - SIOCIWFIRST) >= standard_ioctl_num)
+	if (IW_IOCTL_IDX(cmd) >= standard_ioctl_num)
 		return -EOPNOTSUPP;
 		return -EOPNOTSUPP;
-	descr = &(standard_ioctl[cmd - SIOCIWFIRST]);
+	descr = &(standard_ioctl[IW_IOCTL_IDX(cmd)]);
 
 
 	/* Check if we have a pointer to user space data or not */
 	/* Check if we have a pointer to user space data or not */
 	if (descr->header_type != IW_HEADER_TYPE_POINT) {
 	if (descr->header_type != IW_HEADER_TYPE_POINT) {
@@ -1012,7 +1012,7 @@ static int compat_standard_call(struct net_device	*dev,
 	struct iw_point iwp;
 	struct iw_point iwp;
 	int err;
 	int err;
 
 
-	descr = standard_ioctl + (cmd - SIOCIWFIRST);
+	descr = standard_ioctl + IW_IOCTL_IDX(cmd);
 
 
 	if (descr->header_type != IW_HEADER_TYPE_POINT)
 	if (descr->header_type != IW_HEADER_TYPE_POINT)
 		return ioctl_standard_call(dev, iwr, cmd, info, handler);
 		return ioctl_standard_call(dev, iwr, cmd, info, handler);