Search Linux Wireless

[PATCH 4/5] Wireless: Add regdomain support to cfg80211

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

 



This patch makes cfg80211 provide an interface for defining
a central regulatory domain all cfg80211 wireless drivers
should adhere to.

Now when cfg80211 starts up and you will see this:

ieee80211_regdomains: regulatory domain WORLD created
Regulatory Domain:      WORLD   Regulatory Domain ID:   0x03
        IEEE 802.11g    2GHz    ISM subband
        max_ir_ptmp:    20 dBm  max_eirp_ptmp:  20 dBm
        max_ir_ptp:     0 dBm   max_eirp_ptp:   0 dBm
        max_antenna_gain:       6 dBi
        Environment capability: Indoor & Outdoor
                Channel Freq (MHz)
                5       2432
                6       2437
                7       2442

Signed-off-by: Luis R. Rodriguez <mcgrof@xxxxxxxxx>
---
 include/net/cfg80211.h |   72 ++++++++++++++++++++++++++
 net/wireless/Kconfig   |    1 +
 net/wireless/core.c    |  133 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 206 insertions(+), 0 deletions(-)

diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 1459d12..34fd74b 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -5,11 +5,13 @@
 #include <linux/skbuff.h>
 #include <linux/nl80211.h>
 #include <net/genetlink.h>
+#include <net/ieee80211_regdomains.h>
 
 /*
  * 802.11 configuration in-kernel interface
  *
  * Copyright 2006 Johannes Berg <johannes@xxxxxxxxxxxxxxxx>
+ * Copyright 2007 Luis R. Rodriguez <mcgrof@xxxxxxxxxxxxxxxxxx>
  */
 
 /**
@@ -117,6 +119,73 @@ extern int ieee80211_radiotap_iterator_next(
 struct wiphy;
 
 /**
+ * enum regdom_set_by - the different ways by which the central regulatory
+ *   domain can be set
+ *
+ * @REG_SET_BY_INIT: indicates regdomain was set by regdomain_init()
+ * @REG_SET_BY_DRIVER: indicates regdomain was set by a driver 
+ * 	through cfg80211_set_regdomain(). This type of settings is ignored if
+ * 	the regdomain was already set by userspace (%REG_SET_BY_USER). If
+ * 	userspace never set the regdomain and two drivers are found trying to
+ * 	set the regdomain to two different regdomains then the world regulatory
+ * 	domain will be set by cfg80211_set_regdomain().
+ * @REG_SET_BY_USER: indicates regdomain was set by userspace.
+ * @REG_SET_BY_80211D: indicates an 802.11d frame was received with a valid
+ * 	country information element to which the wireless stack is going to
+ * 	adhere to.
+ * @REG_SET_BY_CONFLICT: indicates a conflict was found and regdomain was 
+ * 	therefore set to the world regulatory domain by 
+ * 	cfg80211_set_regdomain().
+ */
+enum regdom_set_by {
+	REG_SET_BY_INIT = 1,
+	REG_SET_BY_DRIVER,
+	REG_SET_BY_USER,
+	REG_SET_BY_80211D,
+	REG_SET_BY_CONFLICT
+};
+
+/**
+ * struct cfg80211_regdomain_settings - central regulatory domain settings
+ *
+ * @regdomain: central regulatory domain, an &ieee80211_regdomain
+ *
+ * @country: country to which this regulatory domain corresponds in the 
+ * 	last setting. This must be a valid ISO3166-1 country or one of the 
+ * 	accepted exceptions, such as 00 for the world regulatory domain or 
+ * 	UK for the United Kingdom.
+ *
+ * @set_by: tells us who set last the regualtory domain. This can be any of 
+ * 	%regdom_set_by
+ *
+ * @wiphy: wiphy of driver who last set the regdomain, if set by 
+ * 	driver. This used to determine conflicts of driver regulatory domains 
+ * 	settings if more than one wirless card is present and to simply keep
+ * 	record if a driver set the regulatory domain which driver set it.
+ *
+ * This structure defines the cfg80211 central wireless regulatory domain
+ * structure which all cfg80211 drivers should adhere to. It is initialized to
+ * the world regulatory domain by cfg80211 by default (%REG_SET_BY_INIT). If a
+ * wireless driver present has a built-in regulatory domain set either in the
+ * EEPROM or firmware it can inform cfg80211 about this and change the 
+ * regulatory domain as such (%REG_SET_BY_DRIVER). If a second wireless card
+ * is present and if there is a conflict between regulatory domains then the
+ * world regulatory domain is set (%REG_SET_BY_CONFLICT). The regulatory domain
+ * can also be changed by receiving an 802.11d frame with a valid country code
+ * (%REG_SET_BY_80211D). Finally, If the regulatory domain was set by userspace
+ * (%REG_SET_BY_USER) then driver specific setting of regulatory domains 
+ * (%REG_SET_BY_DRIVER) and 802.11d frames for changing the regulatory domain
+ * (%REG_SET_BY_80211D) will be ignored.
+ */
+struct cfg80211_regdomain_settings {
+	struct ieee80211_regdomain *regdomain;
+	enum regdom_set_by set_by;
+	struct wiphy *wiphy;
+	char country[ISOCOUNTRYSIZ2];
+};
+
+
+/**
  * struct cfg80211_ops - backend description for wireless configuration
  *
  * This struct is registered by fullmac card drivers and/or wireless stacks
@@ -183,6 +252,9 @@ struct cfg80211_ops {
 			   struct key_params *params);
 };
 
+extern int cfg80211_set_regdomain(char *country, u32,
+				enum regdom_set_by set_by,
+				struct wiphy *wiphy_reg_set);
 
 /* helper functions specific to nl80211 */
 extern void *nl80211hdr_put(struct sk_buff *skb, u32 pid,
diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig
index c60ee87..0505347 100644
--- a/net/wireless/Kconfig
+++ b/net/wireless/Kconfig
@@ -1,5 +1,6 @@
 config CFG80211
         tristate "Improved wireless configuration API"
+	select IEEE80211_REGDOMAINS
 
 config NL80211
 	bool "nl80211 new netlink interface support"
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 2f3f7b6..c2f773b 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -2,6 +2,7 @@
  * This is the linux wireless configuration interface.
  *
  * Copyright 2006, 2007		Johannes Berg <johannes@xxxxxxxxxxxxxxxx>
+ * Copyright 2007		Luis R. Rodriguez <mcgrof@xxxxxxxxxxxxxxxxxx>
  */
 
 #include <linux/if.h>
@@ -34,6 +35,14 @@ LIST_HEAD(cfg80211_drv_list);
 DEFINE_MUTEX(cfg80211_drv_mutex);
 static int wiphy_counter;
 
+/* All cfg80211 drivers adhere to this */
+struct cfg80211_regdomain_settings cfg80211_regdomain;
+EXPORT_SYMBOL(cfg80211_regdomain);
+/* Since the regulatory domain may be set by different events we protect its
+ * structure */
+DEFINE_MUTEX(cfg80211_regdomain_mutex);
+EXPORT_SYMBOL(cfg80211_regdomain_mutex);
+
 /* for debugfs */
 static struct dentry *ieee80211_debugfs_dir;
 
@@ -335,12 +344,135 @@ static struct notifier_block cfg80211_netdev_notifier = {
 	.notifier_call = cfg80211_netdev_notifier_call,
 };
 
+int cfg80211_set_regdomain(char *country, u32 regdomain_id,
+	enum regdom_set_by set_by, 
+	struct wiphy *wiphy_reg_set)
+{
+	struct ieee80211_regdomain *old_reg, *new_reg;
+	int r = 0;
+	enum regdom_set_by old_set_by;
+	int needs_freeing = 0;
+
+	mutex_lock(&cfg80211_regdomain_mutex);
+	old_reg = cfg80211_regdomain.regdomain;
+	old_set_by = cfg80211_regdomain.set_by;
+
+	switch (cfg80211_regdomain.set_by) {
+	case 0:
+		switch (set_by) {
+		case REG_SET_BY_INIT:
+			/* XXX: Test this first */
+			cfg80211_regdomain.set_by = REG_SET_BY_INIT;
+			break;
+		default:
+			/* Should not happen unless later we support clearing 
+			 * of set_by before calling this routine */
+			r = -EOPNOTSUPP;
+			goto unlock_and_exit;
+		}
+		break;
+	case REG_SET_BY_INIT:
+		switch (set_by) {
+		case REG_SET_BY_INIT:
+			/* Should never happen */
+			r = -EOPNOTSUPP;
+			goto unlock_and_exit;
+		case REG_SET_BY_DRIVER:
+			if (wiphy_reg_set == NULL) {
+				r =-EINVAL;
+				goto unlock_and_exit;
+			}
+			/* XXX: This is not handled yet */
+			r = -EOPNOTSUPP; /* Not handled yet */
+			goto unlock_and_exit;
+		case REG_SET_BY_USER:
+			printk("Userspace changed regulatory domain\n");
+			/* XXX don't free until we know for sure we set
+			 * a new regdomain, then free it and arrange pointers */
+			needs_freeing = 1;
+			cfg80211_regdomain.set_by = REG_SET_BY_USER;
+			break;
+		default:
+			BUG();
+		}
+		break;
+	case REG_SET_BY_USER:
+		switch (set_by) {
+		case REG_SET_BY_USER:
+			printk("Userspace changed regulatory domain\n");
+			needs_freeing = 1;
+			cfg80211_regdomain.set_by = REG_SET_BY_USER;
+			break;
+		default:
+			r = -EOPNOTSUPP; /* Not handled yet */
+			goto unlock_and_exit;
+		}
+		break;
+	default:
+		r = -EOPNOTSUPP; /* Not handled yet */
+		goto unlock_and_exit;
+		break;
+	}
+
+	/* This does all the work */
+	r = regdomain_build(regdomain_id, &new_reg);
+	if(r) {
+		cfg80211_regdomain.set_by =  old_set_by;
+		goto unlock_and_exit;
+	}
+
+	if (needs_freeing) {
+		free_regdomain(old_reg);
+		cfg80211_regdomain.regdomain = NULL;
+	}
+
+	strcpy(cfg80211_regdomain.country, country);
+	cfg80211_regdomain.country[ISOCOUNTRYSIZ2] = '\0';
+	/* Lastly, update pointer */
+	cfg80211_regdomain.regdomain = new_reg;
+	mutex_unlock(&cfg80211_regdomain_mutex);
+	print_regdomain(cfg80211_regdomain.regdomain);
+
+	return 0;
+
+unlock_and_exit:
+	mutex_unlock(&cfg80211_regdomain_mutex);
+	return r;
+}
+EXPORT_SYMBOL(cfg80211_set_regdomain);
+
+static int regdomain_init(void)
+{
+	int r;
+	char country[ISOCOUNTRYSIZ2] = DEFAULT_REG_ISO3166_1;
+	u32 regdomain_id = 0; /* Initialize just to shut up GCC */
+
+	country[ISOCOUNTRYSIZ2] = '\0';
+
+	memset(&cfg80211_regdomain, 0, 
+		sizeof(struct cfg80211_regdomain_settings));
+
+	r = iso3166_to_reg(country, &regdomain_id);
+	if (r)
+		return r;
+	r = cfg80211_set_regdomain(country, regdomain_id, 
+		REG_SET_BY_INIT, NULL);
+	if (r)
+		return r;
+
+	return 0;
+}
+
 static int cfg80211_init(void)
 {
 	int err = wiphy_sysfs_init();
 	if (err)
 		goto out_fail_sysfs;
 
+	err = regdomain_init();
+	if (err)
+		goto out_fail_sysfs;
+
 	err = register_netdevice_notifier(&cfg80211_netdev_notifier);
 	if (err)
 		goto out_fail_notifier;
@@ -367,6 +499,7 @@ static void cfg80211_exit(void)
 	debugfs_remove(ieee80211_debugfs_dir);
 	nl80211_exit();
 	unregister_netdevice_notifier(&cfg80211_netdev_notifier);
+	free_regdomain(cfg80211_regdomain.regdomain);
 	wiphy_sysfs_exit();
 }
 module_exit(cfg80211_exit);
-- 
1.5.2.4

Attachment: signature.asc
Description: Digital signature


[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