Linux wireless drivers development
 help / color / mirror / Atom feed
* Re: [PATCH 03/16] wl1251: add sysfs interface for bluetooth coexistence mode configuration
From: Luca Coelho @ 2013-10-29  7:09 UTC (permalink / raw)
  To: Ben Hutchings
  Cc: Pali Rohár, John W. Linville, Johannes Berg, David S. Miller,
	linux-wireless, netdev, linux-kernel, freemangordon,
	aaro.koskinen, pavel, sre, joni.lapilainen, David Gnedt
In-Reply-To: <1383003587.3779.49.camel@bwh-desktop.uk.level5networks.com>

On Mon, 2013-10-28 at 23:39 +0000, Ben Hutchings wrote:
> On Sat, 2013-10-26 at 22:34 +0200, Pali Rohár wrote:
> > From: David Gnedt <david.gnedt@davizone.at>
> > 
> > Port the bt_coex_mode sysfs interface from wl1251 driver version included
> > in the Maemo Fremantle kernel to allow bt-coexistence mode configuration.
> > This enables userspace applications to set one of the modes
> > WL1251_BT_COEX_OFF, WL1251_BT_COEX_ENABLE and WL1251_BT_COEX_MONOAUDIO.
> > The default mode is WL1251_BT_COEX_OFF.
> > It should be noted that this driver always enabled bt-coexistence before
> > and enabled bt-coexistence directly affects the receiving performance,
> > rendering it unusable in some low-signal situations. Especially monitor
> > mode is affected very badly with bt-coexistence enabled.
> [...]
> 
> This should be implemented consistently with other drivers:
> 
> drivers/net/wireless/ath/ath9k/htc_drv_init.c:module_param_named(btcoex_enable, ath9k_htc_btcoex_enable, int, 0444);
> drivers/net/wireless/ath/ath9k/init.c:module_param_named(btcoex_enable, ath9k_btcoex_enable, int, 0444);
> drivers/net/wireless/b43/main.c:module_param_named(btcoex, modparam_btcoex, int, 0444);
> drivers/net/wireless/ipw2x00/ipw2200.c:module_param(bt_coexist, int, 0444);
> drivers/net/wireless/iwlegacy/common.c:module_param(bt_coex_active, bool, S_IRUGO);
> drivers/net/wireless/iwlwifi/iwl-drv.c:module_param_named(bt_coex_active, iwlwifi_mod_params.bt_coex_active,
> drivers/net/wireless/ti/wlcore/sysfs.c:static DEVICE_ATTR(bt_coex_state, S_IRUGO | S_IWUSR,
> 
> Oh, hmm, I see a problem here.

With so many drivers doing the same thing, isn't it about time to add
this to nl80211?

--
Luca.


^ permalink raw reply

* Re: iwlegacy (4965) - what would 0x8000 as the completed TX rate indicate?
From: Johannes Berg @ 2013-10-29  8:03 UTC (permalink / raw)
  To: Adrian Chadd; +Cc: linux-wireless@vger.kernel.org
In-Reply-To: <CAJ-Vmomsv52xc6-rwaBy9aZUsN3BaPeRTDBu8+bn2ov_Asb=1A@mail.gmail.com>

On Mon, 2013-10-28 at 12:24 -0700, Adrian Chadd wrote:

> >> So, any ideas what that 0x8000 rate in the rate completion means?
> >
> > I think that just means it used the other antenna and rate 0, since the
> > bits 0x1c000 contain the antenna bitmap that was used (up to three can
> > be used for each transmission)
> 
> Well, what's rate=0 mean?
> 
> And 0x8000 is bit 11 set, which is HT40. I definitely don't have HT40 enabled.

0x8000 is bit 15 set, which means "antenna B", I think?

But then I can't seem to figure out what rate=0 means either. Should be
an OFDM rate (bit 9 not set) but 0 isn't a valid OFDM rate value. Hmm.

johannes


^ permalink raw reply

* Re: [PATCH 4/4] wl1251: spi: add device tree support
From: Kumar Gala @ 2013-10-29  8:28 UTC (permalink / raw)
  To: Tomasz Figa
  Cc: linux-arm-kernel, Sebastian Reichel, Mark Rutland, linux-doc,
	Tony Lindgren, Russell King, Sachin Kamat, Ian Campbell,
	Sebastian Reichel, Luciano Coelho, devicetree, Pawel Moll,
	Stephen Warren, John W. Linville, Rob Herring, Bill Pemberton,
	linux-omap, Greg Kroah-Hartman, linux-wireless, linux-kernel,
	Felipe Balbi, Rob Landley, netdev
In-Reply-To: <4893578.SBstXOAcJY@flatron>


On Oct 28, 2013, at 5:38 PM, Tomasz Figa wrote:

> On Monday 28 of October 2013 01:37:34 Kumar Gala wrote:
>> On Oct 27, 2013, at 11:14 AM, Sebastian Reichel wrote:
>>> Add device tree support for the spi variant of wl1251
>>> and document the binding.
>>> 
>>> Signed-off-by: Sebastian Reichel <sre@debian.org>
>>> ---
>>> .../devicetree/bindings/net/wireless/ti,wl1251.txt | 36
>>> ++++++++++++++++++++++ drivers/net/wireless/ti/wl1251/spi.c          
>>>    | 23 ++++++++++---- 2 files changed, 53 insertions(+), 6
>>> deletions(-)
>>> create mode 100644
>>> Documentation/devicetree/bindings/net/wireless/ti,wl1251.txt
>>> 
>>> diff --git
>>> a/Documentation/devicetree/bindings/net/wireless/ti,wl1251.txt
>>> b/Documentation/devicetree/bindings/net/wireless/ti,wl1251.txt new
>>> file mode 100644
>>> index 0000000..5f8a154
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/net/wireless/ti,wl1251.txt
>>> @@ -0,0 +1,36 @@
>>> +* Texas Instruments wl1251 controller
>>> +
>>> +The wl1251 chip can be connected via SPI or via SDIO. The linux
>>> +kernel currently only supports device tree for the SPI variant.
>>> +
>> 
>> From the binding I have no idea what this chip actually does, also we
>> don't normally reference linux kernel support in bindings specs (so
>> please remove it).
>> 
>> However, what would expect the SDIO binding to look like?  Or more
>> specifically, how would you distinguish the SPI vs SDIO
>> binding/connection?  I'm wondering if the compatible should be
>> something like "ti,wl1251-spi" and than the sdio can be
>> "ti,wl1251-sdio"
> 
> Well, you can easily distinguish an SDIO device from an SPI device by its 
> parent node, but...
> 
> The binding for SDIO might require different set of properties (other than 
> ones inherited from generic SDIO or SPI bindings) than one for SPI. So 
> probably different compatible values might be justified.
> 
> Did we already have such case before? (maybe some I2C + SPI devices?)
> 
>>> +Required properties:
>>> +- compatible : Should be "ti,wl1251"
>> 
>> reg is not listed as a required prop.
> 
> It is implied by SPI bindings, but it might be a good idea to have this 
> stated here as well.
> 
>> 
>>> +- interrupts : Should contain interrupt line
>>> +- interrupt-parent : Should be the phandle for the interrupt
>>> +  controller that services interrupts for this device
>>> +- vio-supply : phandle to regulator providing VIO
>>> +- power-gpio : GPIO connected to chip's PMEN pin
>> 
>> should be vendor prefixed: ti,power-gpio
> 
> Hmm, out of curiosity, is it a rule for this kind of properties? I can see 
> both cases with and without prefixes when grepping for "-gpios" in 
> Documentation/devicetree/bindings. We should really have such things 
> written down somewhere.

Agreed, it should be part of the various docs we are suppose to produce for review and binding creation guidelines.

>>> +- For additional required properties on SPI, please consult
>>> +  Documentation/devicetree/bindings/spi/spi-bus.txt
>>> +
>>> +Optional properties:
>>> +- ti,use-eeprom : If found, configuration will be loaded from eeprom.
>> 
>> can you be a bit more specific on what cfg will be loaded.  Also, is
>> this property a boolean, if so how do I know which eeprom the cfg is
>> loaded from (is it one that is directly connected to the wl1251?
> 
> Maybe one from ti,has-eeprom or ti,config-eeprom would be better name for 
> this property?

Probably, ti,wl1251-has-eeprom or something like that would be better.  However, I'm not going to get too caught up on names of properties.

- k

-- 
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation


^ permalink raw reply

* [PATCH] mac80211: verify ieee80211_key_replace() arguments
From: Johannes Berg @ 2013-10-29  9:02 UTC (permalink / raw)
  To: linux-wireless; +Cc: Johannes Berg

From: Johannes Berg <johannes.berg@intel.com>

There's no code calling ieee80211_key_replace() with both
arguments NULL and it wouldn't make sense, but in the
interest of maintainability add a warning for it. As a
side effect, this also shuts up a smatch warning.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
 net/mac80211/key.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index 3e51dd7..ab84680 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -260,6 +260,10 @@ static void ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
 	int idx;
 	bool defunikey, defmultikey, defmgmtkey;
 
+	/* caller must provide at least one old/new */
+	if (WARN_ON(!new && !old))
+		return;
+
 	if (new)
 		list_add_tail(&new->list, &sdata->key_list);
 
-- 
1.8.4.rc3


^ permalink raw reply related

* Re: Linksys AE3000 iw tool issues
From: Xose Vazquez Perez @ 2013-10-29  9:35 UTC (permalink / raw)
  To: "Richard Wang rpw", linux-wireless,
	users@rt2x00.serialmonkey.com

Richard Wang wrote:

> succinctly. Based on other threads, it appeared as if there might be 
> support for rt2800usb for rt3573 chips in experimental versions 
> (http://rt2x00.serialmonkey.com/pipermail/users_rt2x00.serialmonkey.com/2013-July/006283.html). 
> I tried to install the latest development version of backports hoping 
> that this patch would be part of it but it didn't seem to work. I'm 
> hoping to see if anyone happens to have a working patch for rt3573 chips.

Support for RT3573 was added recently, you should use wireless-next:
http://git.kernel.org/cgit/linux/kernel/git/linville/wireless-next.git
or latest backports:
http://www.kernel.org/pub/linux/kernel/projects/backports/2013/10/25/backports-20131025.tar.bz2


^ permalink raw reply

* [RFC] mac80211: add generic cipher scheme support
From: Johannes Berg @ 2013-10-29 10:03 UTC (permalink / raw)
  To: linux-wireless; +Cc: j, Max Stepanov

From: Max Stepanov <Max.Stepanov@intel.com>

This adds generic cipher scheme support to mac80211, such schemes
are fully under control by the driver. On hw registration drivers
may specify additional HW ciphers with a scheme how these ciphers
have to be handled by mac80211 TX/RR. A cipher scheme specifies a
cipher suite value, a size of the security header to be added to
or stripped from frames and how the PN is to be verified on RX.

Signed-off-by: Max Stepanov <Max.Stepanov@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
 include/net/mac80211.h     |  36 +++++++++++
 net/mac80211/cfg.c         |  31 ++++++++--
 net/mac80211/ieee80211_i.h |  10 ++++
 net/mac80211/iface.c       |   5 ++
 net/mac80211/key.c         |  58 +++++++++++-------
 net/mac80211/key.h         |  13 +++-
 net/mac80211/main.c        | 145 +++++++++++++++++++++++++++++++--------------
 net/mac80211/mesh_hwmp.c   |   4 +-
 net/mac80211/mlme.c        |   4 ++
 net/mac80211/rx.c          |  65 +++++++++++++++-----
 net/mac80211/sta_info.h    |  10 +++-
 net/mac80211/tx.c          |  14 +++--
 net/mac80211/util.c        |  68 +++++++++++++++++++++
 net/mac80211/wpa.c         | 116 ++++++++++++++++++++++++++++++++++++
 net/mac80211/wpa.h         |   2 +
 15 files changed, 480 insertions(+), 101 deletions(-)

diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 7ceed99..cca02b2 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -1229,6 +1229,36 @@ struct ieee80211_key_conf {
 };
 
 /**
+ * struct ieee80211_cipher_scheme - cipher scheme
+ *
+ * This structure contains a cipher scheme information defining
+ * the secure packet crypto handling.
+ *
+ * @cipher: a cipher suite selector
+ * @iftype: a cipher iftype bit mask indicating an allowed cipher usage
+ * @hdr_len: a length of a security header used the cipher
+ * @pn_len: a length of a packet number in the security header
+ * @pn_off: an offset of pn from the beginning of the security header
+ * @key_idx_off: an offset of key index byte in the security header
+ * @key_idx_mask: a bit mask of key_idx bits
+ * @key_idx_shift: a bit shift needed to get key_idx
+ *     key_idx value calculation:
+ *      (sec_header_base[key_idx_off] & key_idx_mask) >> key_idx_shift
+ * @mic_len: a mic length in bytes
+ */
+struct ieee80211_cipher_scheme {
+	u32 cipher;
+	u16 iftype;
+	u8 hdr_len;
+	u8 pn_len;
+	u8 pn_off;
+	u8 key_idx_off;
+	u8 key_idx_mask;
+	u8 key_idx_shift;
+	u8 mic_len;
+};
+
+/**
  * enum set_key_cmd - key command
  *
  * Used with the set_key() callback in &struct ieee80211_ops, this
@@ -1636,6 +1666,10 @@ enum ieee80211_hw_flags {
  * @uapsd_max_sp_len: maximum number of total buffered frames the WMM AP may
  *	deliver to a WMM STA during any Service Period triggered by the WMM STA.
  *	Use IEEE80211_WMM_IE_STA_QOSINFO_SP_* for correct values.
+ *
+ * @n_cipher_schemes: a size of an array of cipher schemes definitions.
+ * @cipher_schemes: a pointer to an array of cipher scheme definitions
+ *	supported by HW.
  */
 struct ieee80211_hw {
 	struct ieee80211_conf conf;
@@ -1663,6 +1697,8 @@ struct ieee80211_hw {
 	netdev_features_t netdev_features;
 	u8 uapsd_queues;
 	u8 uapsd_max_sp_len;
+	u8 n_cipher_schemes;
+	const struct ieee80211_cipher_scheme *cipher_schemes;
 };
 
 /**
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index ed1e9a8..f2bbe3d 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -133,7 +133,9 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
 			     struct key_params *params)
 {
 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	struct ieee80211_local *local = sdata->local;
 	struct sta_info *sta = NULL;
+	const struct ieee80211_cipher_scheme *cs = NULL;
 	struct ieee80211_key *key;
 	int err;
 
@@ -145,22 +147,28 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
 	case WLAN_CIPHER_SUITE_WEP40:
 	case WLAN_CIPHER_SUITE_TKIP:
 	case WLAN_CIPHER_SUITE_WEP104:
-		if (IS_ERR(sdata->local->wep_tx_tfm))
+		if (IS_ERR(local->wep_tx_tfm))
 			return -EINVAL;
 		break;
+	case WLAN_CIPHER_SUITE_CCMP:
+	case WLAN_CIPHER_SUITE_AES_CMAC:
+	case WLAN_CIPHER_SUITE_GCMP:
+		break;
 	default:
+		cs = ieee80211_cs_get(local, params->cipher, sdata->vif.type);
 		break;
 	}
 
 	key = ieee80211_key_alloc(params->cipher, key_idx, params->key_len,
-				  params->key, params->seq_len, params->seq);
+				  params->key, params->seq_len, params->seq,
+				  cs);
 	if (IS_ERR(key))
 		return PTR_ERR(key);
 
 	if (pairwise)
 		key->conf.flags |= IEEE80211_KEY_FLAG_PAIRWISE;
 
-	mutex_lock(&sdata->local->sta_mtx);
+	mutex_lock(&local->sta_mtx);
 
 	if (mac_addr) {
 		if (ieee80211_vif_is_mesh(&sdata->vif))
@@ -216,10 +224,13 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
 		break;
 	}
 
+	if (sta)
+		sta->cipher_scheme = cs;
+
 	err = ieee80211_key_link(key, sdata, sta);
 
  out_unlock:
-	mutex_unlock(&sdata->local->sta_mtx);
+	mutex_unlock(&local->sta_mtx);
 
 	return err;
 }
@@ -244,7 +255,7 @@ static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
 			goto out_unlock;
 
 		if (pairwise)
-			key = key_mtx_dereference(local, sta->ptk);
+			key = key_mtx_dereference(local, sta->ptk[key_idx]);
 		else
 			key = key_mtx_dereference(local, sta->gtk[key_idx]);
 	} else
@@ -291,7 +302,7 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
 			goto out;
 
 		if (pairwise)
-			key = rcu_dereference(sta->ptk);
+			key = rcu_dereference(sta->ptk[key_idx]);
 		else if (key_idx < NUM_DEFAULT_KEYS)
 			key = rcu_dereference(sta->gtk[key_idx]);
 	} else
@@ -968,11 +979,19 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
 	 */
 	sdata->control_port_protocol = params->crypto.control_port_ethertype;
 	sdata->control_port_no_encrypt = params->crypto.control_port_no_encrypt;
+	sdata->encrypt_headroom = ieee80211_cs_headroom(sdata->local,
+							&params->crypto,
+							sdata->vif.type);
+
 	list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) {
 		vlan->control_port_protocol =
 			params->crypto.control_port_ethertype;
 		vlan->control_port_no_encrypt =
 			params->crypto.control_port_no_encrypt;
+		vlan->encrypt_headroom =
+			ieee80211_cs_headroom(sdata->local,
+					      &params->crypto,
+					      vlan->vif.type);
 	}
 
 	sdata->vif.bss_conf.beacon_int = params->beacon_interval;
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 5cfa160..de1bb78 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -727,6 +727,7 @@ struct ieee80211_sub_if_data {
 	u16 sequence_number;
 	__be16 control_port_protocol;
 	bool control_port_no_encrypt;
+	int encrypt_headroom;
 
 	struct ieee80211_tx_queue_params tx_conf[IEEE80211_NUM_ACS];
 
@@ -1745,6 +1746,15 @@ void ieee80211_dfs_radar_detected_work(struct work_struct *work);
 int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata,
 			      struct cfg80211_csa_settings *csa_settings);
 
+bool ieee80211_cs_valid(const struct ieee80211_cipher_scheme *cs);
+bool ieee80211_cs_list_valid(const struct ieee80211_cipher_scheme *cs, int n);
+const struct ieee80211_cipher_scheme *
+ieee80211_cs_get(struct ieee80211_local *local, u32 cipher,
+		 enum nl80211_iftype iftype);
+int ieee80211_cs_headroom(struct ieee80211_local *local,
+			  struct cfg80211_crypto_settings *crypto,
+			  enum nl80211_iftype iftype);
+
 #ifdef CONFIG_MAC80211_NOINLINE
 #define debug_noinline noinline
 #else
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index ff101ea..ad28e3d 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -401,6 +401,8 @@ int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
 	snprintf(sdata->name, IFNAMSIZ, "%s-monitor",
 		 wiphy_name(local->hw.wiphy));
 
+	sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM;
+
 	ieee80211_set_default_queues(sdata);
 
 	ret = drv_add_interface(local, sdata);
@@ -1270,6 +1272,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
 
 	sdata->control_port_protocol = cpu_to_be16(ETH_P_PAE);
 	sdata->control_port_no_encrypt = false;
+	sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM;
 
 	sdata->noack_map = 0;
 
@@ -1686,6 +1689,8 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
 	sdata->ap_power_level = IEEE80211_UNSET_POWER_LEVEL;
 	sdata->user_power_level = local->user_power_level;
 
+	sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM;
+
 	/* setup type-dependent data */
 	ieee80211_setup_sdata(sdata, type);
 
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index ab84680..e568d98 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -267,22 +267,22 @@ static void ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
 	if (new)
 		list_add_tail(&new->list, &sdata->key_list);
 
-	if (sta && pairwise) {
-		rcu_assign_pointer(sta->ptk, new);
-	} else if (sta) {
-		if (old)
-			idx = old->conf.keyidx;
-		else
-			idx = new->conf.keyidx;
-		rcu_assign_pointer(sta->gtk[idx], new);
-	} else {
-		WARN_ON(new && old && new->conf.keyidx != old->conf.keyidx);
+	WARN_ON(new && old && new->conf.keyidx != old->conf.keyidx);
 
-		if (old)
-			idx = old->conf.keyidx;
-		else
-			idx = new->conf.keyidx;
+	if (old)
+		idx = old->conf.keyidx;
+	else
+		idx = new->conf.keyidx;
 
+	if (sta) {
+		if (pairwise) {
+			rcu_assign_pointer(sta->ptk[idx], new);
+			sta->ptk_idx = idx;
+		} else {
+			rcu_assign_pointer(sta->gtk[idx], new);
+			sta->gtk_idx = idx;
+		}
+	} else {
 		defunikey = old &&
 			old == key_mtx_dereference(sdata->local,
 						sdata->default_unicast_key);
@@ -316,9 +316,11 @@ static void ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
 		list_del(&old->list);
 }
 
-struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
-					  const u8 *key_data,
-					  size_t seq_len, const u8 *seq)
+struct ieee80211_key *
+ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
+		    const u8 *key_data,
+		    size_t seq_len, const u8 *seq,
+		    const struct ieee80211_cipher_scheme *cs)
 {
 	struct ieee80211_key *key;
 	int i, j, err;
@@ -397,6 +399,18 @@ struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
 			return ERR_PTR(err);
 		}
 		break;
+	default:
+		if (cs) {
+			size_t len = (seq_len > MAX_PN_LEN) ?
+						MAX_PN_LEN : seq_len;
+
+			key->conf.iv_len = cs->hdr_len;
+			key->conf.icv_len = cs->mic_len;
+			for (i = 0; i < IEEE80211_NUM_TIDS + 1; i++)
+				for (j = 0; j < len; j++)
+					key->u.gen.rx_pn[i][j] =
+							seq[len - j - 1];
+		}
 	}
 	memcpy(key->conf.key, key_data, key_len);
 	INIT_LIST_HEAD(&key->list);
@@ -479,7 +493,7 @@ int ieee80211_key_link(struct ieee80211_key *key,
 	mutex_lock(&sdata->local->key_mtx);
 
 	if (sta && pairwise)
-		old_key = key_mtx_dereference(sdata->local, sta->ptk);
+		old_key = key_mtx_dereference(sdata->local, sta->ptk[idx]);
 	else if (sta)
 		old_key = key_mtx_dereference(sdata->local, sta->gtk[idx]);
 	else
@@ -629,8 +643,10 @@ void ieee80211_free_sta_keys(struct ieee80211_local *local,
 		list_add(&key->list, &keys);
 	}
 
-	key = key_mtx_dereference(local, sta->ptk);
-	if (key) {
+	for (i = 0; i < NUM_DEFAULT_KEYS; i++) {
+		key = key_mtx_dereference(local, sta->ptk[i]);
+		if (!key)
+			continue;
 		ieee80211_key_replace(key->sdata, key->sta,
 				key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE,
 				key, NULL);
@@ -881,7 +897,7 @@ ieee80211_gtk_rekey_add(struct ieee80211_vif *vif,
 
 	key = ieee80211_key_alloc(keyconf->cipher, keyconf->keyidx,
 				  keyconf->keylen, keyconf->key,
-				  0, NULL);
+				  0, NULL, NULL);
 	if (IS_ERR(key))
 		return ERR_CAST(key);
 
diff --git a/net/mac80211/key.h b/net/mac80211/key.h
index aaae0ed..0aebb88 100644
--- a/net/mac80211/key.h
+++ b/net/mac80211/key.h
@@ -18,6 +18,7 @@
 
 #define NUM_DEFAULT_KEYS 4
 #define NUM_DEFAULT_MGMT_KEYS 2
+#define MAX_PN_LEN 16
 
 struct ieee80211_local;
 struct ieee80211_sub_if_data;
@@ -93,6 +94,10 @@ struct ieee80211_key {
 			u32 replays; /* dot11RSNAStatsCMACReplays */
 			u32 icverrors; /* dot11RSNAStatsCMACICVErrors */
 		} aes_cmac;
+		struct {
+			/* generic cipher scheme */
+			u8 rx_pn[IEEE80211_NUM_TIDS + 1][MAX_PN_LEN];
+		} gen;
 	} u;
 
 	/* number of times this key has been used */
@@ -113,9 +118,11 @@ struct ieee80211_key {
 	struct ieee80211_key_conf conf;
 };
 
-struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
-					  const u8 *key_data,
-					  size_t seq_len, const u8 *seq);
+struct ieee80211_key *
+ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
+		    const u8 *key_data,
+		    size_t seq_len, const u8 *seq,
+		    const struct ieee80211_cipher_scheme *cs);
 /*
  * Insert a key into data structures (sdata, sta if necessary)
  * to make it used, free old key. On failure, also free the new key.
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 21d5d44..bdb0b6c 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -651,15 +651,14 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
 }
 EXPORT_SYMBOL(ieee80211_alloc_hw);
 
-int ieee80211_register_hw(struct ieee80211_hw *hw)
+static int ieee80211_init_cipher_suites(struct ieee80211_local *local)
 {
-	struct ieee80211_local *local = hw_to_local(hw);
-	int result, i;
-	enum ieee80211_band band;
-	int channels, max_bitrates;
-	bool supp_ht, supp_vht;
-	netdev_features_t feature_whitelist;
-	struct cfg80211_chan_def dflt_chandef = {};
+	bool have_wep = !(IS_ERR(local->wep_tx_tfm) ||
+			  IS_ERR(local->wep_rx_tfm));
+	bool have_mfp = local->hw.flags & IEEE80211_HW_MFP_CAPABLE;
+	const struct ieee80211_cipher_scheme *cs = local->hw.cipher_schemes;
+	int n_suites = 0, r = 0, w = 0;
+	u32 *suites;
 	static const u32 cipher_suites[] = {
 		/* keep WEP first, it may be removed below */
 		WLAN_CIPHER_SUITE_WEP40,
@@ -671,6 +670,93 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
 		WLAN_CIPHER_SUITE_AES_CMAC
 	};
 
+	/* Driver specifies the ciphers, we have nothing to do... */
+	if (local->hw.wiphy->cipher_suites && have_wep)
+		return 0;
+
+	/* Set up cipher suites if driver relies on mac80211 cipher defs */
+	if (!local->hw.wiphy->cipher_suites && !cs) {
+		local->hw.wiphy->cipher_suites = cipher_suites;
+		local->hw.wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
+
+		if (!have_mfp)
+			local->hw.wiphy->n_cipher_suites--;
+
+		if (!have_wep) {
+			local->hw.wiphy->cipher_suites += 2;
+			local->hw.wiphy->n_cipher_suites -= 2;
+		}
+
+		return 0;
+	}
+
+	if (!local->hw.wiphy->cipher_suites) {
+		/*
+		 * Driver specifies cipher schemes only
+		 * We start counting ciphers defined by schemes, TKIP and CCMP
+		 */
+		n_suites = local->hw.n_cipher_schemes + 2;
+
+		/* check if we have WEP40 and WEP104 */
+		if (have_wep)
+			n_suites += 2;
+
+		/* check if we have AES_CMAC */
+		if (have_mfp)
+			n_suites++;
+
+		suites = kmalloc(sizeof(u32) * n_suites, GFP_KERNEL);
+		if (!suites)
+			return -ENOMEM;
+
+		suites[w++] = WLAN_CIPHER_SUITE_CCMP;
+		suites[w++] = WLAN_CIPHER_SUITE_TKIP;
+
+		if (have_wep) {
+			suites[w++] = WLAN_CIPHER_SUITE_WEP40;
+			suites[w++] = WLAN_CIPHER_SUITE_WEP104;
+		}
+
+		if (have_mfp)
+			suites[w++] = WLAN_CIPHER_SUITE_AES_CMAC;
+
+		for (r = 0; r < local->hw.n_cipher_schemes; r++)
+			suites[w++] = cs[r].cipher;
+	} else {
+		/* Driver provides cipher suites, but we need to exclude WEP */
+		suites = kmemdup(local->hw.wiphy->cipher_suites,
+				 sizeof(u32) * local->hw.wiphy->n_cipher_suites,
+				 GFP_KERNEL);
+		if (!suites)
+			return -ENOMEM;
+
+		for (r = 0; r < local->hw.wiphy->n_cipher_suites; r++) {
+			u32 suite = local->hw.wiphy->cipher_suites[r];
+
+			if (suite == WLAN_CIPHER_SUITE_WEP40 ||
+			    suite == WLAN_CIPHER_SUITE_WEP104)
+				continue;
+			suites[w++] = suite;
+		}
+	}
+
+	local->hw.wiphy->cipher_suites = suites;
+	local->hw.wiphy->n_cipher_suites = w;
+	local->wiphy_ciphers_allocated = true;
+
+	return 0;
+}
+
+int ieee80211_register_hw(struct ieee80211_hw *hw)
+{
+	struct ieee80211_local *local = hw_to_local(hw);
+	int result, i;
+	enum ieee80211_band band;
+	int channels, max_bitrates;
+	bool supp_ht, supp_vht;
+	netdev_features_t feature_whitelist;
+	struct cfg80211_chan_def dflt_chandef = {};
+
 	if (hw->flags & IEEE80211_HW_QUEUE_CONTROL &&
 	    (local->hw.offchannel_tx_hw_queue == IEEE80211_INVAL_HW_QUEUE ||
 	     local->hw.offchannel_tx_hw_queue >= local->hw.queues))
@@ -851,43 +937,12 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
 	if (local->hw.wiphy->max_scan_ie_len)
 		local->hw.wiphy->max_scan_ie_len -= local->scan_ies_len;
 
-	/* Set up cipher suites unless driver already did */
-	if (!local->hw.wiphy->cipher_suites) {
-		local->hw.wiphy->cipher_suites = cipher_suites;
-		local->hw.wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
-		if (!(local->hw.flags & IEEE80211_HW_MFP_CAPABLE))
-			local->hw.wiphy->n_cipher_suites--;
-	}
-	if (IS_ERR(local->wep_tx_tfm) || IS_ERR(local->wep_rx_tfm)) {
-		if (local->hw.wiphy->cipher_suites == cipher_suites) {
-			local->hw.wiphy->cipher_suites += 2;
-			local->hw.wiphy->n_cipher_suites -= 2;
-		} else {
-			u32 *suites;
-			int r, w = 0;
-
-			/* Filter out WEP */
-
-			suites = kmemdup(
-				local->hw.wiphy->cipher_suites,
-				sizeof(u32) * local->hw.wiphy->n_cipher_suites,
-				GFP_KERNEL);
-			if (!suites) {
-				result = -ENOMEM;
-				goto fail_wiphy_register;
-			}
-			for (r = 0; r < local->hw.wiphy->n_cipher_suites; r++) {
-				u32 suite = local->hw.wiphy->cipher_suites[r];
-				if (suite == WLAN_CIPHER_SUITE_WEP40 ||
-				    suite == WLAN_CIPHER_SUITE_WEP104)
-					continue;
-				suites[w++] = suite;
-			}
-			local->hw.wiphy->cipher_suites = suites;
-			local->hw.wiphy->n_cipher_suites = w;
-			local->wiphy_ciphers_allocated = true;
-		}
-	}
+	WARN_ON(!ieee80211_cs_list_valid(local->hw.cipher_schemes,
+					 local->hw.n_cipher_schemes));
+
+	result = ieee80211_init_cipher_suites(local);
+	if (result < 0)
+		goto fail_wiphy_register;
 
 	if (!local->ops->remain_on_channel)
 		local->hw.wiphy->max_remain_on_channel_duration = 5000;
diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c
index 486819c..56e0c072 100644
--- a/net/mac80211/mesh_hwmp.c
+++ b/net/mac80211/mesh_hwmp.c
@@ -254,13 +254,13 @@ int mesh_path_error_tx(struct ieee80211_sub_if_data *sdata,
 		return -EAGAIN;
 
 	skb = dev_alloc_skb(local->tx_headroom +
-			    IEEE80211_ENCRYPT_HEADROOM +
+			    sdata->encrypt_headroom +
 			    IEEE80211_ENCRYPT_TAILROOM +
 			    hdr_len +
 			    2 + 15 /* PERR IE */);
 	if (!skb)
 		return -1;
-	skb_reserve(skb, local->tx_headroom + IEEE80211_ENCRYPT_HEADROOM);
+	skb_reserve(skb, local->tx_headroom + sdata->encrypt_headroom);
 	mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len);
 	memset(mgmt, 0, hdr_len);
 	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 5b2b0a1..98d6d1e 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -1745,6 +1745,8 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
 
 	ifmgd->flags = 0;
 	ieee80211_vif_release_channel(sdata);
+
+	sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM;
 }
 
 void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
@@ -4112,6 +4114,8 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
 
 	sdata->control_port_protocol = req->crypto.control_port_ethertype;
 	sdata->control_port_no_encrypt = req->crypto.control_port_no_encrypt;
+	sdata->encrypt_headroom = ieee80211_cs_headroom(local, &req->crypto,
+							sdata->vif.type);
 
 	/* kick off associate process */
 
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 23f49e8..e1b71d8 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -638,6 +638,27 @@ static int ieee80211_get_mmie_keyidx(struct sk_buff *skb)
 	return le16_to_cpu(mmie->key_id);
 }
 
+static int iwl80211_get_cs_keyid(const struct ieee80211_cipher_scheme *cs,
+				 struct sk_buff *skb)
+{
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+	__le16 fc;
+	int hdrlen;
+	u8 keyid;
+
+	fc = hdr->frame_control;
+	hdrlen = ieee80211_hdrlen(fc);
+
+	if (skb->len < hdrlen + cs->hdr_len)
+		return -EINVAL;
+
+	skb_copy_bits(skb, hdrlen + cs->key_idx_off, &keyid, 1);
+	keyid &= cs->key_idx_mask;
+	keyid >>= cs->key_idx_shift;
+
+	return keyid;
+}
+
 static ieee80211_rx_result ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
 {
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
@@ -1368,6 +1389,7 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
 	struct ieee80211_key *sta_ptk = NULL;
 	int mmie_keyidx = -1;
 	__le16 fc;
+	const struct ieee80211_cipher_scheme *cs = NULL;
 
 	/*
 	 * Key selection 101
@@ -1405,11 +1427,19 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
 
 	/* start without a key */
 	rx->key = NULL;
+	fc = hdr->frame_control;
 
-	if (rx->sta)
-		sta_ptk = rcu_dereference(rx->sta->ptk);
+	if (rx->sta) {
+		int keyid = rx->sta->ptk_idx;
 
-	fc = hdr->frame_control;
+		if (ieee80211_has_protected(fc) && rx->sta->cipher_scheme) {
+			cs = rx->sta->cipher_scheme;
+			keyid = iwl80211_get_cs_keyid(cs, rx->skb);
+			if (unlikely(keyid < 0))
+				return RX_DROP_UNUSABLE;
+		}
+		sta_ptk = rcu_dereference(rx->sta->ptk[keyid]);
+	}
 
 	if (!ieee80211_has_protected(fc))
 		mmie_keyidx = ieee80211_get_mmie_keyidx(rx->skb);
@@ -1471,6 +1501,7 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
 		return RX_CONTINUE;
 	} else {
 		u8 keyid;
+
 		/*
 		 * The device doesn't give us the IV so we won't be
 		 * able to look up the key. That's ok though, we
@@ -1486,15 +1517,21 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
 
 		hdrlen = ieee80211_hdrlen(fc);
 
-		if (rx->skb->len < 8 + hdrlen)
-			return RX_DROP_UNUSABLE; /* TODO: count this? */
+		if (cs) {
+			keyidx = iwl80211_get_cs_keyid(cs, rx->skb);
 
-		/*
-		 * no need to call ieee80211_wep_get_keyidx,
-		 * it verifies a bunch of things we've done already
-		 */
-		skb_copy_bits(rx->skb, hdrlen + 3, &keyid, 1);
-		keyidx = keyid >> 6;
+			if (unlikely(keyidx < 0))
+				return RX_DROP_UNUSABLE;
+		} else {
+			if (rx->skb->len < 8 + hdrlen)
+				return RX_DROP_UNUSABLE; /* TODO: count this? */
+			/*
+			 * no need to call ieee80211_wep_get_keyidx,
+			 * it verifies a bunch of things we've done already
+			 */
+			skb_copy_bits(rx->skb, hdrlen + 3, &keyid, 1);
+			keyidx = keyid >> 6;
+		}
 
 		/* check per-station GTK first, if multicast packet */
 		if (is_multicast_ether_addr(hdr->addr1) && rx->sta)
@@ -1542,11 +1579,7 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
 		result = ieee80211_crypto_aes_cmac_decrypt(rx);
 		break;
 	default:
-		/*
-		 * We can reach here only with HW-only algorithms
-		 * but why didn't it decrypt the frame?!
-		 */
-		return RX_DROP_UNUSABLE;
+		result = ieee80211_crypto_hw_decrypt(rx);
 	}
 
 	/* the hdr variable is invalid after the decrypt handlers */
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 3ef06a2..6b0d6c2 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -231,8 +231,10 @@ struct sta_ampdu_mlme {
  * @hnext: hash table linked list pointer
  * @local: pointer to the global information
  * @sdata: virtual interface this station belongs to
- * @ptk: peer key negotiated with this station, if any
+ * @ptk: peer keys negotiated with this station, if any
+ * @ptk_idx: last installed peer key index
  * @gtk: group keys negotiated with this station, if any
+ * @gtk_idx: last installed group key index
  * @rate_ctrl: rate control algorithm reference
  * @rate_ctrl_priv: rate control private per-STA pointer
  * @last_tx_rate: rate used for last transmit, to report to userspace as
@@ -303,6 +305,7 @@ struct sta_ampdu_mlme {
  * @chain_signal_avg: signal average (per chain)
  * @known_smps_mode: the smps_mode the client thinks we are in. Relevant for
  *	AP only.
+ * @cipher_scheme: optional cipher scheme for this station
  */
 struct sta_info {
 	/* General information, mostly static */
@@ -312,7 +315,9 @@ struct sta_info {
 	struct ieee80211_local *local;
 	struct ieee80211_sub_if_data *sdata;
 	struct ieee80211_key __rcu *gtk[NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS];
-	struct ieee80211_key __rcu *ptk;
+	struct ieee80211_key __rcu *ptk[NUM_DEFAULT_KEYS];
+	u8 gtk_idx;
+	u8 ptk_idx;
 	struct rate_control_ref *rate_ctrl;
 	void *rate_ctrl_priv;
 	spinlock_t lock;
@@ -414,6 +419,7 @@ struct sta_info {
 	unsigned int beacon_loss_count;
 
 	enum ieee80211_smps_mode known_smps_mode;
+	const struct ieee80211_cipher_scheme *cipher_scheme;
 
 	/* keep last! */
 	struct ieee80211_sta sta;
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 9868cb7..e84627f 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -557,7 +557,8 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
 
 	if (unlikely(info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT))
 		tx->key = NULL;
-	else if (tx->sta && (key = rcu_dereference(tx->sta->ptk)))
+	else if (tx->sta &&
+		 (key = rcu_dereference(tx->sta->ptk[tx->sta->ptk_idx])))
 		tx->key = key;
 	else if (ieee80211_is_mgmt(hdr->frame_control) &&
 		 is_multicast_ether_addr(hdr->addr1) &&
@@ -840,15 +841,16 @@ static int ieee80211_fragment(struct ieee80211_tx_data *tx,
 		rem -= fraglen;
 		tmp = dev_alloc_skb(local->tx_headroom +
 				    frag_threshold +
-				    IEEE80211_ENCRYPT_HEADROOM +
+				    tx->sdata->encrypt_headroom +
 				    IEEE80211_ENCRYPT_TAILROOM);
 		if (!tmp)
 			return -ENOMEM;
 
 		__skb_queue_tail(&tx->skbs, tmp);
 
-		skb_reserve(tmp, local->tx_headroom +
-				 IEEE80211_ENCRYPT_HEADROOM);
+		skb_reserve(tmp,
+			    local->tx_headroom + tx->sdata->encrypt_headroom);
+
 		/* copy control information */
 		memcpy(tmp->cb, skb->cb, sizeof(tmp->cb));
 
@@ -1484,7 +1486,7 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
 
 	headroom = local->tx_headroom;
 	if (may_encrypt)
-		headroom += IEEE80211_ENCRYPT_HEADROOM;
+		headroom += sdata->encrypt_headroom;
 	headroom -= skb_headroom(skb);
 	headroom = max_t(int, 0, headroom);
 
@@ -2108,7 +2110,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
 	 */
 
 	if (head_need > 0 || skb_cloned(skb)) {
-		head_need += IEEE80211_ENCRYPT_HEADROOM;
+		head_need += sdata->encrypt_headroom;
 		head_need += local->tx_headroom;
 		head_need = max_t(int, 0, head_need);
 		if (ieee80211_skb_resize(sdata, skb, head_need, true)) {
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index a38d582..17a12cd 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -2471,3 +2471,71 @@ int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata,
 	ieee80211_tx_skb(sdata, skb);
 	return 0;
 }
+
+bool ieee80211_cs_valid(const struct ieee80211_cipher_scheme *cs)
+{
+	return !(cs == NULL || cs->cipher == 0 ||
+		 cs->hdr_len < cs->pn_len + cs->pn_off ||
+		 cs->hdr_len <= cs->key_idx_off ||
+		 cs->key_idx_shift > 7 ||
+		 cs->key_idx_mask == 0);
+}
+
+bool ieee80211_cs_list_valid(const struct ieee80211_cipher_scheme *cs, int n)
+{
+	int i;
+
+	/* Ensure we have enough iftype bitmap space for all iftype values */
+	WARN_ON((NUM_NL80211_IFTYPES / 8 + 1) > sizeof(cs[0].iftype));
+
+	for (i = 0; i < n; i++)
+		if (!ieee80211_cs_valid(&cs[i]))
+			return false;
+
+	return true;
+}
+
+const struct ieee80211_cipher_scheme *
+ieee80211_cs_get(struct ieee80211_local *local, u32 cipher,
+		 enum nl80211_iftype iftype)
+{
+	const struct ieee80211_cipher_scheme *l = local->hw.cipher_schemes;
+	int n = local->hw.n_cipher_schemes;
+	int i;
+	const struct ieee80211_cipher_scheme *cs = NULL;
+
+	for (i = 0; i < n; i++) {
+		if (l[i].cipher == cipher) {
+			cs = &l[i];
+			break;
+		}
+	}
+
+	if (!cs || !(cs->iftype & BIT(iftype)))
+		return NULL;
+
+	return cs;
+}
+
+int ieee80211_cs_headroom(struct ieee80211_local *local,
+			  struct cfg80211_crypto_settings *crypto,
+			  enum nl80211_iftype iftype)
+{
+	const struct ieee80211_cipher_scheme *cs;
+	int headroom = IEEE80211_ENCRYPT_HEADROOM;
+	int i;
+
+	for (i = 0; i < crypto->n_ciphers_pairwise; i++) {
+		cs = ieee80211_cs_get(local, crypto->ciphers_pairwise[i],
+				      iftype);
+
+		if (cs && headroom < cs->hdr_len)
+			headroom = cs->hdr_len;
+	}
+
+	cs = ieee80211_cs_get(local, crypto->cipher_group, iftype);
+	if (cs && headroom < cs->hdr_len)
+		headroom = cs->hdr_len;
+
+	return headroom;
+}
diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c
index d657282..7313d37 100644
--- a/net/mac80211/wpa.c
+++ b/net/mac80211/wpa.c
@@ -545,6 +545,106 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
 	return RX_CONTINUE;
 }
 
+static ieee80211_tx_result
+ieee80211_crypto_cs_encrypt(struct ieee80211_tx_data *tx,
+			    struct sk_buff *skb)
+{
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+	struct ieee80211_key *key = tx->key;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	const struct ieee80211_cipher_scheme *cs = key->sta->cipher_scheme;
+	int hdrlen;
+	u8 *pos;
+
+	if (info->control.hw_key &&
+	    !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)) {
+		/* hwaccel has no need for preallocated head room */
+		return TX_CONTINUE;
+	}
+
+	if (unlikely(skb_headroom(skb) < cs->hdr_len &&
+		     pskb_expand_head(skb, cs->hdr_len, 0, GFP_ATOMIC)))
+		return TX_DROP;
+
+	hdrlen = ieee80211_hdrlen(hdr->frame_control);
+
+	pos = skb_push(skb, cs->hdr_len);
+	memmove(pos, pos + cs->hdr_len, hdrlen);
+	skb_set_network_header(skb, skb_network_offset(skb) + cs->hdr_len);
+
+	return TX_CONTINUE;
+}
+
+static inline int ieee80211_crypto_cs_pn_compare(u8 *pn1, u8 *pn2, int len)
+{
+	int i;
+
+	/* pn is little endian */
+	for (i = len - 1; i >= 0; i--) {
+		if (pn1[i] < pn2[i])
+			return -1;
+		else if (pn1[i] > pn2[i])
+			return 1;
+	}
+
+	return 0;
+}
+
+static ieee80211_rx_result
+ieee80211_crypto_cs_decrypt(struct ieee80211_rx_data *rx)
+{
+	struct ieee80211_key *key = rx->key;
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
+	const struct ieee80211_cipher_scheme *cs = NULL;
+	int hdrlen = ieee80211_hdrlen(hdr->frame_control);
+	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb);
+	int data_len;
+	u8 *rx_pn;
+	u8 *skb_pn;
+	u8 qos_tid;
+
+	if (!rx->sta || !rx->sta->cipher_scheme ||
+	    !(status->flag & RX_FLAG_DECRYPTED))
+		return RX_DROP_UNUSABLE;
+
+	if (!ieee80211_is_data(hdr->frame_control))
+		return RX_CONTINUE;
+
+	cs = rx->sta->cipher_scheme;
+
+	data_len = rx->skb->len - hdrlen - cs->hdr_len;
+
+	if (data_len < 0)
+		return RX_DROP_UNUSABLE;
+
+	if (ieee80211_is_data_qos(hdr->frame_control))
+		qos_tid = *ieee80211_get_qos_ctl(hdr) &
+				IEEE80211_QOS_CTL_TID_MASK;
+	else
+		qos_tid = 0;
+
+	if (skb_linearize(rx->skb))
+		return RX_DROP_UNUSABLE;
+
+	hdr = (struct ieee80211_hdr *)rx->skb->data;
+
+	rx_pn = key->u.gen.rx_pn[qos_tid];
+	skb_pn = rx->skb->data + hdrlen + cs->pn_off;
+
+	if (ieee80211_crypto_cs_pn_compare(skb_pn, rx_pn, cs->pn_len) <= 0)
+		return RX_DROP_UNUSABLE;
+
+	memcpy(rx_pn, skb_pn, cs->pn_len);
+
+	/* remove security header and MIC */
+	if (pskb_trim(rx->skb, rx->skb->len - cs->mic_len))
+		return RX_DROP_UNUSABLE;
+
+	memmove(rx->skb->data + cs->hdr_len, rx->skb->data, hdrlen);
+	skb_pull(rx->skb, cs->hdr_len);
+
+	return RX_CONTINUE;
+}
 
 static void bip_aad(struct sk_buff *skb, u8 *aad)
 {
@@ -685,6 +785,7 @@ ieee80211_crypto_hw_encrypt(struct ieee80211_tx_data *tx)
 {
 	struct sk_buff *skb;
 	struct ieee80211_tx_info *info = NULL;
+	ieee80211_tx_result res;
 
 	skb_queue_walk(&tx->skbs, skb) {
 		info  = IEEE80211_SKB_CB(skb);
@@ -692,9 +793,24 @@ ieee80211_crypto_hw_encrypt(struct ieee80211_tx_data *tx)
 		/* handle hw-only algorithm */
 		if (!info->control.hw_key)
 			return TX_DROP;
+
+		if (tx->key->sta->cipher_scheme) {
+			res = ieee80211_crypto_cs_encrypt(tx, skb);
+			if (res != TX_CONTINUE)
+				return res;
+		}
 	}
 
 	ieee80211_tx_set_protected(tx);
 
 	return TX_CONTINUE;
 }
+
+ieee80211_rx_result
+ieee80211_crypto_hw_decrypt(struct ieee80211_rx_data *rx)
+{
+	if (rx->sta->cipher_scheme)
+		return ieee80211_crypto_cs_decrypt(rx);
+
+	return RX_DROP_UNUSABLE;
+}
diff --git a/net/mac80211/wpa.h b/net/mac80211/wpa.h
index 07e33f8..62e5a12 100644
--- a/net/mac80211/wpa.h
+++ b/net/mac80211/wpa.h
@@ -34,5 +34,7 @@ ieee80211_rx_result
 ieee80211_crypto_aes_cmac_decrypt(struct ieee80211_rx_data *rx);
 ieee80211_tx_result
 ieee80211_crypto_hw_encrypt(struct ieee80211_tx_data *tx);
+ieee80211_rx_result
+ieee80211_crypto_hw_decrypt(struct ieee80211_rx_data *rx);
 
 #endif /* WPA_H */
-- 
1.8.4.rc3


^ permalink raw reply related

* Re: [PATCH] cfg80211: allow beaconing after CAC
From: Luis R. Rodriguez @ 2013-10-29 10:25 UTC (permalink / raw)
  To: Janusz Dziedzic; +Cc: linux-wireless, Johannes Berg
In-Reply-To: <1383027326-2635-1-git-send-email-janusz.dziedzic@tieto.com>

On Tue, Oct 29, 2013 at 7:15 AM, Janusz Dziedzic
<janusz.dziedzic@tieto.com> wrote:
> After going throught the Channel Availability Check (CAC)
> required by DFS enable beaconing. Channels that have gone
> through a CAC will be in the NL80211_DFS_AVAILABLE.
> Without this change APs don't start beaconing after
> successful CAC.
>
> Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com>
> ---
> One flag could be used in the future IEEE80211_CHAN_NO_IR.
>
>  net/wireless/chan.c |   20 ++++++++++++++++----
>  1 file changed, 16 insertions(+), 4 deletions(-)
>
> diff --git a/net/wireless/chan.c b/net/wireless/chan.c
> index a6f5c4c..205acf2 100644
> --- a/net/wireless/chan.c
> +++ b/net/wireless/chan.c
> @@ -432,6 +432,7 @@ static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
>  {
>         struct ieee80211_channel *c;
>         u32 freq, start_freq, end_freq;
> +       u32 ignore_flags;
>
>         start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
>         end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
> @@ -441,13 +442,24 @@ static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
>                 if (!c)
>                         return false;
>
> +               ignore_flags = IEEE80211_CHAN_RADAR;
> +
>                 /* check for radar flags */
> -               if ((prohibited_flags & c->flags & IEEE80211_CHAN_RADAR) &&
> -                   (c->dfs_state != NL80211_DFS_AVAILABLE))
> -                       return false;
> +               if (prohibited_flags & c->flags & IEEE80211_CHAN_RADAR) {
> +                       if (c->dfs_state != NL80211_DFS_AVAILABLE)
> +                               return false;
> +                       /*
> +                        * If DFS is required we should check only
> +                        * c->dfs_state == NL80211_DFS_AVAILABLE and
> +                        * ignore IEEE80211_CHAN_NO_IBSS and
> +                        * IEEE80211_CHAN_PASSIVE_SCAN flags
> +                        */
> +                       ignore_flags |= IEEE80211_CHAN_NO_IBSS |
> +                                       IEEE80211_CHAN_PASSIVE_SCAN;
> +               }
>
>                 /* check for the other flags */
> -               if (c->flags & prohibited_flags & ~IEEE80211_CHAN_RADAR)
> +               if (c->flags & prohibited_flags & ~ignore_flags)
>                         return false;
>         }

How do we know that prohibited_flags won't have IEEE80211_CHAN_RADAR
or IEEE80211_CHAN_NO_IBSS set?

Also why is IEEE80211_CHAN_NO_IBSS used and not IEEE80211_CHAN_NO_IR
instead? I sent patches to help clarify this situation around usage of
both no-ibss and active scan flags, by merging them to no-ir. I'm not
sure of the status of those patches going in.

  Luis

^ permalink raw reply

* Re: [PATCH] cfg80211/nl80211: Add support to report unsafe frequency ranges(s)
From: Luis R. Rodriguez @ 2013-10-29 10:30 UTC (permalink / raw)
  To: Jeffrey Johnson
  Cc: Dan Williams, Chauhan, Rajesh, Johannes Berg,
	linux-wireless@vger.kernel.org, Malinen, Jouni, Bahini, Henri,
	Chang, Leo, Luo, Xun
In-Reply-To: <526ED316.7090205@qca.qualcomm.com>

On Mon, Oct 28, 2013 at 10:11 PM, Jeffrey Johnson
<jjohnson@qca.qualcomm.com> wrote:
> The issue that has been debated internally is whether or not userspace needs
> to know the source of the interference.  In other words, if we have an
> enumerated set of interference sources, would userspace behave differently
> based upon the source?

This is ignoring my overall architectural considerations of this being
cross RF domains and likely being a better idea to instead consider a
frequency broker for this sort of information. The only reason I
believe we can let that argument go here is that in this particular
case the same antenna is being used and the information seems to be
coming from the wireless firmware, as such it is pertinent to the
struct wiphy, and hence seems proper placement for an nl80211 event.

In so far as the source of information -- as I have said internally,
although userspace may not use it today, it may tomorrow, and having
that information can be useful. I consider a random source of
associated "frequency advisory" simply pointless. At least with some
sort of category of source, userspace, say hostapd or the supplicant,
may decide to treat it in a very specific way today.

  Luis

^ permalink raw reply

* Re: [PATCH] cfg80211: allow beaconing after CAC
From: Janusz Dziedzic @ 2013-10-29 10:32 UTC (permalink / raw)
  To: Luis R. Rodriguez; +Cc: linux-wireless, Johannes Berg
In-Reply-To: <CAB=NE6W53b=uOkaVUFfKcszXPikS=ax6k_G+7Wmb7g_acpSTsA@mail.gmail.com>

On 29 October 2013 11:25, Luis R. Rodriguez <mcgrof@do-not-panic.com> wrote:
> On Tue, Oct 29, 2013 at 7:15 AM, Janusz Dziedzic
> <janusz.dziedzic@tieto.com> wrote:
>> After going throught the Channel Availability Check (CAC)
>> required by DFS enable beaconing. Channels that have gone
>> through a CAC will be in the NL80211_DFS_AVAILABLE.
>> Without this change APs don't start beaconing after
>> successful CAC.
>>
>> Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com>
>> ---
>> One flag could be used in the future IEEE80211_CHAN_NO_IR.
>>
>>  net/wireless/chan.c |   20 ++++++++++++++++----
>>  1 file changed, 16 insertions(+), 4 deletions(-)
>>
>> diff --git a/net/wireless/chan.c b/net/wireless/chan.c
>> index a6f5c4c..205acf2 100644
>> --- a/net/wireless/chan.c
>> +++ b/net/wireless/chan.c
>> @@ -432,6 +432,7 @@ static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
>>  {
>>         struct ieee80211_channel *c;
>>         u32 freq, start_freq, end_freq;
>> +       u32 ignore_flags;
>>
>>         start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
>>         end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
>> @@ -441,13 +442,24 @@ static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
>>                 if (!c)
>>                         return false;
>>
>> +               ignore_flags = IEEE80211_CHAN_RADAR;
>> +
>>                 /* check for radar flags */
>> -               if ((prohibited_flags & c->flags & IEEE80211_CHAN_RADAR) &&
>> -                   (c->dfs_state != NL80211_DFS_AVAILABLE))
>> -                       return false;
>> +               if (prohibited_flags & c->flags & IEEE80211_CHAN_RADAR) {
>> +                       if (c->dfs_state != NL80211_DFS_AVAILABLE)
>> +                               return false;
>> +                       /*
>> +                        * If DFS is required we should check only
>> +                        * c->dfs_state == NL80211_DFS_AVAILABLE and
>> +                        * ignore IEEE80211_CHAN_NO_IBSS and
>> +                        * IEEE80211_CHAN_PASSIVE_SCAN flags
>> +                        */
>> +                       ignore_flags |= IEEE80211_CHAN_NO_IBSS |
>> +                                       IEEE80211_CHAN_PASSIVE_SCAN;
>> +               }
>>
>>                 /* check for the other flags */
>> -               if (c->flags & prohibited_flags & ~IEEE80211_CHAN_RADAR)
>> +               if (c->flags & prohibited_flags & ~ignore_flags)
>>                         return false;
>>         }
>
> How do we know that prohibited_flags won't have IEEE80211_CHAN_RADAR
> or IEEE80211_CHAN_NO_IBSS set?
>
> Also why is IEEE80211_CHAN_NO_IBSS used and not IEEE80211_CHAN_NO_IR
> instead? I sent patches to help clarify this situation around usage of
> both no-ibss and active scan flags, by merging them to no-ir. I'm not
> sure of the status of those patches going in.
>

I am not sure. Even without my patch seems  IEEE80211_CHAN_RADAR is
added to prohibited_flags and have different meaning (I think).
In orginal code we have:

if (c->flags & prohibited_flags & ~IEEE80211_CHAN_RADAR)

So, we skiping CHAN_RADAR anyway? I am not sure we should change
prohibited_flags to different name or stop skiping CHAN_RADAR and
modify upper layer - cfg80211_reg_can_beacon() in such case.

BR
Janusz

^ permalink raw reply

* ath6kl USB driver loading problem
From: Drasko DRASKOVIC @ 2013-10-29 10:38 UTC (permalink / raw)
  To: linux-wireless, ath6kl-devel

Hi all,
I have a USB dongle that uses ath6kl driver. I have a Cyclone5 Altera
socfpga Linux, kernel version 3.8, and during the boot I am facing
following problem:
usb 1-1: new high-speed USB device number 2 using dwc_otg
INFO: rcu_sched self-detected stall on CPU { 0}  (t=2100 jiffies
g=4294967038 c=4294967037 q=12)
[<800154f4>] (unwind_backtrace+0x0/0xf8) from [<80080044>]
(rcu_check_callbacks+0x388/0x860)
[<80080044>] (rcu_check_callbacks+0x388/0x860) from [<80031ea0>]
(update_process_times+0x38/0x68)
[<80031ea0>] (update_process_times+0x38/0x68) from [<80063000>]
(tick_sched_timer+0x5c/0x1f0)
[<80063000>] (tick_sched_timer+0x5c/0x1f0) from [<80046b14>]
(__run_hrtimer+0x84/0x1b4)
[<80046b14>] (__run_hrtimer+0x84/0x1b4) from [<80047480>]
(hrtimer_interrupt+0x108/0x2d4)
[<80047480>] (hrtimer_interrupt+0x108/0x2d4) from [<80014458>]
(twd_handler+0x40/0x4c)
[<80014458>] (twd_handler+0x40/0x4c) from [<8007a148>]
(handle_percpu_devid_irq+0x5c/0x144)
[<8007a148>] (handle_percpu_devid_irq+0x5c/0x144) from [<800773a0>]
(generic_handle_irq+0x20/0x30)
[<800773a0>] (generic_handle_irq+0x20/0x30) from [<8000f294>]
(handle_IRQ+0x38/0x94)
[<8000f294>] (handle_IRQ+0x38/0x94) from [<80081a78>]
(__ipipe_do_sync_stage+0x218/0x22c)
[<80081a78>] (__ipipe_do_sync_stage+0x218/0x22c) from [<80081be8>]
(ipipe_unstall_root+0x38/0x40)
[<80081be8>] (ipipe_unstall_root+0x38/0x40) from [<8002a5c8>]
(__do_softirq+0x7c/0x224)
[<8002a5c8>] (__do_softirq+0x7c/0x224) from [<8002aa44>] (irq_exit+0x4c/0x54)
[<8002aa44>] (irq_exit+0x4c/0x54) from [<8000f298>] (handle_IRQ+0x3c/0x94)
[<8000f298>] (handle_IRQ+0x3c/0x94) from [<80081a78>]
(__ipipe_do_sync_stage+0x218/0x22c)
[<80081a78>] (__ipipe_do_sync_stage+0x218/0x22c) from [<80081be8>]
(ipipe_unstall_root+0x38/0x40)
[<80081be8>] (ipipe_unstall_root+0x38/0x40) from [<803e2530>]
(__netdev_alloc_frag+0x110/0x154)
[<803e2530>] (__netdev_alloc_frag+0x110/0x154) from [<803e4c80>]
(__netdev_alloc_skb+0x40/0xe0)
[<803e4c80>] (__netdev_alloc_skb+0x40/0xe0) from [<8036824c>]
(ath6kl_usb_post_recv_transfers.constprop.6+0x1c/0x118)
[<8036824c>] (ath6kl_usb_post_recv_transfers.constprop.6+0x1c/0x118)
from [<80368468>] (ath6kl_usb_power_on+0x1c/0x40)
[<80368468>] (ath6kl_usb_power_on+0x1c/0x40) from [<80366de4>]
(ath6kl_core_init+0xa0/0x42c)
[<80366de4>] (ath6kl_core_init+0xa0/0x42c) from [<80367f6c>]
(ath6kl_usb_probe+0x478/0x5a8)
[<80367f6c>] (ath6kl_usb_probe+0x478/0x5a8) from [<803766b8>]
(usb_probe_interface+0xc8/0x190)
[<803766b8>] (usb_probe_interface+0xc8/0x190) from [<802bd8c4>]
(driver_probe_device+0x6c/0x1f8)
[<802bd8c4>] (driver_probe_device+0x6c/0x1f8) from [<802bc000>]
(bus_for_each_drv+0x44/0x8c)
[<802bc000>] (bus_for_each_drv+0x44/0x8c) from [<802bd82c>]
(device_attach+0x74/0x80)
[<802bd82c>] (device_attach+0x74/0x80) from [<802bcebc>]
(bus_probe_device+0x84/0xa8)
[<802bcebc>] (bus_probe_device+0x84/0xa8) from [<802bb710>]
(device_add+0x4c0/0x58c)
[<802bb710>] (device_add+0x4c0/0x58c) from [<80375554>]
(usb_set_configuration+0x550/0x774)
[<80375554>] (usb_set_configuration+0x550/0x774) from [<8037d320>]
(generic_probe+0x2c/0x70)
[<8037d320>] (generic_probe+0x2c/0x70) from [<80375834>]
(usb_probe_device+0x14/0x18)
[<80375834>] (usb_probe_device+0x14/0x18) from [<802bd8c4>]
(driver_probe_device+0x6c/0x1f8)
[<802bd8c4>] (driver_probe_device+0x6c/0x1f8) from [<802bc000>]
(bus_for_each_drv+0x44/0x8c)
[<802bc000>] (bus_for_each_drv+0x44/0x8c) from [<802bd82c>]
(device_attach+0x74/0x80)
[<802bd82c>] (device_attach+0x74/0x80) from [<802bcebc>]
(bus_probe_device+0x84/0xa8)
[<802bcebc>] (bus_probe_device+0x84/0xa8) from [<802bb710>]
(device_add+0x4c0/0x58c)
[<802bb710>] (device_add+0x4c0/0x58c) from [<8036e8d0>]
(usb_new_device+0x11c/0x17c)
[<8036e8d0>] (usb_new_device+0x11c/0x17c) from [<8036f41c>]
(hub_thread+0xaec/0x1208)
[<8036f41c>] (hub_thread+0xaec/0x1208) from [<80042a94>] (kthread+0xa4/0xb0)
[<80042a94>] (kthread+0xa4/0xb0) from [<8000e400>] (ret_from_fork+0x18/0x38)


Any idea why it come to this?

Best regards,
Drasko

^ permalink raw reply

* Re: [PATCH] cfg80211: allow beaconing after CAC
From: Luis R. Rodriguez @ 2013-10-29 10:45 UTC (permalink / raw)
  To: Janusz Dziedzic; +Cc: linux-wireless, Johannes Berg
In-Reply-To: <CALhHN=oThh7HMW5cHQmW_U0yrzN5x3+ZW6a+yd9fc8fyjcuL+w@mail.gmail.com>

On Tue, Oct 29, 2013 at 11:32 AM, Janusz Dziedzic
<janusz.dziedzic@tieto.com> wrote:
> On 29 October 2013 11:25, Luis R. Rodriguez <mcgrof@do-not-panic.com> wrote:
>> How do we know that prohibited_flags won't have IEEE80211_CHAN_RADAR
>> or IEEE80211_CHAN_NO_IBSS set?
>>
>> Also why is IEEE80211_CHAN_NO_IBSS used and not IEEE80211_CHAN_NO_IR
>> instead? I sent patches to help clarify this situation around usage of
>> both no-ibss and active scan flags, by merging them to no-ir. I'm not
>> sure of the status of those patches going in.
>>
>
> I am not sure.

OK please try to be for changes like these.

> Even without my patch seems  IEEE80211_CHAN_RADAR is
> added to prohibited_flags and have different meaning (I think).

IEEE80211_CHAN_RADAR has only one meaning, its not subjective.

> In orginal code we have:
>
> if (c->flags & prohibited_flags & ~IEEE80211_CHAN_RADAR)
>
> So, we skiping CHAN_RADAR anyway? I am not sure we should change
> prohibited_flags to different name or stop skiping CHAN_RADAR and
> modify upper layer - cfg80211_reg_can_beacon() in such case.

I took a look and I'd prefer that this be dealt with elsewhere, in
particular you are right that cfg80211_reg_can_beacon() seems like a
much more suitable place for this check now.

  Luis

^ permalink raw reply

* [PATCH 1/3] ath10k: add phyerr/dfs handling
From: Marek Puzyniak @ 2013-10-29 12:06 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless, Janusz Dziedzic

From: Janusz Dziedzic <janusz.dziedzic@tieto.com>

Handle phyerr, dfs event, radar_report and fft_report.
Add also debugfs dfs_simulate_radar and dfs_stats files.
Use ath dfs pattern detector.

Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com>
---
 drivers/net/wireless/ath/ath10k/core.h  |  13 ++
 drivers/net/wireless/ath/ath10k/debug.c |  87 ++++++++++++
 drivers/net/wireless/ath/ath10k/debug.h |   3 +
 drivers/net/wireless/ath/ath10k/mac.c   |  24 ++++
 drivers/net/wireless/ath/ath10k/wmi.c   | 244 +++++++++++++++++++++++++++++++-
 drivers/net/wireless/ath/ath10k/wmi.h   |  85 +++++++++++
 6 files changed, 455 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 0934f76..671dbe3 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -30,6 +30,7 @@
 #include "wmi.h"
 #include "../ath.h"
 #include "../regd.h"
+#include "../dfs_pattern_detector.h"
 
 #define MS(_v, _f) (((_v) & _f##_MASK) >> _f##_LSB)
 #define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK)
@@ -192,6 +193,14 @@ struct ath10k_target_stats {
 
 };
 
+struct ath10k_dfs_stats {
+	u32 phy_errors;
+	u32 pulses_total;
+	u32 pulses_detected;
+	u32 pulses_discarded;
+	u32 radar_detected;
+};
+
 #define ATH10K_MAX_NUM_PEER_IDS (1 << 11) /* htt rx_desc limit */
 
 struct ath10k_peer {
@@ -261,6 +270,8 @@ struct ath10k_debug {
 
 	unsigned long htt_stats_mask;
 	struct delayed_work htt_stats_dwork;
+	struct ath10k_dfs_stats dfs_stats;
+	struct ath_dfs_pool_stats dfs_pool_stats;
 };
 
 enum ath10k_state {
@@ -428,6 +439,8 @@ struct ath10k {
 	u32 survey_last_cycle_count;
 	struct survey_info survey[ATH10K_NUM_CHANS];
 
+	struct dfs_pattern_detector *dfs_detector;
+
 #ifdef CONFIG_ATH10K_DEBUGFS
 	struct ath10k_debug debug;
 #endif
diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c
index 760ff22..d4d0cdd 100644
--- a/drivers/net/wireless/ath/ath10k/debug.c
+++ b/drivers/net/wireless/ath/ath10k/debug.c
@@ -21,6 +21,14 @@
 #include "core.h"
 #include "debug.h"
 
+#define ATH10K_DFS_STAT(s, p) (\
+	len += scnprintf(buf + len, size - len, "%28s : %10u\n", s, \
+			 ar->debug.dfs_stats.p))
+
+#define ATH10K_DFS_POOL_STAT(s, p) (\
+	len += scnprintf(buf + len, size - len, "%28s : %10u\n", s, \
+			 ar->debug.dfs_pool_stats.p))
+
 /* ms */
 #define ATH10K_DEBUG_HTT_STATS_INTERVAL 1000
 
@@ -639,6 +647,75 @@ void ath10k_debug_stop(struct ath10k *ar)
 		cancel_delayed_work(&ar->debug.htt_stats_dwork);
 }
 
+static ssize_t ath10k_write_simulate_radar(struct file *file,
+					   const char __user *user_buf,
+					   size_t count, loff_t *ppos)
+{
+	struct ath10k *ar = file->private_data;
+
+	ieee80211_radar_detected(ar->hw);
+
+	return count;
+}
+
+static const struct file_operations fops_simulate_radar = {
+	.write = ath10k_write_simulate_radar,
+	.open = simple_open,
+	.owner = THIS_MODULE,
+	.llseek = default_llseek,
+};
+
+static ssize_t ath10k_read_file_dfs(struct file *file, char __user *user_buf,
+				    size_t count, loff_t *ppos)
+{
+	int retval = 0, size = 8000, len = 0;
+	struct ath10k *ar = file->private_data;
+	char *buf;
+
+	buf = kzalloc(size, GFP_KERNEL);
+	if (buf == NULL)
+		return -ENOMEM;
+
+	if (!ar->dfs_detector) {
+		len += scnprintf(buf + len, size - len, "DFS not enabled\n");
+		goto exit;
+	}
+
+	ar->debug.dfs_pool_stats = ar->dfs_detector->get_stats(ar->dfs_detector);
+
+	len += scnprintf(buf + len, size - len, "Pulse detector statistics:\n");
+	ATH10K_DFS_STAT("reported phy errors     ", phy_errors);
+	ATH10K_DFS_STAT("pulse events reported   ", pulses_total);
+	ATH10K_DFS_STAT("DFS pulses detected     ", pulses_detected);
+	ATH10K_DFS_STAT("DFS pulses discarded    ", pulses_discarded);
+	ATH10K_DFS_STAT("Radars detected         ", radar_detected);
+
+	len += scnprintf(buf + len, size - len, "Global Pool statistics:\n");
+	ATH10K_DFS_POOL_STAT("Pool references         ", pool_reference);
+	ATH10K_DFS_POOL_STAT("Pulses allocated        ", pulse_allocated);
+	ATH10K_DFS_POOL_STAT("Pulses alloc error      ", pulse_alloc_error);
+	ATH10K_DFS_POOL_STAT("Pulses in use           ", pulse_used);
+	ATH10K_DFS_POOL_STAT("Seqs. allocated         ", pseq_allocated);
+	ATH10K_DFS_POOL_STAT("Seqs. alloc error       ", pseq_alloc_error);
+	ATH10K_DFS_POOL_STAT("Seqs. in use            ", pseq_used);
+
+exit:
+	if (len > size)
+		len = size;
+
+	retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+	kfree(buf);
+
+	return retval;
+}
+
+static const struct file_operations fops_dfs_stats = {
+	.read = ath10k_read_file_dfs,
+	.open = simple_open,
+	.owner = THIS_MODULE,
+	.llseek = default_llseek,
+};
+
 int ath10k_debug_create(struct ath10k *ar)
 {
 	ar->debug.debugfs_phy = debugfs_create_dir("ath10k",
@@ -667,6 +744,16 @@ int ath10k_debug_create(struct ath10k *ar)
 	debugfs_create_file("htt_stats_mask", S_IRUSR, ar->debug.debugfs_phy,
 			    ar, &fops_htt_stats_mask);
 
+	if (config_enabled(CONFIG_ATH10K_DFS_CERTIFIED)) {
+		debugfs_create_file("dfs_simulate_radar", S_IWUSR,
+				    ar->debug.debugfs_phy, ar,
+				    &fops_simulate_radar);
+
+		debugfs_create_file("dfs_stats", S_IRUSR,
+				    ar->debug.debugfs_phy, ar,
+				    &fops_dfs_stats);
+	}
+
 	return 0;
 }
 
diff --git a/drivers/net/wireless/ath/ath10k/debug.h b/drivers/net/wireless/ath/ath10k/debug.h
index 46e640a..cde53d6 100644
--- a/drivers/net/wireless/ath/ath10k/debug.h
+++ b/drivers/net/wireless/ath/ath10k/debug.h
@@ -33,6 +33,7 @@ enum ath10k_debug_mask {
 	ATH10K_DBG_MGMT		= 0x00000100,
 	ATH10K_DBG_DATA		= 0x00000200,
 	ATH10K_DBG_BMI		= 0x00000400,
+	ATH10K_DBG_REGULATORY	= 0x00000800,
 	ATH10K_DBG_ANY		= 0xffffffff,
 };
 
@@ -53,6 +54,7 @@ void ath10k_debug_read_service_map(struct ath10k *ar,
 void ath10k_debug_read_target_stats(struct ath10k *ar,
 				    struct wmi_stats_event *ev);
 
+#define ATH10K_DFS_STAT_INC(ar, c) (ar->debug.dfs_stats.c++)
 #else
 static inline int ath10k_debug_start(struct ath10k *ar)
 {
@@ -82,6 +84,7 @@ static inline void ath10k_debug_read_target_stats(struct ath10k *ar,
 						  struct wmi_stats_event *ev)
 {
 }
+#define ATH10K_DFS_STAT_INC(ar, c) do { } while (0)
 #endif /* CONFIG_ATH10K_DEBUGFS */
 
 #ifdef CONFIG_ATH10K_DEBUG
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index bbb0efa..79f8bfd 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -1438,9 +1438,20 @@ static void ath10k_reg_notifier(struct wiphy *wiphy,
 {
 	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
 	struct ath10k *ar = hw->priv;
+	bool result;
 
 	ath_reg_notifier_apply(wiphy, request, &ar->ath_common.regulatory);
 
+	if (config_enabled(CONFIG_ATH10K_DFS_CERTIFIED) && ar->dfs_detector) {
+		ath10k_dbg(ATH10K_DBG_REGULATORY, "dfs region 0x%X\n",
+			   request->dfs_region);
+		result = ar->dfs_detector->set_dfs_domain(ar->dfs_detector,
+							  request->dfs_region);
+		if (!result)
+			ath10k_warn("dfs region 0x%X not supported, will trigger radar for every pulse\n",
+				    request->dfs_region);
+	}
+
 	mutex_lock(&ar->conf_mutex);
 	if (ar->state == ATH10K_STATE_ON)
 		ath10k_regd_update(ar);
@@ -3525,6 +3536,16 @@ int ath10k_mac_register(struct ath10k *ar)
 
 	ar->hw->netdev_features = NETIF_F_HW_CSUM;
 
+	if (config_enabled(CONFIG_ATH10K_DFS_CERTIFIED)) {
+		/* Init ath dfs pattern detector */
+		ar->ath_common.debug_mask = ATH_DBG_DFS;
+		ar->dfs_detector = dfs_pattern_detector_init(&ar->ath_common,
+							     NL80211_DFS_UNSET);
+
+		if (!ar->dfs_detector)
+			ath10k_warn("dfs pattern detector init failed\n");
+	}
+
 	ret = ath_regd_init(&ar->ath_common.regulatory, ar->hw->wiphy,
 			    ath10k_reg_notifier);
 	if (ret) {
@@ -3560,6 +3581,9 @@ void ath10k_mac_unregister(struct ath10k *ar)
 {
 	ieee80211_unregister_hw(ar->hw);
 
+	if (config_enabled(CONFIG_ATH10K_DFS_CERTIFIED) && ar->dfs_detector)
+		ar->dfs_detector->exit(ar->dfs_detector);
+
 	kfree(ar->mac.sbands[IEEE80211_BAND_2GHZ].channels);
 	kfree(ar->mac.sbands[IEEE80211_BAND_5GHZ].channels);
 
diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c
index a796d0b..b7dcdce 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.c
+++ b/drivers/net/wireless/ath/ath10k/wmi.c
@@ -1383,9 +1383,251 @@ static void ath10k_wmi_event_tbttoffset_update(struct ath10k *ar,
 	ath10k_dbg(ATH10K_DBG_WMI, "WMI_TBTTOFFSET_UPDATE_EVENTID\n");
 }
 
+static void ath10k_dfs_radar_report(struct ath10k *ar,
+				    struct wmi_single_phyerr_rx_event *event,
+				    struct phyerr_radar_report *rr,
+				    u64 tsf)
+{
+	u32 reg0, reg1, tsf32l;
+	struct pulse_event pe;
+	u64 tsf64;
+	u8 rssi, width;
+
+	reg0 = __le32_to_cpu(rr->reg0);
+	reg1 = __le32_to_cpu(rr->reg1);
+
+	ath10k_dbg(ATH10K_DBG_REGULATORY,
+		   "wmi phyerr radar report chirp %d max_width %d agc_total_gain %d pulse_delta_diff %d\n",
+		   MS(reg0, RADAR_REPORT_REG0_PULSE_IS_CHIRP),
+		   MS(reg0, RADAR_REPORT_REG0_PULSE_IS_MAX_WIDTH),
+		   MS(reg0, RADAR_REPORT_REG0_AGC_TOTAL_GAIN),
+		   MS(reg0, RADAR_REPORT_REG0_PULSE_DELTA_DIFF));
+	ath10k_dbg(ATH10K_DBG_REGULATORY,
+		   "wmi phyerr radar report pulse_delta_pean %d pulse_sidx %d fft_valid %d agc_mb_gain %d subchan_mask %d\n",
+		   MS(reg0, RADAR_REPORT_REG0_PULSE_DELTA_PEAK),
+		   MS(reg0, RADAR_REPORT_REG0_PULSE_SIDX),
+		   MS(reg1, RADAR_REPORT_REG1_PULSE_SRCH_FFT_VALID),
+		   MS(reg1, RADAR_REPORT_REG1_PULSE_AGC_MB_GAIN),
+		   MS(reg1, RADAR_REPORT_REG1_PULSE_SUBCHAN_MASK));
+	ath10k_dbg(ATH10K_DBG_REGULATORY,
+		   "wmi phyerr radar report pulse_tsf_offset 0x%X pulse_dur: %d\n",
+		   MS(reg1, RADAR_REPORT_REG1_PULSE_TSF_OFFSET),
+		   MS(reg1, RADAR_REPORT_REG1_PULSE_DUR));
+
+	if (!ar->dfs_detector)
+		return;
+
+	/* report event to DFS pattern detector */
+	tsf32l = __le32_to_cpu(event->hdr.tsf_timestamp);
+	tsf64 = tsf & (~0xFFFFFFFFULL);
+	tsf64 |= tsf32l;
+
+	width = MS(reg1, RADAR_REPORT_REG1_PULSE_DUR);
+	rssi = event->hdr.rssi_combined;
+
+	/*
+	 * hardware store this as 8 bit signed value,
+	 * set to zero if negative number
+	 */
+	if (rssi & 0x80)
+		rssi = 0;
+
+	pe.ts = tsf64;
+	pe.freq = ar->hw->conf.chandef.chan->center_freq;
+	pe.width = width;
+	pe.rssi = rssi;
+
+	ath10k_dbg(ATH10K_DBG_REGULATORY,
+		   "dfs add pulse freq: %d, width: %d, rssi %d, tsf: %llX\n",
+		   pe.freq, pe.width, pe.rssi, pe.ts);
+
+	ATH10K_DFS_STAT_INC(ar, pulses_detected);
+
+	if (!ar->dfs_detector->add_pulse(ar->dfs_detector, &pe)) {
+		ath10k_dbg(ATH10K_DBG_REGULATORY,
+			   "dfs no pulse pattern detected, yet\n");
+		return;
+	}
+
+	ath10k_dbg(ATH10K_DBG_REGULATORY, "dfs radar detected\n");
+	ATH10K_DFS_STAT_INC(ar, radar_detected);
+	ieee80211_radar_detected(ar->hw);
+}
+
+static int ath10k_dfs_fft_report(struct ath10k *ar,
+				 struct wmi_single_phyerr_rx_event *event,
+				 struct phyerr_fft_report *fftr,
+				 u64 tsf)
+{
+	u32 reg0, reg1;
+	u8 rssi, peak_mag;
+
+	reg0 = __le32_to_cpu(fftr->reg0);
+	reg1 = __le32_to_cpu(fftr->reg1);
+	rssi = event->hdr.rssi_combined;
+
+	ath10k_dbg(ATH10K_DBG_REGULATORY,
+		   "wmi phyerr fft report total_gain_db %d base_pwr_db %d fft_chn_idx %d peak_sidx %d\n",
+		   MS(reg0, SEARCH_FFT_REPORT_REG0_TOTAL_GAIN_DB),
+		   MS(reg0, SEARCH_FFT_REPORT_REG0_BASE_PWR_DB),
+		   MS(reg0, SEARCH_FFT_REPORT_REG0_FFT_CHN_IDX),
+		   MS(reg0, SEARCH_FFT_REPORT_REG0_PEAK_SIDX));
+	ath10k_dbg(ATH10K_DBG_REGULATORY,
+		   "wmi phyerr fft report rel_pwr_db %d avgpwr_db %d peak_mag %d num_store_bin %d\n",
+		   MS(reg1, SEARCH_FFT_REPORT_REG1_RELPWR_DB),
+		   MS(reg1, SEARCH_FFT_REPORT_REG1_AVGPWR_DB),
+		   MS(reg1, SEARCH_FFT_REPORT_REG1_PEAK_MAG),
+		   MS(reg1, SEARCH_FFT_REPORT_REG1_NUM_STR_BINS_IB));
+
+	peak_mag = MS(reg1, SEARCH_FFT_REPORT_REG1_PEAK_MAG);
+
+	/* false event detection */
+	if (rssi == DFS_RSSI_POSSIBLY_FALSE &&
+	    peak_mag < 2 * DFS_PEAK_MAG_THOLD_POSSIBLY_FALSE) {
+		ath10k_dbg(ATH10K_DBG_REGULATORY, "dfs false pulse detected\n");
+		ATH10K_DFS_STAT_INC(ar, pulses_discarded);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void ath10k_wmi_event_dfs(struct ath10k *ar,
+				 struct wmi_single_phyerr_rx_event *event,
+				 u64 tsf)
+{
+	int buf_len, tlv_len, res, i = 0;
+	struct phyerr_tlv *tlv;
+	struct phyerr_radar_report *rr;
+	struct phyerr_fft_report *fftr;
+	u8 *tlv_buf;
+
+	buf_len = __le32_to_cpu(event->hdr.buf_len);
+	ath10k_dbg(ATH10K_DBG_REGULATORY,
+		   "wmi event dfs err_code %d rssi %d tsfl 0x%X tsf64 0x%llX len %d\n",
+		   event->hdr.phy_err_code, event->hdr.rssi_combined,
+		   __le32_to_cpu(event->hdr.tsf_timestamp), tsf, buf_len);
+
+	/* Skip event if DFS disabled */
+	if (!config_enabled(CONFIG_ATH10K_DFS_CERTIFIED))
+		return;
+
+	ATH10K_DFS_STAT_INC(ar, pulses_total);
+
+	while (i < buf_len) {
+		if (i + sizeof(*tlv) > buf_len) {
+			ath10k_warn("too short buf for tlv header (%d)\n", i);
+			return;
+		}
+
+		tlv = (struct phyerr_tlv *)&event->bufp[i];
+		tlv_len = __le16_to_cpu(tlv->len);
+		tlv_buf = &event->bufp[i + sizeof(*tlv)];
+		ath10k_dbg(ATH10K_DBG_REGULATORY,
+			   "wmi event dfs tlv_len %d tlv_tag 0x%02X tlv_sig 0x%02X\n",
+			   tlv_len, tlv->tag, tlv->sig);
+
+		switch (tlv->tag) {
+		case PHYERR_TLV_TAG_RADAR_PULSE_SUMMARY:
+			if (i + sizeof(*tlv) + sizeof(*rr) > buf_len) {
+				ath10k_warn("too short radar pulse summary (%d)\n", i);
+				return;
+			}
+
+			rr = (struct phyerr_radar_report *)tlv_buf;
+			ath10k_dfs_radar_report(ar, event, rr, tsf);
+			break;
+		case PHYERR_TLV_TAG_SEARCH_FFT_REPORT:
+			if (i + sizeof(*tlv) + sizeof(*fftr) > buf_len) {
+				ath10k_warn("too short fft report (%d)\n", i);
+				return;
+			}
+
+			fftr = (struct phyerr_fft_report *)tlv_buf;
+			res = ath10k_dfs_fft_report(ar, event, fftr, tsf);
+			if (res)
+				return;
+			break;
+		}
+
+		i += sizeof(*tlv) + tlv_len;
+	}
+}
+
+static void ath10k_wmi_event_spectral_scan(struct ath10k *ar,
+				struct wmi_single_phyerr_rx_event *event,
+				u64 tsf)
+{
+	ath10k_dbg(ATH10K_DBG_WMI, "wmi event spectral scan\n");
+}
+
 static void ath10k_wmi_event_phyerr(struct ath10k *ar, struct sk_buff *skb)
 {
-	ath10k_dbg(ATH10K_DBG_WMI, "WMI_PHYERR_EVENTID\n");
+	struct wmi_comb_phyerr_rx_event *comb_event;
+	struct wmi_single_phyerr_rx_event *event;
+	u32 count, i, buf_len, phy_err_code;
+	u64 tsf;
+	int left_len = skb->len;
+
+	ATH10K_DFS_STAT_INC(ar, phy_errors);
+
+	/* Check if combined event available */
+	if (left_len < sizeof(*comb_event)) {
+		ath10k_warn("wmi phyerr combined event wrong len\n");
+		return;
+	}
+
+	left_len -= sizeof(*comb_event);
+
+	/* Check number of included events */
+	comb_event = (struct wmi_comb_phyerr_rx_event *)skb->data;
+	count = __le32_to_cpu(comb_event->hdr.num_phyerr_events);
+
+	tsf = __le32_to_cpu(comb_event->hdr.tsf_u32);
+	tsf <<= 32;
+	tsf |= __le32_to_cpu(comb_event->hdr.tsf_l32);
+
+	ath10k_dbg(ATH10K_DBG_WMI,
+		   "wmi event phyerr count %d tsf64 0x%llX\n",
+		   count, tsf);
+
+	event = (struct wmi_single_phyerr_rx_event *)comb_event->bufp;
+	for (i = 0; i < count; i++) {
+		/* Check if we can read event header */
+		if (left_len < sizeof(*event)) {
+			ath10k_warn("single event (%d) wrong head len\n", i);
+			return;
+		}
+
+		left_len -= sizeof(*event);
+
+		buf_len = __le32_to_cpu(event->hdr.buf_len);
+		phy_err_code = event->hdr.phy_err_code;
+
+		if (left_len < buf_len) {
+			ath10k_warn("single event (%d) wrong buf len\n", i);
+			return;
+		}
+
+		left_len -= buf_len;
+
+		switch (phy_err_code) {
+		case PHY_ERROR_RADAR:
+			ath10k_wmi_event_dfs(ar, event, tsf);
+			break;
+		case PHY_ERROR_SPECTRAL_SCAN:
+			ath10k_wmi_event_spectral_scan(ar, event, tsf);
+			break;
+		case PHY_ERROR_FALSE_RADAR_EXT:
+			ath10k_wmi_event_dfs(ar, event, tsf);
+			ath10k_wmi_event_spectral_scan(ar, event, tsf);
+			break;
+		default:
+			break;
+		}
+
+		event += sizeof(*event) + buf_len;
+	}
 }
 
 static void ath10k_wmi_event_roam(struct ath10k *ar, struct sk_buff *skb)
diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h
index 81e33a7..ad53333 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.h
+++ b/drivers/net/wireless/ath/ath10k/wmi.h
@@ -1978,6 +1978,10 @@ struct wmi_mgmt_rx_event_v2 {
 #define WMI_RX_STATUS_ERR_MIC			0x10
 #define WMI_RX_STATUS_ERR_KEY_CACHE_MISS	0x20
 
+#define PHY_ERROR_SPECTRAL_SCAN		0x26
+#define PHY_ERROR_FALSE_RADAR_EXT		0x24
+#define PHY_ERROR_RADAR				0x05
+
 struct wmi_single_phyerr_rx_hdr {
 	/* TSF timestamp */
 	__le32 tsf_timestamp;
@@ -2069,6 +2073,87 @@ struct wmi_comb_phyerr_rx_event {
 	u8 bufp[0];
 } __packed;
 
+#define PHYERR_TLV_SIG				0xBB
+#define PHYERR_TLV_TAG_SEARCH_FFT_REPORT	0xFB
+#define PHYERR_TLV_TAG_RADAR_PULSE_SUMMARY	0xF8
+
+struct phyerr_radar_report {
+	__le32 reg0; /* RADAR_REPORT_REG0_* */
+	__le32 reg1; /* REDAR_REPORT_REG1_* */
+} __packed;
+
+#define RADAR_REPORT_REG0_PULSE_IS_CHIRP_MASK		0x80000000
+#define RADAR_REPORT_REG0_PULSE_IS_CHIRP_LSB		31
+
+#define RADAR_REPORT_REG0_PULSE_IS_MAX_WIDTH_MASK	0x40000000
+#define RADAR_REPORT_REG0_PULSE_IS_MAX_WIDTH_LSB	30
+
+#define RADAR_REPORT_REG0_AGC_TOTAL_GAIN_MASK		0x3FF00000
+#define RADAR_REPORT_REG0_AGC_TOTAL_GAIN_LSB		20
+
+#define RADAR_REPORT_REG0_PULSE_DELTA_DIFF_MASK		0x000F0000
+#define RADAR_REPORT_REG0_PULSE_DELTA_DIFF_LSB		16
+
+#define RADAR_REPORT_REG0_PULSE_DELTA_PEAK_MASK		0x0000FC00
+#define RADAR_REPORT_REG0_PULSE_DELTA_PEAK_LSB		10
+
+#define RADAR_REPORT_REG0_PULSE_SIDX_MASK		0x000003FF
+#define RADAR_REPORT_REG0_PULSE_SIDX_LSB		0
+
+#define RADAR_REPORT_REG1_PULSE_SRCH_FFT_VALID_MASK	0x80000000
+#define RADAR_REPORT_REG1_PULSE_SRCH_FFT_VALID_LSB	31
+
+#define RADAR_REPORT_REG1_PULSE_AGC_MB_GAIN_MASK	0x7F000000
+#define RADAR_REPORT_REG1_PULSE_AGC_MB_GAIN_LSB		24
+
+#define RADAR_REPORT_REG1_PULSE_SUBCHAN_MASK_MASK	0x00FF0000
+#define RADAR_REPORT_REG1_PULSE_SUBCHAN_MASK_LSB	16
+
+#define RADAR_REPORT_REG1_PULSE_TSF_OFFSET_MASK		0x0000FF00
+#define RADAR_REPORT_REG1_PULSE_TSF_OFFSET_LSB		8
+
+#define RADAR_REPORT_REG1_PULSE_DUR_MASK		0x000000FF
+#define RADAR_REPORT_REG1_PULSE_DUR_LSB			0
+
+struct phyerr_fft_report {
+	__le32 reg0; /* SEARCH_FFT_REPORT_REG0_ * */
+	__le32 reg1; /* SEARCH_FFT_REPORT_REG1_ * */
+} __packed;
+
+#define SEARCH_FFT_REPORT_REG0_TOTAL_GAIN_DB_MASK	0xFF800000
+#define SEARCH_FFT_REPORT_REG0_TOTAL_GAIN_DB_LSB	23
+
+#define SEARCH_FFT_REPORT_REG0_BASE_PWR_DB_MASK		0x007FC000
+#define SEARCH_FFT_REPORT_REG0_BASE_PWR_DB_LSB		14
+
+#define SEARCH_FFT_REPORT_REG0_FFT_CHN_IDX_MASK		0x00003000
+#define SEARCH_FFT_REPORT_REG0_FFT_CHN_IDX_LSB		12
+
+#define SEARCH_FFT_REPORT_REG0_PEAK_SIDX_MASK		0x00000FFF
+#define SEARCH_FFT_REPORT_REG0_PEAK_SIDX_LSB		0
+
+#define SEARCH_FFT_REPORT_REG1_RELPWR_DB_MASK		0xFC000000
+#define SEARCH_FFT_REPORT_REG1_RELPWR_DB_LSB		26
+
+#define SEARCH_FFT_REPORT_REG1_AVGPWR_DB_MASK		0x03FC0000
+#define SEARCH_FFT_REPORT_REG1_AVGPWR_DB_LSB		18
+
+#define SEARCH_FFT_REPORT_REG1_PEAK_MAG_MASK		0x0003FF00
+#define SEARCH_FFT_REPORT_REG1_PEAK_MAG_LSB		8
+
+#define SEARCH_FFT_REPORT_REG1_NUM_STR_BINS_IB_MASK	0x000000FF
+#define SEARCH_FFT_REPORT_REG1_NUM_STR_BINS_IB_LSB	0
+
+
+struct phyerr_tlv {
+	__le16 len;
+	u8 tag;
+	u8 sig;
+} __packed;
+
+#define DFS_RSSI_POSSIBLY_FALSE			50
+#define DFS_PEAK_MAG_THOLD_POSSIBLY_FALSE	40
+
 struct wmi_mgmt_tx_hdr {
 	__le32 vdev_id;
 	struct wmi_mac_addr peer_macaddr;
-- 
1.8.1.2


^ permalink raw reply related

* [PATCH 2/3] ath10k: introduce DFS implementation
From: Marek Puzyniak @ 2013-10-29 12:06 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless, Marek Puzyniak, Michal Kazior
In-Reply-To: <1383048394-15256-1-git-send-email-marek.puzyniak@tieto.com>

Configure interface combination for AP running on channels
where radar detection is required. It allows only one type
of interface - AP on DFS channel and limits number of AP
interfaces to 8. Setup WMI channel flags accordingly to mac
channel configuration. CAC based on additional monitor vdev
is started if required for current channel.

Signed-off-by: Marek Puzyniak <marek.puzyniak@tieto.com>
Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/Kconfig  |   7 ++
 drivers/net/wireless/ath/ath10k/core.h   |   6 ++
 drivers/net/wireless/ath/ath10k/htt_rx.c |   5 +
 drivers/net/wireless/ath/ath10k/mac.c    | 156 +++++++++++++++++++++++++++++--
 drivers/net/wireless/ath/ath10k/wmi.c    |  23 ++++-
 drivers/net/wireless/ath/ath10k/wmi.h    |   1 +
 6 files changed, 185 insertions(+), 13 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/Kconfig b/drivers/net/wireless/ath/ath10k/Kconfig
index 82e8088..f51b8e9 100644
--- a/drivers/net/wireless/ath/ath10k/Kconfig
+++ b/drivers/net/wireless/ath/ath10k/Kconfig
@@ -37,3 +37,10 @@ config ATH10K_TRACING
 	---help---
 	  Select this to ath10k use tracing infrastructure.
 
+config ATH10K_DFS_CERTIFIED
+	bool "Atheros DFS support for certified platforms"
+	depends on ATH10K && CFG80211_CERTIFICATION_ONUS
+	default n
+	---help---
+	  This option enables DFS support for initiating radiation on
+	  ath10k.
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 671dbe3..d5fd74b 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -310,6 +310,11 @@ enum ath10k_fw_features {
 	ATH10K_FW_FEATURE_COUNT,
 };
 
+enum ath10k_dev_flags {
+	/* Indicates that ath10k device is during CAC phase of DFS */
+	ATH10K_CAC_RUNNING,
+};
+
 struct ath10k {
 	struct ath_common ath_common;
 	struct ieee80211_hw *hw;
@@ -403,6 +408,7 @@ struct ath10k {
 	bool monitor_enabled;
 	bool monitor_present;
 	unsigned int filter_flags;
+	unsigned long dev_flags;
 
 	struct wmi_pdev_set_wmm_params_arg wmm_params;
 	struct completion install_key_done;
diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c
index 90d4f74..d619291 100644
--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
@@ -959,6 +959,11 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
 				continue;
 			}
 
+			if (test_bit(ATH10K_CAC_RUNNING, &htt->ar->dev_flags)) {
+				ath10k_htt_rx_free_msdu_chain(msdu_head);
+				continue;
+			}
+
 			/* FIXME: we do not support chaining yet.
 			 * this needs investigation */
 			if (msdu_chaining) {
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 79f8bfd..4c62201 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -459,6 +459,10 @@ static int ath10k_vdev_start(struct ath10k_vif *arvif)
 		arg.ssid = arvif->u.ap.ssid;
 		arg.ssid_len = arvif->u.ap.ssid_len;
 		arg.hidden_ssid = arvif->u.ap.hidden_ssid;
+
+		/* For now allow DFS for AP mode */
+		arg.channel.chan_radar =
+			!!(channel->flags & IEEE80211_CHAN_RADAR);
 	} else if (arvif->vdev_type == WMI_VDEV_TYPE_IBSS) {
 		arg.ssid = arvif->vif->bss_conf.ssid;
 		arg.ssid_len = arvif->vif->bss_conf.ssid_len;
@@ -528,6 +532,8 @@ static int ath10k_monitor_start(struct ath10k *ar, int vdev_id)
 	/* TODO setup this dynamically, what in case we
 	   don't have any vifs? */
 	arg.channel.mode = chan_to_phymode(&ar->hw->conf.chandef);
+	arg.channel.chan_radar =
+			!!(channel->flags & IEEE80211_CHAN_RADAR);
 
 	arg.channel.min_power = 0;
 	arg.channel.max_power = channel->max_power * 2;
@@ -662,6 +668,107 @@ static int ath10k_monitor_destroy(struct ath10k *ar)
 	return ret;
 }
 
+static int ath10k_start_cac(struct ath10k *ar)
+{
+	int ret;
+
+	lockdep_assert_held(&ar->conf_mutex);
+
+	set_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
+
+	ret = ath10k_monitor_create(ar);
+	if (ret) {
+		clear_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
+		return ret;
+	}
+
+	ret = ath10k_monitor_start(ar, ar->monitor_vdev_id);
+	if (ret) {
+		clear_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
+		ath10k_monitor_destroy(ar);
+		return ret;
+	}
+
+	ath10k_dbg(ATH10K_DBG_MAC, "mac cac start monitor vdev %d\n",
+		   ar->monitor_vdev_id);
+
+	return 0;
+}
+
+static int ath10k_stop_cac(struct ath10k *ar)
+{
+	lockdep_assert_held(&ar->conf_mutex);
+
+	/* CAC is not running - do nothing */
+	if (!test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags))
+		return 0;
+
+	ath10k_monitor_stop(ar);
+	ath10k_monitor_destroy(ar);
+	clear_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
+
+	ath10k_dbg(ATH10K_DBG_MAC, "mac cac finished\n");
+
+	return 0;
+}
+
+static const char *ath10k_dfs_state(enum nl80211_dfs_state dfs_state)
+{
+	switch (dfs_state) {
+	case NL80211_DFS_USABLE:
+		return "USABLE";
+	case NL80211_DFS_UNAVAILABLE:
+		return "UNAVAILABLE";
+	case NL80211_DFS_AVAILABLE:
+		return "AVAILABLE";
+	default:
+		WARN_ON(1);
+		return "bug";
+	}
+}
+
+static void ath10k_config_radar_detection(struct ath10k *ar)
+{
+	struct ieee80211_channel *chan = ar->hw->conf.chandef.chan;
+	bool radar = ar->hw->conf.radar_enabled;
+	bool chan_radar = !!(chan->flags & IEEE80211_CHAN_RADAR);
+	enum nl80211_dfs_state dfs_state = chan->dfs_state;
+	int ret;
+
+	lockdep_assert_held(&ar->conf_mutex);
+
+	ath10k_dbg(ATH10K_DBG_MAC,
+		   "mac radar config update: chan %dMHz radar %d chan radar %d chan state %s\n",
+		   chan->center_freq, radar, chan_radar,
+		   ath10k_dfs_state(dfs_state));
+
+	/*
+	 * It's safe to call it even if CAC is not started.
+	 * This call here guarantees changing channel, etc. will stop CAC.
+	 */
+	ath10k_stop_cac(ar);
+
+	if (!radar)
+		return;
+
+	if (!chan_radar)
+		return;
+
+	if (dfs_state != NL80211_DFS_USABLE)
+		return;
+
+	ret = ath10k_start_cac(ar);
+	if (ret) {
+		/*
+		 * Not possible to start CAC on current channel so starting
+		 * radiation is not allowed, make this channel DFS_UNAVAILABLE
+		 * by indicating that radar was detected.
+		 */
+		ath10k_warn("failed to start CAC (%d)\n", ret);
+		ieee80211_radar_detected(ar->hw);
+	}
+}
+
 static void ath10k_control_beaconing(struct ath10k_vif *arvif,
 				struct ieee80211_bss_conf *info)
 {
@@ -1371,6 +1478,9 @@ static int ath10k_update_channel_list(struct ath10k *ar)
 			ch->ht40plus =
 				!(channel->flags & IEEE80211_CHAN_NO_HT40PLUS);
 
+			ch->chan_radar =
+				!!(channel->flags & IEEE80211_CHAN_RADAR);
+
 			passive = channel->flags & IEEE80211_CHAN_PASSIVE_SCAN;
 			ch->passive = passive;
 
@@ -1915,6 +2025,7 @@ void ath10k_halt(struct ath10k *ar)
 {
 	lockdep_assert_held(&ar->conf_mutex);
 
+	ath10k_stop_cac(ar);
 	del_timer_sync(&ar->scan.timeout);
 	ath10k_offchan_tx_purge(ar);
 	ath10k_mgmt_over_wmi_tx_purge(ar);
@@ -2029,11 +2140,16 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
 	mutex_lock(&ar->conf_mutex);
 
 	if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
-		ath10k_dbg(ATH10K_DBG_MAC, "mac config channel %d mhz\n",
-			   conf->chandef.chan->center_freq);
+		ath10k_dbg(ATH10K_DBG_MAC,
+			   "mac config channel %d mhz flags 0x%x\n",
+			   conf->chandef.chan->center_freq,
+			   conf->chandef.chan->flags);
+
 		spin_lock_bh(&ar->data_lock);
 		ar->rx_channel = conf->chandef.chan;
 		spin_unlock_bh(&ar->data_lock);
+
+		ath10k_config_radar_detection(ar);
 	}
 
 	if (changed & IEEE80211_CONF_CHANGE_POWER) {
@@ -3302,12 +3418,32 @@ static const struct ieee80211_iface_limit ath10k_if_limits[] = {
 	},
 };
 
-static const struct ieee80211_iface_combination ath10k_if_comb = {
-	.limits = ath10k_if_limits,
-	.n_limits = ARRAY_SIZE(ath10k_if_limits),
-	.max_interfaces = 8,
-	.num_different_channels = 1,
-	.beacon_int_infra_match = true,
+static const struct ieee80211_iface_limit ath10k_if_dfs_limits[] = {
+	{
+	.max	= 8,
+	.types	= BIT(NL80211_IFTYPE_AP)
+	},
+};
+
+static const struct ieee80211_iface_combination ath10k_if_comb[] = {
+	{
+		.limits = ath10k_if_limits,
+		.n_limits = ARRAY_SIZE(ath10k_if_limits),
+		.max_interfaces = 8,
+		.num_different_channels = 1,
+		.beacon_int_infra_match = true,
+	},
+	{
+		.limits = ath10k_if_dfs_limits,
+		.n_limits = ARRAY_SIZE(ath10k_if_dfs_limits),
+		.max_interfaces = 8,
+		.num_different_channels = 1,
+		.beacon_int_infra_match = true,
+		.radar_detect_widths =	BIT(NL80211_CHAN_WIDTH_20_NOHT) |
+					BIT(NL80211_CHAN_WIDTH_20) |
+					BIT(NL80211_CHAN_WIDTH_40) |
+					BIT(NL80211_CHAN_WIDTH_80),
+	}
 };
 
 static struct ieee80211_sta_vht_cap ath10k_create_vht_cap(struct ath10k *ar)
@@ -3531,8 +3667,8 @@ int ath10k_mac_register(struct ath10k *ar)
 	 */
 	ar->hw->queues = 4;
 
-	ar->hw->wiphy->iface_combinations = &ath10k_if_comb;
-	ar->hw->wiphy->n_iface_combinations = 1;
+	ar->hw->wiphy->iface_combinations = ath10k_if_comb;
+	ar->hw->wiphy->n_iface_combinations = ARRAY_SIZE(ath10k_if_comb);
 
 	ar->hw->netdev_features = NETIF_F_HW_CSUM;
 
diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c
index b7dcdce..8a7ebcc 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.c
+++ b/drivers/net/wireless/ath/ath10k/wmi.c
@@ -909,6 +909,11 @@ static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb)
 	ath10k_dbg(ATH10K_DBG_MGMT,
 		   "event mgmt rx status %08x\n", rx_status);
 
+	if (test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags)) {
+		dev_kfree_skb(skb);
+		return 0;
+	}
+
 	if (rx_status & WMI_RX_STATUS_ERR_DECRYPT) {
 		dev_kfree_skb(skb);
 		return 0;
@@ -2304,6 +2309,7 @@ int ath10k_wmi_pdev_set_channel(struct ath10k *ar,
 {
 	struct wmi_set_channel_cmd *cmd;
 	struct sk_buff *skb;
+	u32 ch_flags = 0;
 
 	if (arg->passive)
 		return -EINVAL;
@@ -2312,10 +2318,14 @@ int ath10k_wmi_pdev_set_channel(struct ath10k *ar,
 	if (!skb)
 		return -ENOMEM;
 
+	if (arg->chan_radar)
+		ch_flags |= WMI_CHAN_FLAG_DFS;
+
 	cmd = (struct wmi_set_channel_cmd *)skb->data;
 	cmd->chan.mhz               = __cpu_to_le32(arg->freq);
 	cmd->chan.band_center_freq1 = __cpu_to_le32(arg->freq);
 	cmd->chan.mode              = arg->mode;
+	cmd->chan.flags		   |= __cpu_to_le32(ch_flags);
 	cmd->chan.min_power         = arg->min_power;
 	cmd->chan.max_power         = arg->max_power;
 	cmd->chan.reg_power         = arg->max_reg_power;
@@ -2864,6 +2874,7 @@ static int ath10k_wmi_vdev_start_restart(struct ath10k *ar,
 	struct sk_buff *skb;
 	const char *cmdname;
 	u32 flags = 0;
+	u32 ch_flags = 0;
 
 	if (cmd_id != ar->wmi.cmd->vdev_start_request_cmdid &&
 	    cmd_id != ar->wmi.cmd->vdev_restart_request_cmdid)
@@ -2890,6 +2901,8 @@ static int ath10k_wmi_vdev_start_restart(struct ath10k *ar,
 		flags |= WMI_VDEV_START_HIDDEN_SSID;
 	if (arg->pmf_enabled)
 		flags |= WMI_VDEV_START_PMF_ENABLED;
+	if (arg->channel.chan_radar)
+		ch_flags |= WMI_CHAN_FLAG_DFS;
 
 	cmd = (struct wmi_vdev_start_request_cmd *)skb->data;
 	cmd->vdev_id         = __cpu_to_le32(arg->vdev_id);
@@ -2911,6 +2924,7 @@ static int ath10k_wmi_vdev_start_restart(struct ath10k *ar,
 		__cpu_to_le32(arg->channel.band_center_freq1);
 
 	cmd->chan.mode = arg->channel.mode;
+	cmd->chan.flags |= __cpu_to_le32(ch_flags);
 	cmd->chan.min_power = arg->channel.min_power;
 	cmd->chan.max_power = arg->channel.max_power;
 	cmd->chan.reg_power = arg->channel.max_reg_power;
@@ -2918,9 +2932,10 @@ static int ath10k_wmi_vdev_start_restart(struct ath10k *ar,
 	cmd->chan.antenna_max = arg->channel.max_antenna_gain;
 
 	ath10k_dbg(ATH10K_DBG_WMI,
-		   "wmi vdev %s id 0x%x freq %d, mode %d, ch_flags: 0x%0X,"
-		   "max_power: %d\n", cmdname, arg->vdev_id, arg->channel.freq,
-		   arg->channel.mode, flags, arg->channel.max_power);
+		   "wmi vdev %s id 0x%x flags: 0x%0X, freq %d, mode %d, "
+		   "ch_flags: 0x%0X, max_power: %d\n", cmdname, arg->vdev_id,
+		   flags, arg->channel.freq, arg->channel.mode,
+		   cmd->chan.flags, arg->channel.max_power);
 
 	return ath10k_wmi_cmd_send(ar, skb, cmd_id);
 }
@@ -3254,6 +3269,8 @@ int ath10k_wmi_scan_chan_list(struct ath10k *ar,
 			flags |= WMI_CHAN_FLAG_ALLOW_VHT;
 		if (ch->ht40plus)
 			flags |= WMI_CHAN_FLAG_HT40_PLUS;
+		if (ch->chan_radar)
+			flags |= WMI_CHAN_FLAG_DFS;
 
 		ci->mhz               = __cpu_to_le32(ch->freq);
 		ci->band_center_freq1 = __cpu_to_le32(ch->freq);
diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h
index ad53333..b47a301 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.h
+++ b/drivers/net/wireless/ath/ath10k/wmi.h
@@ -916,6 +916,7 @@ struct wmi_channel_arg {
 	bool allow_ht;
 	bool allow_vht;
 	bool ht40plus;
+	bool chan_radar;
 	/* note: power unit is 0.5 dBm */
 	u32 min_power;
 	u32 max_power;
-- 
1.8.1.2


^ permalink raw reply related

* [PATCH 3/3] ath10k: add debugfs file to control radar events blocking
From: Marek Puzyniak @ 2013-10-29 12:06 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless, Marek Puzyniak, Michal Kazior
In-Reply-To: <1383048394-15256-1-git-send-email-marek.puzyniak@tieto.com>

Sometimes for DFS testing is required to stay on current channel even after
radar detected. This patch allows to enable/disable radar detected event to be
passed to mac80211.

By default radar detected event in not blocked.

To block it:
echo 1 > /sys/kernel/debug/ieee80211/phyX/ath10k/dfs_block_radar_events

To unblock again:
echo 0 > /sys/kernel/debug/ieee80211/phyX/ath10k/dfs_block_radar_events

Inform about blocking radar detected event even when logs are disabled
for throughput/performance reasons.

Signed-off-by: Marek Puzyniak <marek.puzyniak@tieto.com>
Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/core.h  | 1 +
 drivers/net/wireless/ath/ath10k/debug.c | 4 ++++
 drivers/net/wireless/ath/ath10k/wmi.c   | 8 ++++++++
 3 files changed, 13 insertions(+)

diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index d5fd74b..cdedcbf 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -409,6 +409,7 @@ struct ath10k {
 	bool monitor_present;
 	unsigned int filter_flags;
 	unsigned long dev_flags;
+	u32 dfs_block_radar_events;
 
 	struct wmi_pdev_set_wmm_params_arg wmm_params;
 	struct completion install_key_done;
diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c
index d4d0cdd..2cfe96b 100644
--- a/drivers/net/wireless/ath/ath10k/debug.c
+++ b/drivers/net/wireless/ath/ath10k/debug.c
@@ -749,6 +749,10 @@ int ath10k_debug_create(struct ath10k *ar)
 				    ar->debug.debugfs_phy, ar,
 				    &fops_simulate_radar);
 
+		debugfs_create_bool("dfs_block_radar_events", S_IWUSR,
+				    ar->debug.debugfs_phy,
+				    &ar->dfs_block_radar_events);
+
 		debugfs_create_file("dfs_stats", S_IRUSR,
 				    ar->debug.debugfs_phy, ar,
 				    &fops_dfs_stats);
diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c
index 8a7ebcc..8600465 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.c
+++ b/drivers/net/wireless/ath/ath10k/wmi.c
@@ -1456,6 +1456,14 @@ static void ath10k_dfs_radar_report(struct ath10k *ar,
 
 	ath10k_dbg(ATH10K_DBG_REGULATORY, "dfs radar detected\n");
 	ATH10K_DFS_STAT_INC(ar, radar_detected);
+
+	/* Control radar events reporting in debugfs file
+	   dfs_block_radar_events */
+	if (ar->dfs_block_radar_events) {
+		ath10k_info("DFS Radar detected, but ignored as requested\n");
+		return;
+	}
+
 	ieee80211_radar_detected(ar->hw);
 }
 
-- 
1.8.1.2


^ permalink raw reply related

* [PATCH v2 1/2] cfg80211: handle correctly prohibited flags
From: Janusz Dziedzic @ 2013-10-29 12:11 UTC (permalink / raw)
  To: linux-wireless; +Cc: johannes, mcgrof, Janusz Dziedzic

Handle correctly prohibited flags in cfg80211_secondary_chans_ok()
function. Before, we ignore IEEE80211_CHAN_RADAR flag even
caller put the flag as prohibited one.

Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com>
---
 net/wireless/chan.c |   11 +----------
 1 file changed, 1 insertion(+), 10 deletions(-)

diff --git a/net/wireless/chan.c b/net/wireless/chan.c
index a6f5c4c..6f2be48 100644
--- a/net/wireless/chan.c
+++ b/net/wireless/chan.c
@@ -438,16 +438,7 @@ static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
 
 	for (freq = start_freq; freq <= end_freq; freq += 20) {
 		c = ieee80211_get_channel(wiphy, freq);
-		if (!c)
-			return false;
-
-		/* check for radar flags */
-		if ((prohibited_flags & c->flags & IEEE80211_CHAN_RADAR) &&
-		    (c->dfs_state != NL80211_DFS_AVAILABLE))
-			return false;
-
-		/* check for the other flags */
-		if (c->flags & prohibited_flags & ~IEEE80211_CHAN_RADAR)
+		if (!c || c->flags & prohibited_flags)
 			return false;
 	}
 
-- 
1.7.9.5


^ permalink raw reply related

* [PATCH v2 2/2] cfg80211: allow beaconing after DFS CAC
From: Janusz Dziedzic @ 2013-10-29 12:11 UTC (permalink / raw)
  To: linux-wireless; +Cc: johannes, mcgrof, Janusz Dziedzic
In-Reply-To: <1383048716-5886-1-git-send-email-janusz.dziedzic@tieto.com>

After going throught the Channel Availability Check (CAC)
required by DFS enable beaconing. Channels that have
gone through a CAC will be in the NL80211_DFS_AVAILABLE
state. Without this change APs don't start beaconing
after a successful CAC.

Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com>
---
 net/wireless/chan.c |   78 +++++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 73 insertions(+), 5 deletions(-)

diff --git a/net/wireless/chan.c b/net/wireless/chan.c
index 6f2be48..a7168d5 100644
--- a/net/wireless/chan.c
+++ b/net/wireless/chan.c
@@ -426,6 +426,71 @@ bool cfg80211_chandef_dfs_usable(struct wiphy *wiphy,
 }
 
 
+static bool cfg80211_get_chans_dfs_available(struct wiphy *wiphy,
+					     u32 center_freq,
+					     u32 bandwidth)
+{
+	struct ieee80211_channel *c;
+	u32 freq, start_freq, end_freq;
+
+	start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
+	end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
+
+	/* Check entire range of channels for the bandwidth.
+	 * If any channel in between is disabled or has not
+	 * had gone through CAC return false
+	 */
+	for (freq = start_freq; freq <= end_freq; freq += 20) {
+		c = ieee80211_get_channel(wiphy, freq);
+		if (!c)
+			return false;
+
+		if (c->flags & IEEE80211_CHAN_DISABLED)
+			return false;
+
+		if ((c->flags & IEEE80211_CHAN_RADAR)  &&
+		    (c->dfs_state != NL80211_DFS_AVAILABLE))
+			return false;
+	}
+
+	return true;
+}
+
+static bool cfg80211_chandef_dfs_available(struct wiphy *wiphy,
+				const struct cfg80211_chan_def *chandef)
+{
+	int width;
+	int r;
+
+	if (WARN_ON(!cfg80211_chandef_valid(chandef)))
+		return false;
+
+	width = cfg80211_chandef_get_width(chandef);
+	if (width < 0)
+		return false;
+
+	r = cfg80211_get_chans_dfs_available(wiphy, chandef->center_freq1,
+					     width);
+
+	/* If any of channels unavailable for cf1 just return */
+	if (!r)
+		return r;
+
+	switch (chandef->width) {
+	case NL80211_CHAN_WIDTH_80P80:
+		WARN_ON(!chandef->center_freq2);
+		r = cfg80211_get_chans_dfs_available(wiphy,
+						     chandef->center_freq2,
+						     width);
+	default:
+		WARN_ON(chandef->center_freq2);
+		break;
+	}
+
+	return r;
+}
+
+
 static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
 					u32 center_freq, u32 bandwidth,
 					u32 prohibited_flags)
@@ -547,11 +612,14 @@ bool cfg80211_reg_can_beacon(struct wiphy *wiphy,
 
 	trace_cfg80211_reg_can_beacon(wiphy, chandef);
 
-	res = cfg80211_chandef_usable(wiphy, chandef,
-				      IEEE80211_CHAN_DISABLED |
-				      IEEE80211_CHAN_PASSIVE_SCAN |
-				      IEEE80211_CHAN_NO_IBSS |
-				      IEEE80211_CHAN_RADAR);
+	if (cfg80211_chandef_dfs_required(wiphy, chandef) > 0)
+		res = cfg80211_chandef_dfs_available(wiphy, chandef);
+	else
+		res = cfg80211_chandef_usable(wiphy, chandef,
+					      IEEE80211_CHAN_DISABLED |
+					      IEEE80211_CHAN_PASSIVE_SCAN |
+					      IEEE80211_CHAN_NO_IBSS |
+					      IEEE80211_CHAN_RADAR);
 
 	trace_cfg80211_return_bool(res);
 	return res;
-- 
1.7.9.5


^ permalink raw reply related

* Re: [PATCH v2 1/2] cfg80211: handle correctly prohibited flags
From: Luis R. Rodriguez @ 2013-10-29 13:13 UTC (permalink / raw)
  To: Janusz Dziedzic; +Cc: linux-wireless, Johannes Berg
In-Reply-To: <1383048716-5886-1-git-send-email-janusz.dziedzic@tieto.com>

On Tue, Oct 29, 2013 at 1:11 PM, Janusz Dziedzic
<janusz.dziedzic@tieto.com> wrote:
> Handle correctly prohibited flags in cfg80211_secondary_chans_ok()
> function. Before, we ignore IEEE80211_CHAN_RADAR flag even
> caller put the flag as prohibited one.
>
> Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com>
> ---
>  net/wireless/chan.c |   11 +----------
>  1 file changed, 1 insertion(+), 10 deletions(-)
>
> diff --git a/net/wireless/chan.c b/net/wireless/chan.c
> index a6f5c4c..6f2be48 100644
> --- a/net/wireless/chan.c
> +++ b/net/wireless/chan.c
> @@ -438,16 +438,7 @@ static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
>
>         for (freq = start_freq; freq <= end_freq; freq += 20) {
>                 c = ieee80211_get_channel(wiphy, freq);
> -               if (!c)
> -                       return false;
> -
> -               /* check for radar flags */
> -               if ((prohibited_flags & c->flags & IEEE80211_CHAN_RADAR) &&
> -                   (c->dfs_state != NL80211_DFS_AVAILABLE))
> -                       return false;
> -
> -               /* check for the other flags */
> -               if (c->flags & prohibited_flags & ~IEEE80211_CHAN_RADAR)
> +               if (!c || c->flags & prohibited_flags)
>                         return false;
>         }

NACK

Although you do something that looks good here you are breaking this
check atomically for this commit and introducing a regression here,
you want to merge the two patches together.

  Luis

^ permalink raw reply

* Re: [PATCH v2 2/2] cfg80211: allow beaconing after DFS CAC
From: Luis R. Rodriguez @ 2013-10-29 13:16 UTC (permalink / raw)
  To: Janusz Dziedzic; +Cc: linux-wireless, Johannes Berg
In-Reply-To: <1383048716-5886-2-git-send-email-janusz.dziedzic@tieto.com>

On Tue, Oct 29, 2013 at 1:11 PM, Janusz Dziedzic
<janusz.dziedzic@tieto.com> wrote:
> After going throught the Channel Availability Check (CAC)
> required by DFS enable beaconing. Channels that have
> gone through a CAC will be in the NL80211_DFS_AVAILABLE
> state. Without this change APs don't start beaconing
> after a successful CAC.
>
> Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com>

This looks better but please merge the two patches together. Also
Johannes -- will this patch need to rely on the new no-ir patch? I
provided feedback on your concern there.

  Luis

^ permalink raw reply

* Re: [PATCH 03/16] wl1251: add sysfs interface for bluetooth coexistence mode configuration
From: Kalle Valo @ 2013-10-29 13:35 UTC (permalink / raw)
  To: Luca Coelho
  Cc: Ben Hutchings, Pali Rohár, John W. Linville, Johannes Berg,
	David S. Miller, linux-wireless, netdev, linux-kernel,
	freemangordon, aaro.koskinen, pavel, sre, joni.lapilainen,
	David Gnedt
In-Reply-To: <1383030565.21526.92.camel@porter.coelho.fi>

Luca Coelho <luca@coelho.fi> writes:

> On Mon, 2013-10-28 at 23:39 +0000, Ben Hutchings wrote:
>> On Sat, 2013-10-26 at 22:34 +0200, Pali Rohár wrote:
>> > From: David Gnedt <david.gnedt@davizone.at>
>> > 
>> > Port the bt_coex_mode sysfs interface from wl1251 driver version included
>> > in the Maemo Fremantle kernel to allow bt-coexistence mode configuration.
>> > This enables userspace applications to set one of the modes
>> > WL1251_BT_COEX_OFF, WL1251_BT_COEX_ENABLE and WL1251_BT_COEX_MONOAUDIO.
>> > The default mode is WL1251_BT_COEX_OFF.
>> > It should be noted that this driver always enabled bt-coexistence before
>> > and enabled bt-coexistence directly affects the receiving performance,
>> > rendering it unusable in some low-signal situations. Especially monitor
>> > mode is affected very badly with bt-coexistence enabled.
>> [...]
>> 
>> This should be implemented consistently with other drivers:
>> 
>> drivers/net/wireless/ath/ath9k/htc_drv_init.c:module_param_named(btcoex_enable, ath9k_htc_btcoex_enable, int, 0444);
>> drivers/net/wireless/ath/ath9k/init.c:module_param_named(btcoex_enable, ath9k_btcoex_enable, int, 0444);
>> drivers/net/wireless/b43/main.c:module_param_named(btcoex, modparam_btcoex, int, 0444);
>> drivers/net/wireless/ipw2x00/ipw2200.c:module_param(bt_coexist, int, 0444);
>> drivers/net/wireless/iwlegacy/common.c:module_param(bt_coex_active, bool, S_IRUGO);
>> drivers/net/wireless/iwlwifi/iwl-drv.c:module_param_named(bt_coex_active, iwlwifi_mod_params.bt_coex_active,
>> drivers/net/wireless/ti/wlcore/sysfs.c:static DEVICE_ATTR(bt_coex_state, S_IRUGO | S_IWUSR,
>> 
>> Oh, hmm, I see a problem here.
>
> With so many drivers doing the same thing, isn't it about time to add
> this to nl80211?

Yes, this really needs to be in nl80211. I even suggested this years ago
but was turned down at the time. Can't remember the reason anymore.

-- 
Kalle Valo

^ permalink raw reply

* Re: I always need a miracle to connect with iwlwifi
From: Krishna Chaitanya @ 2013-10-29 14:23 UTC (permalink / raw)
  To: Felipe Contreras
  Cc: Oleksij Rempel, ilw, hostap@lists.shmoo.com,
	linux-wireless Mailing List
In-Reply-To: <CAMP44s3uRF=jNn5ZHZ=QOcvuMoXxsfkxU=VOq3K02Q0qOspEdA@mail.gmail.com>

On Tue, Oct 29, 2013 at 3:02 AM, Felipe Contreras
<felipe.contreras@gmail.com> wrote:
> On Mon, Oct 28, 2013 at 2:44 PM, Krishna Chaitanya
> <chaitanya.mgit@gmail.com> wrote:
>> On Tue, Oct 29, 2013 at 1:36 AM, Felipe Contreras
>> <felipe.contreras@gmail.com> wrote:
>>> On Mon, Oct 28, 2013 at 1:27 PM, Krishna Chaitanya
>>> <chaitanya.mgit@gmail.com> wrote:
>>
>>>> From the logs we can see that we have received authentication response,
>>>> so the association request is getting dropped somewhere? We might
>>>> need the mac80211 and iwlwifi trace-cmd logs to check for the drop.
>>>>
>>>> http://wireless.kernel.org/en/developers/Documentation/mac80211/tracing
>>>
>>> There you go:
>>> http://people.freedesktop.org/~felipec/wpa/trace.dat.xz
>>>
>> Hmm.."trace-cmd report -i trace.dat" returned lots of errors, i have even
>> tried with the trace-cmd from git (ubuntu). Did it worked fro you?
>
> Yes, but maybe I overrode the file. I've pushed a new one again. The
> sha-1 is 36c260d8d8c171a24eb1aa7b2ea736b06c9b55b7.
>
Thanks, able to decode now. I am not familiar with the
iwlwifi code, but let me give it a try.



-- 
Thanks,
Regards,
Chaitanya T K.

^ permalink raw reply

* Re: [PATCH NEXT] rtlwifi: Fix endian error in extracting packet type
From: Bjørn Mork @ 2013-10-29 14:27 UTC (permalink / raw)
  To: Ben Hutchings
  Cc: Larry Finger, linville, linux-wireless, Mark Cave-Ayland, netdev,
	Stable
In-Reply-To: <1383005246.3779.61.camel@bwh-desktop.uk.level5networks.com>

Ben Hutchings <bhutchings@solarflare.com> writes:

>> @@ -1077,8 +1077,8 @@ u8 rtl_is_special_data(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx)
>>  
>>  	ip = (struct iphdr *)((u8 *) skb->data + mac_hdr_len +
>>  			      SNAP_SIZE + PROTOC_TYPE_SIZE);
>> -	ether_type = *(u16 *) ((u8 *) skb->data + mac_hdr_len + SNAP_SIZE);
>> -	/*	ether_type = ntohs(ether_type); */
>> +	ether_type = be16_to_cpu(*(__be16 *)((u8 *)skb->data + mac_hdr_len +
>> +					     SNAP_SIZE));
>>  
>>  	if (ETH_P_IP == ether_type) {
>>  		if (IPPROTO_UDP == ip->protocol) {
>
> This crazy function also says that *all* IPv6 frames are special, which
> apparently means that on TX they should get sent at the lowest possible
> bit rate.  So I think this is going to cause a regression for IPv6
> throughput unless you remove that case.
>
> The DHCP case is also not validating IP and UDP header lengths against
> the packet length, though this may be harmless in practice.

It's not validating the upper 8 bits of the port numbers either, so it
will hit random UDP traffic in addition to DHCP. 

But it was good to see this function now. I was wondering how to support
some buggy 3G modem firmware without ugly hacks. Seems there will always
be worse hacks in drivers/net, no matter what I do :-)


Bjørn

^ permalink raw reply

* RE: [PATCH 2/5] ath: clean up ath_reg_apply_active_scan_flags()
From: Shete, Mihir @ 2013-10-29 16:19 UTC (permalink / raw)
  To: Luis R. Rodriguez, linville@tuxdriver.com,
	johannes@sipsolutions.net
  Cc: linux-wireless@vger.kernel.org, janusz.dziedzic@tieto.com,
	Bhattacharyya, Tushnim
In-Reply-To: <1382375691-25476-3-git-send-email-mcgrof@do-not-panic.com>



> -----Original Message-----
> From: Luis R. Rodriguez [mailto:mcgrof@gmail.com] On Behalf Of Luis R.
> Rodriguez
> Sent: Monday, October 21, 2013 10:45 PM
> To: linville@tuxdriver.com; johannes@sipsolutions.net
> Cc: linux-wireless@vger.kernel.org; janusz.dziedzic@tieto.com; Shete, Mihir;
> Bhattacharyya, Tushnim; Luis R. Rodriguez
> Subject: [PATCH 2/5] ath: clean up ath_reg_apply_active_scan_flags()
> 
> The routine ath_reg_apply_active_scan_flags() can be a bit hard to read, this
> cleans it up by adding helpers for the two cases of clearing IR flags or adding
> them. This approach also makes no assumptions on the index of channels 12
> and 13 so it should be portable accross different drivers.
> 
> Cc: smihir@qti.qualcomm.com
> Cc: tushnimb@qca.qualcomm.com
> Signed-off-by: Luis R. Rodriguez <mcgrof@do-not-panic.com>
> ---
>  drivers/net/wireless/ath/regd.c | 101 +++++++++++++++++++++++++-------
> --------
>  1 file changed, 63 insertions(+), 38 deletions(-)
> 
> diff --git a/drivers/net/wireless/ath/regd.c
> b/drivers/net/wireless/ath/regd.c index 1b4a532..48e2368 100644
> --- a/drivers/net/wireless/ath/regd.c
> +++ b/drivers/net/wireless/ath/regd.c
> @@ -177,6 +177,47 @@ static bool ath_is_radar_freq(u16 center_freq)
>  	return (center_freq >= 5260 && center_freq <= 5700);  }
> 
> +static void ath_force_clear_no_ir_chan(struct wiphy *wiphy,
> +				       struct ieee80211_channel *ch) {
> +	const struct ieee80211_reg_rule *reg_rule;
> +
> +	reg_rule = freq_reg_info(wiphy, ch->center_freq);

Do we not require to provide the frequency in KHZ here?

^ permalink raw reply

* Re: [PATCH 1/3] ath10k: add phyerr/dfs handling
From: Joe Perches @ 2013-10-29 17:25 UTC (permalink / raw)
  To: Marek Puzyniak; +Cc: ath10k, linux-wireless, Janusz Dziedzic
In-Reply-To: <1383048394-15256-1-git-send-email-marek.puzyniak@tieto.com>

On Tue, 2013-10-29 at 13:06 +0100, Marek Puzyniak wrote:
> Handle phyerr, dfs event, radar_report and fft_report.
> Add also debugfs dfs_simulate_radar and dfs_stats files.
> Use ath dfs pattern detector.

Just some simple notes:

> diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c
[]
> +#define ATH10K_DFS_STAT(s, p) (\
> +	len += scnprintf(buf + len, size - len, "%28s : %10u\n", s, \
> +			 ar->debug.dfs_stats.p))
> +
> +#define ATH10K_DFS_POOL_STAT(s, p) (\
> +	len += scnprintf(buf + len, size - len, "%28s : %10u\n", s, \
> +			 ar->debug.dfs_pool_stats.p))

You've already got sizes for the description field
might as well use %-28s

> +static ssize_t ath10k_read_file_dfs(struct file *file, char __user *user_buf,
> +				    size_t count, loff_t *ppos)
> +{
> +	int retval = 0, size = 8000, len = 0;

size should either be a #define or a const

[]

> +	len += scnprintf(buf + len, size - len, "Pulse detector statistics:\n");
> +	ATH10K_DFS_STAT("reported phy errors     ", phy_errors);
> +	ATH10K_DFS_STAT("pulse events reported   ", pulses_total);
> +	ATH10K_DFS_STAT("DFS pulses detected     ", pulses_detected);
> +	ATH10K_DFS_STAT("DFS pulses discarded    ", pulses_discarded);
> +	ATH10K_DFS_STAT("Radars detected         ", radar_detected);
> +
> +	len += scnprintf(buf + len, size - len, "Global Pool statistics:\n");
> +	ATH10K_DFS_POOL_STAT("Pool references         ", pool_reference);
> +	ATH10K_DFS_POOL_STAT("Pulses allocated        ", pulse_allocated);
> +	ATH10K_DFS_POOL_STAT("Pulses alloc error      ", pulse_alloc_error);
> +	ATH10K_DFS_POOL_STAT("Pulses in use           ", pulse_used);
> +	ATH10K_DFS_POOL_STAT("Seqs. allocated         ", pseq_allocated);
> +	ATH10K_DFS_POOL_STAT("Seqs. alloc error       ", pseq_alloc_error);
> +	ATH10K_DFS_POOL_STAT("Seqs. in use            ", pseq_used);

If using %-28s, the trailing quoted spaces can be removed.



^ permalink raw reply

* [PATCH 0/3] wireless: fix usage of freq_reg_info()
From: Luis R. Rodriguez @ 2013-10-29 18:34 UTC (permalink / raw)
  To: linville, johannes; +Cc: linux-wireless, Luis R. Rodriguez

Mihir reported freq_reg_info() was being used incorrectly on
ath/regd.c, I spotted this was true for years, and given that
a few vendors copied the same behaviour it meant other drivers
also had this broken. This fixes this issue accross 3 drivers.

These patches depend on the no-ir patches, and depending
on what tree that goes in this may be desirable to be merged
there.

The fix is represented with the Coccinelle SmPL grammar:

@@
struct ieee80211_channel *ch;
struct wiphy *wiphy;
const struct ieee80211_reg_rule *rule;
@@

-rule = freq_reg_info(wiphy, ch->center_freq);
+rule = freq_reg_info(wiphy, MHZ_TO_KHZ(ch->center_freq));

Luis R. Rodriguez (3):
  ath: fix usage of freq_reg_info()
  brcm80211: fix usage of freq_reg_info()
  rtlwifi: fix usage of freq_reg_info()

 drivers/net/wireless/ath/regd.c                   | 2 +-
 drivers/net/wireless/brcm80211/brcmsmac/channel.c | 3 ++-
 drivers/net/wireless/rtlwifi/regd.c               | 7 ++++---
 3 files changed, 7 insertions(+), 5 deletions(-)

-- 
1.8.4.rc3


^ permalink raw reply

* [PATCH 1/3] ath: fix usage of freq_reg_info()
From: Luis R. Rodriguez @ 2013-10-29 18:34 UTC (permalink / raw)
  To: linville, johannes
  Cc: linux-wireless, Luis R. Rodriguez, Julia Lawall,
	Peter Senna Tschudin
In-Reply-To: <1383071666-26817-1-git-send-email-mcgrof@do-not-panic.com>

freq_reg_info() expects KHz and not MHz, fix this. In
this case we'll now be getting the no-ir flags cleared
on channels for any channel when the country IE trusts
that channel.

@@
struct ieee80211_channel *ch;
struct wiphy *wiphy;
const struct ieee80211_reg_rule *rule;
@@

-rule = freq_reg_info(wiphy, ch->center_freq);
+rule = freq_reg_info(wiphy, MHZ_TO_KHZ(ch->center_freq));

Generated-by: Coccinelle SmPL
Cc: Julia Lawall <julia.lawall@lip6.fr>
Cc: Peter Senna Tschudin <peter.senna@gmail.com>
Reported-by: Mihir Shete <smihir@qti.qualcomm.com>
Signed-off-by: Luis R. Rodriguez <mcgrof@do-not-panic.com>
---
 drivers/net/wireless/ath/regd.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c
index a258283..bc9ba78 100644
--- a/drivers/net/wireless/ath/regd.c
+++ b/drivers/net/wireless/ath/regd.c
@@ -182,7 +182,7 @@ static void ath_force_clear_no_ir_chan(struct wiphy *wiphy,
 {
 	const struct ieee80211_reg_rule *reg_rule;
 
-	reg_rule = freq_reg_info(wiphy, ch->center_freq);
+	reg_rule = freq_reg_info(wiphy, MHZ_TO_KHZ(ch->center_freq));
 	if (IS_ERR(reg_rule))
 		return;
 
-- 
1.8.4.rc3


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox