Search Linux Wireless

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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Signed-off-by: Rostislav Lisovy <lisovy@xxxxxxxxx>
---
 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(-)

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))
-- 
1.8.5.1

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




[Index of Archives]     [Linux Host AP]     [ATH6KL]     [Linux Wireless Personal Area Network]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Linux Kernel]     [IDE]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite Hiking]     [MIPS Linux]     [ARM Linux]     [Linux RAID]

  Powered by Linux