diff mbox

[RFC,1/4] mac80211: Allow 5/10 MHz channel setting (for OCB)

Message ID 1392643374-3545-2-git-send-email-lisovy@gmail.com (mailing list archive)
State Not Applicable, archived
Headers show

Commit Message

Rostislav Lisovy Feb. 17, 2014, 1:22 p.m. UTC
Signed-off-by: Rostislav Lisovy <lisovy@gmail.com>
---
 include/net/cfg80211.h       |  19 ++++++-
 include/net/mac80211.h       |   4 +-
 include/uapi/linux/nl80211.h |  17 ++++++-
 net/wireless/chan.c          |   8 +++
 net/wireless/core.c          |   3 --
 net/wireless/nl80211.c       |  14 ++++++
 net/wireless/reg.c           | 115 ++++++++++++++++++++++++++++++++++++++-----
 7 files changed, 161 insertions(+), 19 deletions(-)

Comments

Johannes Berg Feb. 17, 2014, 1:49 p.m. UTC | #1
On Mon, 2014-02-17 at 14:22 +0100, Rostislav Lisovy wrote:
> Signed-off-by: Rostislav Lisovy <lisovy@gmail.com>

Err, some text is definitely needed.

> ---
>  include/net/cfg80211.h       |  19 ++++++-
>  include/net/mac80211.h       |   4 +-
>  include/uapi/linux/nl80211.h |  17 ++++++-
>  net/wireless/chan.c          |   8 +++
>  net/wireless/core.c          |   3 --
>  net/wireless/nl80211.c       |  14 ++++++
>  net/wireless/reg.c           | 115 ++++++++++++++++++++++++++++++++++++++-----
>  7 files changed, 161 insertions(+), 19 deletions(-)

This is mostly a cfg80211 patch - please remove the non-cfg80211 parts
from this one and label it appropriately.

> + * @IEEE80211_CHAN_NO_20MHZ: 20 MHz bandwidth is not permitted
> + *     on this channel.
> + * @IEEE80211_CHAN_NO_10MHZ: 10 MHz bandwidth is not permitted
> + *     on this channel.
> + * @IEEE80211_CHAN_OCB_ONLY: only OCB is allowed on this channel.

That's not very clear - does no-20 also imply no-higher-than-20? Also,
why no flag for 5MHz? Maybe there should be flags for _5MHZ_ONLY,
_10MHZ_ONLY and _OCB_ONLY?

> +++ b/include/net/mac80211.h
> @@ -517,6 +517,8 @@ enum mac80211_tx_info_flags {
>   */
>  enum mac80211_tx_control_flags {
>  	IEEE80211_TX_CTRL_PORT_CTRL_PROTO	= BIT(0),
> +	IEEE80211_TX_CTL_10MHZ			= BIT(1),
> +	IEEE80211_TX_CTL_5MHZ			= BIT(2),
>  };

This doesn't belong here.
 
> @@ -4516,7 +4518,7 @@ conf_is_ht40(struct ieee80211_conf *conf)
>  static inline bool
>  conf_is_ht(struct ieee80211_conf *conf)
>  {
> -	return conf->chandef.width != NL80211_CHAN_WIDTH_20_NOHT;
> +	return conf_is_ht20(conf) || conf_is_ht40(conf);
>  }

This also doesn't, but is also wrong - consider VHT.

> +++ b/net/wireless/core.c
> @@ -451,9 +451,6 @@ int wiphy_register(struct wiphy *wiphy)
>  	int i;
>  	u16 ifmodes = wiphy->interface_modes;
>  
> -	/* support for 5/10 MHz is broken due to nl80211 API mess - disable */
> -	wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_5_10_MHZ;

Err, what tree are you basing your code on? Consider updating it.

Besides, that wouldn't even belong into this patch anyway.

> +++ b/net/wireless/nl80211.c
> @@ -570,6 +570,16 @@ static int nl80211_msg_put_channel(struct sk_buff *msg,
>  	if ((chan->flags & IEEE80211_CHAN_NO_IBSS) &&
>  	    nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_IBSS))
>  		goto nla_put_failure;
> +	if ((chan->flags & IEEE80211_CHAN_NO_20MHZ) &&
> +	    nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_20MHZ))
> +		goto nla_put_failure;
> +	if ((chan->flags & IEEE80211_CHAN_NO_10MHZ) &&
> +	    nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_10MHZ))
> +		goto nla_put_failure;
> +
> +	if ((chan->flags & IEEE80211_CHAN_OCB_ONLY) &&
> +	    nla_put_flag(msg, NL80211_FREQUENCY_ATTR_OCB_ONLY))
> +		goto nla_put_failure;

This has to depend on the split format here, otherwise we overrun the
buffer used by older userspace tools. Also, such channels should be
removed from the nl80211 advertisement for userspace that can't cope
with non-split information.

> +		if (band_rule_found && bw_fits) {
> +			u32 allowed_bw = regd->reg_rules[i].freq_range.max_bandwidth_khz;
> +			if (desired_bw_khz > allowed_bw) {
> +				return ERR_PTR(-ENOENT);
> +			} else {
> +				return rr;
> +			}
> +		}

code style
 
>  const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy,
> -					       u32 center_freq)
> +					       u32 center_freq,
> +					       u32 desired_bw_khz)

>  EXPORT_SYMBOL(freq_reg_info);

This function is exported but you haven't updated any of its users ...

> +	do {
> +		reg_rule = freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq), desired_bw_khz);
> +		if ((IS_ERR(reg_rule)) && ((u32)reg_rule == -ENOENT)) {
> +			bw_flags |= IEEE80211_CHAN_NO_20MHZ;
> +		} else {
> +			break;
> +		}
> +
> +		/* check for 10 MHz BW */
> +		desired_bw_khz = MHZ_TO_KHZ(10);
> +		reg_rule = freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq), desired_bw_khz);
> +		if ((IS_ERR(reg_rule)) && ((u32)reg_rule == -ENOENT)) {
> +			bw_flags |= IEEE80211_CHAN_NO_10MHZ;
> +		} else {
> +			break;
> +		}
> +
> +		/* check for 5 MHz BW */
> +		desired_bw_khz = MHZ_TO_KHZ(5);
> +		reg_rule = freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq), desired_bw_khz);
> +		if ((IS_ERR(reg_rule)) && ((u32)reg_rule == -ENOENT)) {
> +
> +		} else {
> +			break;
> +		}
> +	} while (0);

That is one of the ugliest code constructs I've ever seen. You should
rewrite it.

> +#if 0

Umm.

Might have mentioned that in your 0/4 that you want help on specific
things :)

[I haven't even really reviewed the regulatory code]

johannes

--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Rostislav Lisovy Feb. 17, 2014, 3:49 p.m. UTC | #2
Dear Johannes;
Thank you for your feedback.

On Mon, 2014-02-17 at 14:49 +0100, Johannes Berg wrote:
> On Mon, 2014-02-17 at 14:22 +0100, Rostislav Lisovy wrote:
> > Signed-off-by: Rostislav Lisovy <lisovy@gmail.com>
> 
> Err, some text is definitely needed.
> 
> > ---
> >  include/net/cfg80211.h       |  19 ++++++-
> >  include/net/mac80211.h       |   4 +-
> >  include/uapi/linux/nl80211.h |  17 ++++++-
> >  net/wireless/chan.c          |   8 +++
> >  net/wireless/core.c          |   3 --
> >  net/wireless/nl80211.c       |  14 ++++++
> >  net/wireless/reg.c           | 115 ++++++++++++++++++++++++++++++++++++++-----
> >  7 files changed, 161 insertions(+), 19 deletions(-)

> 
> Umm.
> 
> Might have mentioned that in your 0/4 that you want help on specific
> things :)
> 
> [I haven't even really reviewed the regulatory code]


As you have already noticed, this is work in progress. I agree it is
necessary to keep the code clean if I want others to read it -- I try to
do so but it does not always go very well.

One thing I am not sure about (mentioned in 0/4) and need some
suggestions is frequency setting in the OCB mode.
Since there are no beacons containing the information about the
frequency to set, the frequency is set manually. Since the
'set_channel()' function is not present anymore (and we do not have
'ocb_join()' function, thus we cannot use 'preset_chandef'), I was not
able to figure out how to set a fixed channel.
If I would want to set the frequency with the
ieee80211_vif_use_channel() function, I would need to somehow obtain the
*sdata. If I check ieee80211_set_monitor_channel() I see how *sdata is
obtained there, but I am not sure if it is a good idea to add *ocb_sdata
to struct ieee80211_local?

Best regards;
Rostislav Lisovy

--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Johannes Berg Feb. 17, 2014, 4:38 p.m. UTC | #3
On Mon, 2014-02-17 at 16:49 +0100, Rostislav Lisovy wrote:

> As you have already noticed, this is work in progress. I agree it is
> necessary to keep the code clean if I want others to read it -- I try to
> do so but it does not always go very well.

:-)

> One thing I am not sure about (mentioned in 0/4) and need some
> suggestions is frequency setting in the OCB mode.
> Since there are no beacons containing the information about the
> frequency to set, the frequency is set manually. Since the
> 'set_channel()' function is not present anymore (and we do not have
> 'ocb_join()' function, thus we cannot use 'preset_chandef'), I was not
> able to figure out how to set a fixed channel.

I don't really see why you didn't add an obc_join() method. I know that
it's not strictly a BSS concept, but still there should be *some*
indication that you want to participate in the OBC now, so you should
still have something like obc_join() that starts everything.

> If I would want to set the frequency with the
> ieee80211_vif_use_channel() function, I would need to somehow obtain the
> *sdata. If I check ieee80211_set_monitor_channel() I see how *sdata is
> obtained there, but I am not sure if it is a good idea to add *ocb_sdata
> to struct ieee80211_local?

That doesn't seem right, no; it seems in theory multiple OBC interfaces
could be present.

johannes

--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 3eae46c..14f8cc1 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -110,6 +110,11 @@  enum ieee80211_band {
  *	channel as the control or any of the secondary channels.
  *	This may be due to the driver or due to regulatory bandwidth
  *	restrictions.
+ * @IEEE80211_CHAN_NO_20MHZ: 20 MHz bandwidth is not permitted
+ *     on this channel.
+ * @IEEE80211_CHAN_NO_10MHZ: 10 MHz bandwidth is not permitted
+ *     on this channel.
+ * @IEEE80211_CHAN_OCB_ONLY: only OCB is allowed on this channel.
  */
 enum ieee80211_channel_flags {
 	IEEE80211_CHAN_DISABLED		= 1<<0,
@@ -121,6 +126,9 @@  enum ieee80211_channel_flags {
 	IEEE80211_CHAN_NO_OFDM		= 1<<6,
 	IEEE80211_CHAN_NO_80MHZ		= 1<<7,
 	IEEE80211_CHAN_NO_160MHZ	= 1<<8,
+	IEEE80211_CHAN_NO_20MHZ		= 1<<9,
+	IEEE80211_CHAN_NO_10MHZ		= 1<<10,
+	IEEE80211_CHAN_OCB_ONLY		= 1<<11,
 };
 
 #define IEEE80211_CHAN_NO_HT40 \
@@ -362,6 +370,10 @@  static inline enum nl80211_channel_type
 cfg80211_get_chandef_type(const struct cfg80211_chan_def *chandef)
 {
 	switch (chandef->width) {
+	case NL80211_CHAN_WIDTH_5:
+		return NL80211_CHAN_5MHZ;
+	case NL80211_CHAN_WIDTH_10:
+		return NL80211_CHAN_10MHZ;
 	case NL80211_CHAN_WIDTH_20_NOHT:
 		return NL80211_CHAN_NO_HT;
 	case NL80211_CHAN_WIDTH_20:
@@ -3480,6 +3492,10 @@  void wiphy_apply_custom_regulatory(struct wiphy *wiphy,
  * freq_reg_info - get regulatory information for the given frequency
  * @wiphy: the wiphy for which we want to process this rule for
  * @center_freq: Frequency in KHz for which we want regulatory information for
+ * @desired_bw_khz: the desired max bandwidth you want to use per
+ *	channel. Note that this is still 20 MHz if you want to use HT40
+ *	as HT40 makes use of two channels for its 40 MHz width bandwidth.
+ *	If set to 0 we'll assume you want the standard 20 MHz.
  *
  * Use this function to get the regulatory rule for a specific frequency on
  * a given wireless device. If the device has a specific regulatory domain
@@ -3495,7 +3511,8 @@  void wiphy_apply_custom_regulatory(struct wiphy *wiphy,
  * purely subjective and right now it's 802.11 specific.
  */
 const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy,
-					       u32 center_freq);
+					       u32 center_freq,
+					       u32 desired_bw_khz);
 
 /**
  * reg_initiator_name - map regulatory request initiator enum to name
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 7ceed99..01c8a1f 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -517,6 +517,8 @@  enum mac80211_tx_info_flags {
  */
 enum mac80211_tx_control_flags {
 	IEEE80211_TX_CTRL_PORT_CTRL_PROTO	= BIT(0),
+	IEEE80211_TX_CTL_10MHZ			= BIT(1),
+	IEEE80211_TX_CTL_5MHZ			= BIT(2),
 };
 
 /*
@@ -4516,7 +4518,7 @@  conf_is_ht40(struct ieee80211_conf *conf)
 static inline bool
 conf_is_ht(struct ieee80211_conf *conf)
 {
-	return conf->chandef.width != NL80211_CHAN_WIDTH_20_NOHT;
+	return conf_is_ht20(conf) || conf_is_ht40(conf);
 }
 
 static inline enum nl80211_iftype
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index f752e98..f2d3f67 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -2246,6 +2246,14 @@  enum nl80211_band_attr {
  * @NL80211_FREQUENCY_ATTR_NO_160MHZ: any 160 MHz (but not 80+80) channel
  *	using this channel as the primary or any of the secondary channels
  *	isn't possible
+ * @NL80211_FREQUENCY_ATTR_NO_20MHZ: 20 MHz operation is not allowed
+ *     on this channel in current regulatory domain.
+ *     - this still allows 10 MHz and 5 MHz operation
+ * @NL80211_FREQUENCY_ATTR_NO_10MHZ: 10 MHz operation is not allowed
+ *     on this channel in current regulatory domain.
+ *     - this still allows 20 MHz and 5 MHz operation
+ * @NL80211_FREQUENCY_ATTR_OCB_ONLY: no other than OCB networks are
+ *	permitted on this channel in current regulatory domain.
  * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number
  *	currently defined
  * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use
@@ -2264,6 +2272,9 @@  enum nl80211_frequency_attr {
 	NL80211_FREQUENCY_ATTR_NO_HT40_PLUS,
 	NL80211_FREQUENCY_ATTR_NO_80MHZ,
 	NL80211_FREQUENCY_ATTR_NO_160MHZ,
+	NL80211_FREQUENCY_ATTR_NO_20MHZ,
+	NL80211_FREQUENCY_ATTR_NO_10MHZ,
+	NL80211_FREQUENCY_ATTR_OCB_ONLY,
 
 	/* keep last */
 	__NL80211_FREQUENCY_ATTR_AFTER_LAST,
@@ -2822,12 +2833,16 @@  enum nl80211_ac {
  *	below the control channel
  * @NL80211_CHAN_HT40PLUS: HT40 channel, secondary channel
  *	above the control channel
+ * @NL80211_CHAN_5MHZ: normal, 5 MHz bandwidth
+ * @NL80211_CHAN_10MHZ: normal, 10 MHz bandwidth
  */
 enum nl80211_channel_type {
 	NL80211_CHAN_NO_HT,
 	NL80211_CHAN_HT20,
 	NL80211_CHAN_HT40MINUS,
-	NL80211_CHAN_HT40PLUS
+	NL80211_CHAN_HT40PLUS,
+	NL80211_CHAN_5MHZ,
+	NL80211_CHAN_10MHZ,
 };
 
 /**
diff --git a/net/wireless/chan.c b/net/wireless/chan.c
index 9b8cc87..729a30c 100644
--- a/net/wireless/chan.c
+++ b/net/wireless/chan.c
@@ -22,6 +22,14 @@  void cfg80211_chandef_create(struct cfg80211_chan_def *chandef,
 	chandef->center_freq2 = 0;
 
 	switch (chan_type) {
+	case NL80211_CHAN_5MHZ:
+		chandef->width = NL80211_CHAN_WIDTH_5;
+		chandef->center_freq1 = chan->center_freq;
+		break;
+	case NL80211_CHAN_10MHZ:
+		chandef->width = NL80211_CHAN_WIDTH_10;
+		chandef->center_freq1 = chan->center_freq;
+		break;
 	case NL80211_CHAN_NO_HT:
 		chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
 		chandef->center_freq1 = chan->center_freq;
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 52b865f..507e71f 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -451,9 +451,6 @@  int wiphy_register(struct wiphy *wiphy)
 	int i;
 	u16 ifmodes = wiphy->interface_modes;
 
-	/* support for 5/10 MHz is broken due to nl80211 API mess - disable */
-	wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_5_10_MHZ;
-
 	/*
 	 * There are major locking problems in nl80211/mac80211 for CSA,
 	 * disable for all drivers until this has been reworked.
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 138dc3b..a62d716 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -570,6 +570,16 @@  static int nl80211_msg_put_channel(struct sk_buff *msg,
 	if ((chan->flags & IEEE80211_CHAN_NO_IBSS) &&
 	    nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_IBSS))
 		goto nla_put_failure;
+	if ((chan->flags & IEEE80211_CHAN_NO_20MHZ) &&
+	    nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_20MHZ))
+		goto nla_put_failure;
+	if ((chan->flags & IEEE80211_CHAN_NO_10MHZ) &&
+	    nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_10MHZ))
+		goto nla_put_failure;
+
+	if ((chan->flags & IEEE80211_CHAN_OCB_ONLY) &&
+	    nla_put_flag(msg, NL80211_FREQUENCY_ATTR_OCB_ONLY))
+		goto nla_put_failure;
 	if (chan->flags & IEEE80211_CHAN_RADAR) {
 		if (nla_put_flag(msg, NL80211_FREQUENCY_ATTR_RADAR))
 			goto nla_put_failure;
@@ -1800,6 +1810,8 @@  static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
 				info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
 
 		switch (chantype) {
+		case NL80211_CHAN_5MHZ:
+		case NL80211_CHAN_10MHZ:
 		case NL80211_CHAN_NO_HT:
 		case NL80211_CHAN_HT20:
 		case NL80211_CHAN_HT40PLUS:
@@ -2195,6 +2207,8 @@  static int nl80211_send_chandef(struct sk_buff *msg,
 			chandef->chan->center_freq))
 		return -ENOBUFS;
 	switch (chandef->width) {
+	case NL80211_CHAN_WIDTH_5:
+	case NL80211_CHAN_WIDTH_10:
 	case NL80211_CHAN_WIDTH_20_NOHT:
 	case NL80211_CHAN_WIDTH_20:
 	case NL80211_CHAN_WIDTH_40:
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 7da67fd..992432c 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -710,13 +710,17 @@  static u32 map_regdom_flags(u32 rd_flags)
 }
 
 static const struct ieee80211_reg_rule *
-freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq,
+freq_reg_info_regd(struct wiphy *wiphy,
+		   u32 center_freq, u32 desired_bw_khz,
 		   const struct ieee80211_regdomain *regd)
 {
 	int i;
 	bool band_rule_found = false;
 	bool bw_fits = false;
 
+	if (!desired_bw_khz)
+		desired_bw_khz = MHZ_TO_KHZ(20);
+
 	if (!regd)
 		return ERR_PTR(-EINVAL);
 
@@ -735,7 +739,16 @@  freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq,
 		if (!band_rule_found)
 			band_rule_found = freq_in_rule_band(fr, center_freq);
 
-		bw_fits = reg_does_bw_fit(fr, center_freq, MHZ_TO_KHZ(20));
+		bw_fits = reg_does_bw_fit(fr, center_freq, desired_bw_khz);
+
+		if (band_rule_found && bw_fits) {
+			u32 allowed_bw = regd->reg_rules[i].freq_range.max_bandwidth_khz;
+			if (desired_bw_khz > allowed_bw) {
+				return ERR_PTR(-ENOENT);
+			} else {
+				return rr;
+			}
+		}
 
 		if (band_rule_found && bw_fits)
 			return rr;
@@ -748,7 +761,8 @@  freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq,
 }
 
 const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy,
-					       u32 center_freq)
+					       u32 center_freq,
+					       u32 desired_bw_khz)
 {
 	const struct ieee80211_regdomain *regd;
 	struct regulatory_request *lr = get_last_request();
@@ -764,7 +778,7 @@  const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy,
 	else
 		regd = get_cfg80211_regdom();
 
-	return freq_reg_info_regd(wiphy, center_freq, regd);
+	return freq_reg_info_regd(wiphy, center_freq, desired_bw_khz, regd);
 }
 EXPORT_SYMBOL(freq_reg_info);
 
@@ -788,6 +802,7 @@  EXPORT_SYMBOL(reg_initiator_name);
 
 #ifdef CONFIG_CFG80211_REG_DEBUG
 static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan,
+				    u32 desired_bw_khz,
 				    const struct ieee80211_reg_rule *reg_rule)
 {
 	const struct ieee80211_power_rule *power_rule;
@@ -802,8 +817,8 @@  static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan,
 	else
 		snprintf(max_antenna_gain, 32, "%d", power_rule->max_antenna_gain);
 
-	REG_DBG_PRINT("Updating information on frequency %d MHz with regulatory rule:\n",
-		      chan->center_freq);
+	REG_DBG_PRINT("Updating information on frequency %d MHz for a %d MHz width channel with regulatory rule:\n",
+		      chan->center_freq, KHZ_TO_MHZ(desired_bw_khz));
 
 	REG_DBG_PRINT("%d KHz - %d KHz @ %d KHz), (%s mBi, %d mBm)\n",
 		      freq_range->start_freq_khz, freq_range->end_freq_khz,
@@ -812,6 +827,7 @@  static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan,
 }
 #else
 static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan,
+				    u32 desired_bw_khz,
 				    const struct ieee80211_reg_rule *reg_rule)
 {
 	return;
@@ -821,13 +837,18 @@  static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan,
 /*
  * Note that right now we assume the desired channel bandwidth
  * is always 20 MHz for each individual channel (HT40 uses 20 MHz
- * per channel, the primary and the extension channel).
+ * per channel, the primary and the extension channel). To support
+ * smaller custom bandwidths such as 5 MHz or 10 MHz we'll need a
+ * new ieee80211_channel.target_bw and re run the regulatory check
+ * on the wiphy with the target_bw specified. Then we can simply use
+ * that below for the desired_bw_khz below.
  */
 static void handle_channel(struct wiphy *wiphy,
 			   enum nl80211_reg_initiator initiator,
 			   struct ieee80211_channel *chan)
 {
 	u32 flags, bw_flags = 0;
+	u32 desired_bw_khz = MHZ_TO_KHZ(20);
 	const struct ieee80211_reg_rule *reg_rule = NULL;
 	const struct ieee80211_power_rule *power_rule = NULL;
 	const struct ieee80211_freq_range *freq_range = NULL;
@@ -838,7 +859,33 @@  static void handle_channel(struct wiphy *wiphy,
 
 	flags = chan->orig_flags;
 
-	reg_rule = freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq));
+	do {
+		reg_rule = freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq), desired_bw_khz);
+		if ((IS_ERR(reg_rule)) && ((u32)reg_rule == -ENOENT)) {
+			bw_flags |= IEEE80211_CHAN_NO_20MHZ;
+		} else {
+			break;
+		}
+
+		/* check for 10 MHz BW */
+		desired_bw_khz = MHZ_TO_KHZ(10);
+		reg_rule = freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq), desired_bw_khz);
+		if ((IS_ERR(reg_rule)) && ((u32)reg_rule == -ENOENT)) {
+			bw_flags |= IEEE80211_CHAN_NO_10MHZ;
+		} else {
+			break;
+		}
+
+		/* check for 5 MHz BW */
+		desired_bw_khz = MHZ_TO_KHZ(5);
+		reg_rule = freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq), desired_bw_khz);
+		if ((IS_ERR(reg_rule)) && ((u32)reg_rule == -ENOENT)) {
+
+		} else {
+			break;
+		}
+	} while (0);
+
 	if (IS_ERR(reg_rule)) {
 		/*
 		 * We will disable all channels that do not match our
@@ -859,11 +906,15 @@  static void handle_channel(struct wiphy *wiphy,
 		return;
 	}
 
-	chan_reg_rule_print_dbg(chan, reg_rule);
+	chan_reg_rule_print_dbg(chan, desired_bw_khz, reg_rule);
 
 	power_rule = &reg_rule->power_rule;
 	freq_range = &reg_rule->freq_range;
 
+	if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(10))
+		bw_flags |= IEEE80211_CHAN_NO_10MHZ;
+	if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(20))
+		bw_flags |= IEEE80211_CHAN_NO_20MHZ;
 	if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40))
 		bw_flags = IEEE80211_CHAN_NO_HT40;
 	if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(80))
@@ -871,9 +922,18 @@  static void handle_channel(struct wiphy *wiphy,
 	if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(160))
 		bw_flags |= IEEE80211_CHAN_NO_160MHZ;
 
+#if 0
 	if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
 	    request_wiphy && request_wiphy == wiphy &&
 	    request_wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) {
+#else
+	/*
+	 * FIXME FIXME FIXME
+	 * we always want to use the last requested reg domain
+	 * do NOT use old values as a base
+	 */
+	if (1) {
+#endif
 		/*
 		 * This guarantees the driver's requested regulatory domain
 		 * will always be used as a base for further regulatory
@@ -1253,25 +1313,54 @@  static void handle_channel_custom(struct wiphy *wiphy,
 				  const struct ieee80211_regdomain *regd)
 {
 	u32 bw_flags = 0;
+	u32 desired_bw_khz = MHZ_TO_KHZ(20);
 	const struct ieee80211_reg_rule *reg_rule = NULL;
 	const struct ieee80211_power_rule *power_rule = NULL;
 	const struct ieee80211_freq_range *freq_range = NULL;
 
 	reg_rule = freq_reg_info_regd(wiphy, MHZ_TO_KHZ(chan->center_freq),
-				      regd);
+				      desired_bw_khz, regd);
+
+	if ((IS_ERR(reg_rule)) && !(PTR_ERR(reg_rule) == -ERANGE &&
+		    last_request->initiator ==
+		    NL80211_REGDOM_SET_BY_COUNTRY_IE)) { /* FFIXME Wat? */
+		/*
+		 * if 20 MHz BW is not ok apply further tests
+		 * avoid catching the below exception (-ERANGE...) to exhibit
+		 * exactly the same behaviour (not disable anything in this case)
+		 * "bw_flags" have to be used to impose restrictions because "flags"
+		 * is only applied in case of disabling the whole channel!
+		 */
+		bw_flags |= IEEE80211_CHAN_NO_20MHZ;
+
+		/* check for 10 MHz BW */
+		reg_rule = freq_reg_info_regd(wiphy, MHZ_TO_KHZ(chan->center_freq),
+					      MHZ_TO_KHZ(10), regd);
+		if (IS_ERR(reg_rule)) {
+			bw_flags |= IEEE80211_CHAN_NO_10MHZ;
+		}
+
+		/* check for 5 MHz BW */
+		reg_rule = freq_reg_info_regd(wiphy, MHZ_TO_KHZ(chan->center_freq),
+					      MHZ_TO_KHZ(5), regd);
+	}
 
 	if (IS_ERR(reg_rule)) {
-		REG_DBG_PRINT("Disabling freq %d MHz as custom regd has no rule that fits it\n",
-			      chan->center_freq);
+		REG_DBG_PRINT("Disabling freq %d MHz as custom regd has no rule that fits a %d MHz wide channel\n",
+			      chan->center_freq, KHZ_TO_MHZ(desired_bw_khz));
 		chan->flags = IEEE80211_CHAN_DISABLED;
 		return;
 	}
 
-	chan_reg_rule_print_dbg(chan, reg_rule);
+	chan_reg_rule_print_dbg(chan, desired_bw_khz, reg_rule);
 
 	power_rule = &reg_rule->power_rule;
 	freq_range = &reg_rule->freq_range;
 
+	if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(10))
+		bw_flags |= IEEE80211_CHAN_NO_10MHZ;
+	if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(20))
+		bw_flags |= IEEE80211_CHAN_NO_20MHZ;
 	if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40))
 		bw_flags = IEEE80211_CHAN_NO_HT40;
 	if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(80))