Search Linux Wireless

[RFC] wireless: Add 802.11d support

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

 



This adds 802.11d support to mac80211 and cfg80211.

Signed-off-by: Luis R. Rodriguez <lrodriguez@xxxxxxxxxxx>
---

This is not complete, I'm missing the reg.c part, but figured
I'd send it out for review to get comments now. Anyone know what the
country IE extension thing is? A quick glance at 11d gave me nothing.

 include/linux/ieee80211.h |   19 +++++
 include/net/wireless.h    |   11 +++
 net/mac80211/mlme.c       |  174 +++++++++++++++++++++++++++++++++++++++++++++
 net/wireless/reg.c        |    7 ++
 4 files changed, 211 insertions(+), 0 deletions(-)

diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index aad9919..40f5e2a 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -1032,6 +1032,25 @@ enum ieee80211_spectrum_mgmt_actioncode {
 	WLAN_ACTION_SPCT_CHL_SWITCH = 4,
 };
 
+/* Extension IDs >= this one indicate the Country IE
+ * has extensions */
+#define IEEE80211_COUNTRY_EXTENSION_ID 201
+
+struct ieee80211_country_ie_triplet {
+	union {
+		struct {
+			u8 first_channel;
+			u8 num_channels;
+			u8 max_power;
+		} __attribute__ ((packed)) chans;
+		struct {
+			u8 extension_id;
+			u8 class;
+			u8 coverage;
+		} __attribute__ ((packed)) ext;
+	};
+} __attribute__ ((packed));
+
 /* BACK action code */
 enum ieee80211_back_actioncode {
 	WLAN_ACTION_ADDBA_REQ = 0,
diff --git a/include/net/wireless.h b/include/net/wireless.h
index 41294c5..f148a02 100644
--- a/include/net/wireless.h
+++ b/include/net/wireless.h
@@ -357,4 +357,15 @@ ieee80211_get_channel(struct wiphy *wiphy, int freq)
  * for a regulatory domain structure for the respective country.
  */
 extern void regulatory_hint(struct wiphy *wiphy, const char *alpha2);
+
+/**
+ * regulatory_hint_ie - wireless stack hints a regulatory domain
+ * @wiphy: the wireless device giving the hint (used only for reporting
+ *	conflicts)
+ * @rd: a regulatory domain built from the received country IE
+ *
+ * We will intersect the rd ... etc
+ */
+extern int regulatory_hint_ie(struct wiphy *wiphy,
+	struct ieee80211_regdomain *rd);
 #endif /* __NET_WIRELESS_H */
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index d5006b2..388d13c 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -648,6 +648,169 @@ static void ieee80211_sta_send_apinfo(struct ieee80211_sub_if_data *sdata,
 	wireless_send_event(sdata->dev, SIOCGIWAP, &wrqu, NULL);
 }
 
+/* Converts a country IE to a regulatory domain. A regulatory domain
+ * structure has a lot of information which the IE doesn't yet have,
+ * so for the other values we use upper max values as we will intersect
+ * with our userspace regulaotry agent to get lower bounds. This needs
+ * review for 802.11j considerations, keep in mind the first_channel
+ * cannot be >= 201 as that means its an extension */
+struct ieee80211_regdomain *ieee80211_country_ie_2_rd(
+				u8 *country_ie, u8 country_ie_len)
+{
+	struct ieee80211_regdomain *rd = NULL;
+	u32 flags = 0;
+	u32 num_rules = 0, size_of_regd = 0;
+	u8 *triplets_start = NULL;
+	u8 len_at_triplet = 0;
+	unsigned int i = 0;
+	/* Used too often */
+	int triplet_size = sizeof(struct ieee80211_country_ie_triplet);
+
+	rd->alpha2[0] = country_ie[0];
+	rd->alpha2[1] = country_ie[1];
+
+	/*
+	 * Third octet can be:
+	 *    'I' - Indoor
+	 *    'O' - Outdoor
+	 *
+	 *  anything else we assume is no restrictions
+	 */
+	if (country_ie[2] == 'I')
+		flags = NL80211_RRF_NO_OUTDOOR;
+	else if (country_ie[2] == 'O')
+		flags = NL80211_RRF_NO_INDOOR;
+
+	country_ie += 3;
+	country_ie_len -= 3;
+
+	triplets_start = country_ie;
+	len_at_triplet = country_ie_len;
+
+	/* We need to build a reg rule for each triplet, but first we must
+	 * calculate the number of reg rules we will need. We will need one
+	 * for each channel subband */
+	while (country_ie_len >= triplet_size) {
+		struct ieee80211_country_ie_triplet *triplet =
+			(struct ieee80211_country_ie_triplet *) country_ie;
+
+		if (triplet->ext.extension_id >= IEEE80211_COUNTRY_EXTENSION_ID) {
+			country_ie += triplet_size;
+			country_ie_len -= triplet_size;
+			continue;
+		}
+
+		country_ie += triplet_size;
+		country_ie_len -= triplet_size;
+		num_rules++;
+
+		if (num_rules > NL80211_MAX_SUPP_REG_RULES)
+			return NULL;
+	}
+
+	country_ie = triplets_start;
+	country_ie_len = len_at_triplet;
+
+	size_of_regd = sizeof(struct ieee80211_regdomain) +
+		(num_rules * sizeof(struct ieee80211_reg_rule));
+
+	rd = kzalloc(size_of_regd, GFP_KERNEL);
+	if (!rd)
+		return NULL;
+
+	rd->n_reg_rules = num_rules;
+
+	/* This time around we fill in the rd */
+	while (country_ie_len >= triplet_size) {
+		struct ieee80211_country_ie_triplet *triplet =
+			(struct ieee80211_country_ie_triplet *) country_ie;
+		struct ieee80211_reg_rule *reg_rule = NULL;
+		struct ieee80211_freq_range *freq_range = NULL;
+		struct ieee80211_power_rule *power_rule = NULL;
+
+		/* Not sure what these guys are yet, or how to handle them, but
+		 * it means we use the 'ext' part of the union instead
+		 * of 'chan' */
+		if (triplet->ext.extension_id >=
+				IEEE80211_COUNTRY_EXTENSION_ID) {
+			country_ie += triplet_size;
+			country_ie_len -= triplet_size;
+			continue;
+		}
+
+		reg_rule = &rd->reg_rules[i];
+		freq_range = &reg_rule->freq_range;
+		power_rule = &reg_rule->power_rule;
+
+		reg_rule->flags = flags;
+
+		/* The +10 is since the regulatory domain expects
+		 * the actual band edge, not the center of freq for
+		 * its start and end freqs */
+		freq_range->start_freq_khz =
+			MHZ_TO_KHZ(ieee80211_channel_to_frequency(
+				triplet->chans.first_channel) + 10);
+		freq_range->end_freq_khz =
+			MHZ_TO_KHZ(ieee80211_channel_to_frequency(
+				triplet->chans.first_channel +
+					triplet->chans.num_channels) + 10);
+
+
+#ifdef CONFIG_WIRELESS_OLD_REGULATORY
+		/* We can't expect to have anything to intersect with...
+		 * so use standard low values */
+		freq_range->max_bandwidth_khz = MHZ_TO_KHZ(40);
+		power_rule->max_antenna_gain = DBI_TO_MBI(6);
+		power_rule->max_eirp = DBM_TO_MBM(20);
+#else
+		/* Large arbitrary values, we intersect later */
+		/* Increment this if we ever support >= 40 MHz channels
+		 * in IEEE 802.11 */
+		freq_range->max_bandwidth_khz = MHZ_TO_KHZ(40);
+		power_rule->max_antenna_gain = DBI_TO_MBI(100);
+		power_rule->max_eirp = DBM_TO_MBM(100);
+#endif
+
+		country_ie += triplet_size;
+		country_ie_len -= triplet_size;
+		i++;
+
+		BUG_ON(i > NL80211_MAX_SUPP_REG_RULES);
+	}
+	return rd;
+}
+
+static int ieee80211_sta_process_country_ie(struct ieee80211_if_sta *ifsta,
+				struct ieee80211_local *local,
+				u8 *country_ie, u8 country_ie_len)
+{
+	struct ieee80211_regdomain *rd = NULL;
+	int r = 0;
+
+	if (country_ie_len < 6)
+		return -EINVAL;
+
+	rd = ieee80211_country_ie_2_rd(country_ie, country_ie_len);
+	if (!rd)
+		return -EINVAL;
+
+	r = regulatory_hint_ie(local->hw.wiphy, rd);
+
+#ifdef CONFIG_WIRELESS_OLD_REGULATORY
+	/* If the userspace regulatory agent is installed when
+	 * OLD_REGULATORY is enabled then great, if not then oh well,
+	 * we'll use low ball values */
+	return 0;
+#endif
+	if (!r) {
+		printk(KERN_ERR "cfg80211: calling CRDA failed - "
+			"unable to get country regulatory information "
+			"for country IE\n");
+		return -EINVAL;
+	}
+	return 0;
+}
+
 static void ieee80211_sta_send_associnfo(struct ieee80211_sub_if_data *sdata,
 					 struct ieee80211_if_sta *ifsta)
 {
@@ -1686,6 +1849,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
 	u32 changed = 0;
 	bool erp_valid;
 	u8 erp_value = 0;
+	int r = 0;
 
 	/* Process beacon from the current BSS */
 	baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt;
@@ -1745,6 +1909,16 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
 					       ap_ht_cap_flags);
 	}
 
+	if (elems.country_elem) {
+		r = ieee80211_sta_process_country_ie(ifsta, local,
+			elems.country_elem, elems.country_elem_len);
+#ifdef CONFIG_MAC80211_VERBOSE_SPECT_MGMT_DEBUG
+		if (!r)
+			printk(KERN_DEBUG "mac80211: Unable to parse "
+				"country IE\n");
+#endif
+	}
+
 	ieee80211_bss_info_change_notify(sdata, changed);
 }
 
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 4c7e39d..26a11cd 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -740,6 +740,13 @@ void regulatory_hint(struct wiphy *wiphy, const char *alpha2)
 }
 EXPORT_SYMBOL(regulatory_hint);
 
+int regulatory_hint_ie(struct wiphy *wiphy, struct ieee80211_regdomain *rd)
+{
+	/* Intersect rd with cfg80211_regdomain then if that is
+	 * successfull we ... */
+	return 0;
+}
+EXPORT_SYMBOL(regulatory_hint_ie);
 
 static void print_rd_rules(const struct ieee80211_regdomain *rd)
 {
-- 
1.5.6.3

--
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 Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Linux Kernel]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Samba]     [Device Mapper]
  Powered by Linux