From 2df78167ade3d0542f0f95be7cc58861343cce3a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 28 Oct 2008 16:49:41 +0100 Subject: wireless: fix a few sparse warnings Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/reg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/wireless') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 9dff716d1b02..8e5d23975a0d 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -638,7 +638,7 @@ static void print_regdomain(const struct ieee80211_regdomain *rd) print_rd_rules(rd); } -void print_regdomain_info(const struct ieee80211_regdomain *rd) +static void print_regdomain_info(const struct ieee80211_regdomain *rd) { printk(KERN_INFO "cfg80211: Regulatory domain: %c%c\n", rd->alpha2[0], rd->alpha2[1]); -- cgit v1.2.3 From bd815252720e4b667d9946d050d003ec89bda099 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 29 Oct 2008 20:00:45 +0100 Subject: wireless: implement basic rate helper function This adds a helper function that, given a bitmap of basic rates and a bitrate returns the response rate for this rate. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/wireless.h | 16 ++++++++++++++++ net/wireless/util.c | 19 +++++++++++++++++++ 2 files changed, 35 insertions(+) (limited to 'net/wireless') diff --git a/include/net/wireless.h b/include/net/wireless.h index 41294c5f6f8f..17d4b582cf34 100644 --- a/include/net/wireless.h +++ b/include/net/wireless.h @@ -340,6 +340,22 @@ ieee80211_get_channel(struct wiphy *wiphy, int freq) return __ieee80211_get_channel(wiphy, freq); } +/** + * ieee80211_get_response_rate - get basic rate for a given rate + * + * @sband: the band to look for rates in + * @basic_rates: bitmap of basic rates + * @bitrate: the bitrate for which to find the basic rate + * + * This function returns the basic rate corresponding to a given + * bitrate, that is the next lower bitrate contained in the basic + * rate map, which is, for this function, given as a bitmap of + * indices of rates in the band's bitrate table. + */ +struct ieee80211_rate * +ieee80211_get_response_rate(struct ieee80211_supported_band *sband, + u64 basic_rates, int bitrate); + /** * regulatory_hint - driver hint to the wireless core a regulatory domain * @wiphy: the wireless device giving the hint (used only for reporting diff --git a/net/wireless/util.c b/net/wireless/util.c index f54424693a38..e76cc28b0345 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -7,6 +7,25 @@ #include #include "core.h" +struct ieee80211_rate * +ieee80211_get_response_rate(struct ieee80211_supported_band *sband, + u64 basic_rates, int bitrate) +{ + struct ieee80211_rate *result = &sband->bitrates[0]; + int i; + + for (i = 0; i < sband->n_bitrates; i++) { + if (!(basic_rates & BIT(i))) + continue; + if (sband->bitrates[i].bitrate > bitrate) + continue; + result = &sband->bitrates[i]; + } + + return result; +} +EXPORT_SYMBOL(ieee80211_get_response_rate); + int ieee80211_channel_to_frequency(int chan) { if (chan < 14) -- cgit v1.2.3 From 90c97a040d6b08cc4890328aa262fdc37336ab01 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 30 Oct 2008 16:59:22 +0200 Subject: nl80211: Add basic rate configuration for AP mode Add a new attribute, NL80211_ATTR_BSS_BASIC_RATES, that can be used with NL80211_CMD_SET_BSS for userspace (e.g., hostapd) to set which rates are in the basic rate set. Signed-off-by: Jouni Malinen Acked-by: Johannes Berg Signed-off-by: John W. Linville --- include/linux/nl80211.h | 6 ++++++ include/net/cfg80211.h | 5 +++++ net/mac80211/cfg.c | 18 ++++++++++++++++++ net/wireless/nl80211.c | 8 ++++++++ 4 files changed, 37 insertions(+) (limited to 'net/wireless') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index e4cc7869b22f..5009809588c0 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -243,6 +243,9 @@ enum nl80211_commands { * (u8, 0 or 1) * @NL80211_ATTR_BSS_SHORT_SLOT_TIME: whether short slot time enabled * (u8, 0 or 1) + * @NL80211_ATTR_BSS_BASIC_RATES: basic rates, array of basic + * rates in format defined by IEEE 802.11 7.3.2.2 but without the length + * restriction (at most %NL80211_MAX_SUPP_RATES). * * @NL80211_ATTR_HT_CAPABILITY: HT Capability information element (from * association request when used with NL80211_CMD_NEW_STATION) @@ -307,6 +310,8 @@ enum nl80211_attrs { NL80211_ATTR_MESH_PARAMS, + NL80211_ATTR_BSS_BASIC_RATES, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -318,6 +323,7 @@ enum nl80211_attrs { * here */ #define NL80211_ATTR_HT_CAPABILITY NL80211_ATTR_HT_CAPABILITY +#define NL80211_ATTR_BSS_BASIC_RATES NL80211_ATTR_BSS_BASIC_RATES #define NL80211_MAX_SUPP_RATES 32 #define NL80211_MAX_SUPP_REG_RULES 32 diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 03e1e88c6a09..7caf3c76a12f 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -280,11 +280,16 @@ struct mpath_info { * (0 = no, 1 = yes, -1 = do not change) * @use_short_slot_time: Whether the use of short slot time is allowed * (0 = no, 1 = yes, -1 = do not change) + * @basic_rates: basic rates in IEEE 802.11 format + * (or NULL for no change) + * @basic_rates_len: number of basic rates */ struct bss_parameters { int use_cts_prot; int use_short_preamble; int use_short_slot_time; + u8 *basic_rates; + u8 basic_rates_len; }; /** diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 91f56a48e2b4..442a4d7b1808 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1046,6 +1046,24 @@ static int ieee80211_change_bss(struct wiphy *wiphy, changed |= BSS_CHANGED_ERP_SLOT; } + if (params->basic_rates) { + int i, j; + u32 rates = 0; + struct ieee80211_local *local = wiphy_priv(wiphy); + struct ieee80211_supported_band *sband = + wiphy->bands[local->oper_channel->band]; + + for (i = 0; i < params->basic_rates_len; i++) { + int rate = (params->basic_rates[i] & 0x7f) * 5; + for (j = 0; j < sband->n_bitrates; j++) { + if (sband->bitrates[j].bitrate == rate) + rates |= BIT(j); + } + } + sdata->vif.bss_conf.basic_rates = rates; + changed |= BSS_CHANGED_BASIC_RATES; + } + ieee80211_bss_info_change_notify(sdata, changed); return 0; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 5e1d658a8b5a..1ea5e3fd3931 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -95,6 +95,8 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { [NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 }, [NL80211_ATTR_BSS_SHORT_PREAMBLE] = { .type = NLA_U8 }, [NL80211_ATTR_BSS_SHORT_SLOT_TIME] = { .type = NLA_U8 }, + [NL80211_ATTR_BSS_BASIC_RATES] = { .type = NLA_BINARY, + .len = NL80211_MAX_SUPP_RATES }, [NL80211_ATTR_MESH_PARAMS] = { .type = NLA_NESTED }, @@ -1613,6 +1615,12 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]) params.use_short_slot_time = nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]); + if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) { + params.basic_rates = + nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); + params.basic_rates_len = + nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); + } err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); if (err) -- cgit v1.2.3 From 318884875bdddca663ecc373c813cf8e117d9e43 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 30 Oct 2008 16:59:24 +0200 Subject: nl80211: Add TX queue parameter configuration Add a new attribute, NL80211_ATTR_WIPHY_TXQ_PARAMS, that can be used with NL80211_CMD_SET_WIPHY for userspace (e.g., hostapd) to set TX queue parameters (txop, cwmin, cwmax, aifs). Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville --- include/linux/nl80211.h | 43 +++++++++++++++++++++++++++++-- include/net/cfg80211.h | 23 +++++++++++++++++ net/mac80211/cfg.c | 25 ++++++++++++++++++ net/wireless/nl80211.c | 67 +++++++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 151 insertions(+), 7 deletions(-) (limited to 'net/wireless') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 5009809588c0..79827345351d 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -25,8 +25,9 @@ * * @NL80211_CMD_GET_WIPHY: request information about a wiphy or dump request * to get a list of all present wiphys. - * @NL80211_CMD_SET_WIPHY: set wiphy name, needs %NL80211_ATTR_WIPHY and - * %NL80211_ATTR_WIPHY_NAME. + * @NL80211_CMD_SET_WIPHY: set wiphy parameters, needs %NL80211_ATTR_WIPHY or + * %NL80211_ATTR_IFINDEX; can be used to set %NL80211_ATTR_WIPHY_NAME + * and/or %NL80211_ATTR_WIPHY_TXQ_PARAMS. * @NL80211_CMD_NEW_WIPHY: Newly created wiphy, response to get request * or rename notification. Has attributes %NL80211_ATTR_WIPHY and * %NL80211_ATTR_WIPHY_NAME. @@ -178,6 +179,7 @@ enum nl80211_commands { * @NL80211_ATTR_WIPHY: index of wiphy to operate on, cf. * /sys/class/ieee80211//index * @NL80211_ATTR_WIPHY_NAME: wiphy name (used for renaming) + * @NL80211_ATTR_WIPHY_TXQ_PARAMS: a nested array of TX queue parameters * * @NL80211_ATTR_IFINDEX: network interface index of the device to operate on * @NL80211_ATTR_IFNAME: network interface name @@ -312,6 +314,8 @@ enum nl80211_attrs { NL80211_ATTR_BSS_BASIC_RATES, + NL80211_ATTR_WIPHY_TXQ_PARAMS, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -324,6 +328,7 @@ enum nl80211_attrs { */ #define NL80211_ATTR_HT_CAPABILITY NL80211_ATTR_HT_CAPABILITY #define NL80211_ATTR_BSS_BASIC_RATES NL80211_ATTR_BSS_BASIC_RATES +#define NL80211_ATTR_WIPHY_TXQ_PARAMS NL80211_ATTR_WIPHY_TXQ_PARAMS #define NL80211_MAX_SUPP_RATES 32 #define NL80211_MAX_SUPP_REG_RULES 32 @@ -698,4 +703,38 @@ enum nl80211_meshconf_params { NL80211_MESHCONF_ATTR_MAX = __NL80211_MESHCONF_ATTR_AFTER_LAST - 1 }; +/** + * enum nl80211_txq_attr - TX queue parameter attributes + * @__NL80211_TXQ_ATTR_INVALID: Attribute number 0 is reserved + * @NL80211_TXQ_ATTR_QUEUE: TX queue identifier (NL80211_TXQ_Q_*) + * @NL80211_TXQ_ATTR_TXOP: Maximum burst time in units of 32 usecs, 0 meaning + * disabled + * @NL80211_TXQ_ATTR_CWMIN: Minimum contention window [a value of the form + * 2^n-1 in the range 1..32767] + * @NL80211_TXQ_ATTR_CWMAX: Maximum contention window [a value of the form + * 2^n-1 in the range 1..32767] + * @NL80211_TXQ_ATTR_AIFS: Arbitration interframe space [0..255] + * @__NL80211_TXQ_ATTR_AFTER_LAST: Internal + * @NL80211_TXQ_ATTR_MAX: Maximum TXQ attribute number + */ +enum nl80211_txq_attr { + __NL80211_TXQ_ATTR_INVALID, + NL80211_TXQ_ATTR_QUEUE, + NL80211_TXQ_ATTR_TXOP, + NL80211_TXQ_ATTR_CWMIN, + NL80211_TXQ_ATTR_CWMAX, + NL80211_TXQ_ATTR_AIFS, + + /* keep last */ + __NL80211_TXQ_ATTR_AFTER_LAST, + NL80211_TXQ_ATTR_MAX = __NL80211_TXQ_ATTR_AFTER_LAST - 1 +}; + +enum nl80211_txq_q { + NL80211_TXQ_Q_VO, + NL80211_TXQ_Q_VI, + NL80211_TXQ_Q_BE, + NL80211_TXQ_Q_BK +}; + #endif /* __LINUX_NL80211_H */ diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 7caf3c76a12f..448023193e55 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -371,6 +371,24 @@ struct mesh_config { u16 dot11MeshHWMPnetDiameterTraversalTime; }; +/** + * struct ieee80211_txq_params - TX queue parameters + * @queue: TX queue identifier (NL80211_TXQ_Q_*) + * @txop: Maximum burst time in units of 32 usecs, 0 meaning disabled + * @cwmin: Minimum contention window [a value of the form 2^n-1 in the range + * 1..32767] + * @cwmax: Maximum contention window [a value of the form 2^n-1 in the range + * 1..32767] + * @aifs: Arbitration interframe space [0..255] + */ +struct ieee80211_txq_params { + enum nl80211_txq_q queue; + u16 txop; + u16 cwmin; + u16 cwmax; + u8 aifs; +}; + /* from net/wireless.h */ struct wiphy; @@ -430,6 +448,8 @@ struct wiphy; * @set_mesh_cfg: set mesh parameters (by now, just mesh id) * * @change_bss: Modify parameters for a given BSS. + * + * @set_txq_params: Set TX queue parameters */ struct cfg80211_ops { int (*add_virtual_intf)(struct wiphy *wiphy, char *name, @@ -490,6 +510,9 @@ struct cfg80211_ops { const struct mesh_config *nconf, u32 mask); int (*change_bss)(struct wiphy *wiphy, struct net_device *dev, struct bss_parameters *params); + + int (*set_txq_params)(struct wiphy *wiphy, + struct ieee80211_txq_params *params); }; #endif /* __NET_CFG80211_H */ diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 442a4d7b1808..fe672faa819d 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1069,6 +1069,30 @@ static int ieee80211_change_bss(struct wiphy *wiphy, return 0; } +static int ieee80211_set_txq_params(struct wiphy *wiphy, + struct ieee80211_txq_params *params) +{ + struct ieee80211_local *local = wiphy_priv(wiphy); + struct ieee80211_tx_queue_params p; + + if (!local->ops->conf_tx) + return -EOPNOTSUPP; + + memset(&p, 0, sizeof(p)); + p.aifs = params->aifs; + p.cw_max = params->cwmax; + p.cw_min = params->cwmin; + p.txop = params->txop; + if (local->ops->conf_tx(local_to_hw(local), params->queue, &p)) { + printk(KERN_DEBUG "%s: failed to set TX queue " + "parameters for queue %d\n", local->mdev->name, + params->queue); + return -EINVAL; + } + + return 0; +} + struct cfg80211_ops mac80211_config_ops = { .add_virtual_intf = ieee80211_add_iface, .del_virtual_intf = ieee80211_del_iface, @@ -1095,4 +1119,5 @@ struct cfg80211_ops mac80211_config_ops = { .get_mesh_params = ieee80211_get_mesh_params, #endif .change_bss = ieee80211_change_bss, + .set_txq_params = ieee80211_set_txq_params, }; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 1ea5e3fd3931..e3e1494e769a 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -58,6 +58,7 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { [NL80211_ATTR_WIPHY] = { .type = NLA_U32 }, [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING, .len = BUS_ID_SIZE-1 }, + [NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED }, [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 }, [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 }, @@ -286,20 +287,76 @@ static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info) return -ENOBUFS; } +static const struct nla_policy txq_params_policy[NL80211_TXQ_ATTR_MAX + 1] = { + [NL80211_TXQ_ATTR_QUEUE] = { .type = NLA_U8 }, + [NL80211_TXQ_ATTR_TXOP] = { .type = NLA_U16 }, + [NL80211_TXQ_ATTR_CWMIN] = { .type = NLA_U16 }, + [NL80211_TXQ_ATTR_CWMAX] = { .type = NLA_U16 }, + [NL80211_TXQ_ATTR_AIFS] = { .type = NLA_U8 }, +}; + +static int parse_txq_params(struct nlattr *tb[], + struct ieee80211_txq_params *txq_params) +{ + if (!tb[NL80211_TXQ_ATTR_QUEUE] || !tb[NL80211_TXQ_ATTR_TXOP] || + !tb[NL80211_TXQ_ATTR_CWMIN] || !tb[NL80211_TXQ_ATTR_CWMAX] || + !tb[NL80211_TXQ_ATTR_AIFS]) + return -EINVAL; + + txq_params->queue = nla_get_u8(tb[NL80211_TXQ_ATTR_QUEUE]); + txq_params->txop = nla_get_u16(tb[NL80211_TXQ_ATTR_TXOP]); + txq_params->cwmin = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMIN]); + txq_params->cwmax = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMAX]); + txq_params->aifs = nla_get_u8(tb[NL80211_TXQ_ATTR_AIFS]); + + return 0; +} + static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev; - int result; - - if (!info->attrs[NL80211_ATTR_WIPHY_NAME]) - return -EINVAL; + int result = 0, rem_txq_params = 0; + struct nlattr *nl_txq_params; rdev = cfg80211_get_dev_from_info(info); if (IS_ERR(rdev)) return PTR_ERR(rdev); - result = cfg80211_dev_rename(rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME])); + if (info->attrs[NL80211_ATTR_WIPHY_NAME]) { + result = cfg80211_dev_rename( + rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME])); + if (result) + goto bad_res; + } + + if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) { + struct ieee80211_txq_params txq_params; + struct nlattr *tb[NL80211_TXQ_ATTR_MAX + 1]; + + if (!rdev->ops->set_txq_params) { + result = -EOPNOTSUPP; + goto bad_res; + } + + nla_for_each_nested(nl_txq_params, + info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS], + rem_txq_params) { + nla_parse(tb, NL80211_TXQ_ATTR_MAX, + nla_data(nl_txq_params), + nla_len(nl_txq_params), + txq_params_policy); + result = parse_txq_params(tb, &txq_params); + if (result) + goto bad_res; + + result = rdev->ops->set_txq_params(&rdev->wiphy, + &txq_params); + if (result) + goto bad_res; + } + } +bad_res: cfg80211_put_dev(rdev); return result; } -- cgit v1.2.3 From d71aaf6053e380996e40c9ecfe39a8dced52694c Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Thu, 30 Oct 2008 13:33:52 -0700 Subject: cfg80211: a reg rule is invalid if freq diff is 0 A regulatory rule is invalid when the frequency difference between the end of the frequency range and the start is 0. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/wireless/reg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/wireless') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 8e5d23975a0d..34983a825ac1 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -321,7 +321,7 @@ static bool is_valid_reg_rule(const struct ieee80211_reg_rule *rule) freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz; - if (freq_range->max_bandwidth_khz > freq_diff) + if (freq_diff <= 0 || freq_range->max_bandwidth_khz > freq_diff) return false; return true; -- cgit v1.2.3 From 9c96477d10763d4314012425ecc8a3cff9e60ea9 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Thu, 30 Oct 2008 13:33:53 -0700 Subject: cfg80211: Add regulatory domain intersection capability There are certain scenerios where we require intersecting two regulatory domains. This adds intersection support. When we enable 802.11d support we will use this to intersect the regulatory domain from the AP's country IE and what our regulatory agent believes is correct for a country. This patch enables intersection for now in the case where the last regdomain was set by a country IE which was parsed and the user then wants to set the regulatory domain. Since we don't support country IE parsing yet this code path will not be hit, however this allows us to pave the way for 11d support. Intersection code has been tested in userspace with CRDA. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/wireless/reg.c | 166 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 158 insertions(+), 8 deletions(-) (limited to 'net/wireless') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 34983a825ac1..3f18a1d03a55 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -50,6 +50,7 @@ struct regulatory_request { struct wiphy *wiphy; enum reg_set_by initiator; char alpha2[2]; + bool intersect; }; static struct regulatory_request *last_request; @@ -359,6 +360,143 @@ static u32 freq_max_bandwidth(const struct ieee80211_freq_range *freq_range, return 0; } +/* Helper for regdom_intersect(), this does the real + * mathematical intersection fun */ +static int reg_rules_intersect( + const struct ieee80211_reg_rule *rule1, + const struct ieee80211_reg_rule *rule2, + struct ieee80211_reg_rule *intersected_rule) +{ + const struct ieee80211_freq_range *freq_range1, *freq_range2; + struct ieee80211_freq_range *freq_range; + const struct ieee80211_power_rule *power_rule1, *power_rule2; + struct ieee80211_power_rule *power_rule; + u32 freq_diff; + + freq_range1 = &rule1->freq_range; + freq_range2 = &rule2->freq_range; + freq_range = &intersected_rule->freq_range; + + power_rule1 = &rule1->power_rule; + power_rule2 = &rule2->power_rule; + power_rule = &intersected_rule->power_rule; + + freq_range->start_freq_khz = max(freq_range1->start_freq_khz, + freq_range2->start_freq_khz); + freq_range->end_freq_khz = min(freq_range1->end_freq_khz, + freq_range2->end_freq_khz); + freq_range->max_bandwidth_khz = min(freq_range1->max_bandwidth_khz, + freq_range2->max_bandwidth_khz); + + freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz; + if (freq_range->max_bandwidth_khz > freq_diff) + freq_range->max_bandwidth_khz = freq_diff; + + power_rule->max_eirp = min(power_rule1->max_eirp, + power_rule2->max_eirp); + power_rule->max_antenna_gain = min(power_rule1->max_antenna_gain, + power_rule2->max_antenna_gain); + + intersected_rule->flags = (rule1->flags | rule2->flags); + + if (!is_valid_reg_rule(intersected_rule)) + return -EINVAL; + + return 0; +} + +/** + * regdom_intersect - do the intersection between two regulatory domains + * @rd1: first regulatory domain + * @rd2: second regulatory domain + * + * Use this function to get the intersection between two regulatory domains. + * Once completed we will mark the alpha2 for the rd as intersected, "98", + * as no one single alpha2 can represent this regulatory domain. + * + * Returns a pointer to the regulatory domain structure which will hold the + * resulting intersection of rules between rd1 and rd2. We will + * kzalloc() this structure for you. + */ +static struct ieee80211_regdomain *regdom_intersect( + const struct ieee80211_regdomain *rd1, + const struct ieee80211_regdomain *rd2) +{ + int r, size_of_regd; + unsigned int x, y; + unsigned int num_rules = 0, rule_idx = 0; + const struct ieee80211_reg_rule *rule1, *rule2; + struct ieee80211_reg_rule *intersected_rule; + struct ieee80211_regdomain *rd; + /* This is just a dummy holder to help us count */ + struct ieee80211_reg_rule irule; + + /* Uses the stack temporarily for counter arithmetic */ + intersected_rule = &irule; + + memset(intersected_rule, 0, sizeof(struct ieee80211_reg_rule)); + + if (!rd1 || !rd2) + return NULL; + + /* First we get a count of the rules we'll need, then we actually + * build them. This is to so we can malloc() and free() a + * regdomain once. The reason we use reg_rules_intersect() here + * is it will return -EINVAL if the rule computed makes no sense. + * All rules that do check out OK are valid. */ + + for (x = 0; x < rd1->n_reg_rules; x++) { + rule1 = &rd1->reg_rules[x]; + for (y = 0; y < rd2->n_reg_rules; y++) { + rule2 = &rd2->reg_rules[y]; + if (!reg_rules_intersect(rule1, rule2, + intersected_rule)) + num_rules++; + memset(intersected_rule, 0, + sizeof(struct ieee80211_reg_rule)); + } + } + + if (!num_rules) + return NULL; + + size_of_regd = sizeof(struct ieee80211_regdomain) + + ((num_rules + 1) * sizeof(struct ieee80211_reg_rule)); + + rd = kzalloc(size_of_regd, GFP_KERNEL); + if (!rd) + return NULL; + + for (x = 0; x < rd1->n_reg_rules; x++) { + rule1 = &rd1->reg_rules[x]; + for (y = 0; y < rd2->n_reg_rules; y++) { + rule2 = &rd2->reg_rules[y]; + /* This time around instead of using the stack lets + * write to the target rule directly saving ourselves + * a memcpy() */ + intersected_rule = &rd->reg_rules[rule_idx]; + r = reg_rules_intersect(rule1, rule2, + intersected_rule); + /* No need to memset here the intersected rule here as + * we're not using the stack anymore */ + if (r) + continue; + rule_idx++; + } + } + + if (rule_idx != num_rules) { + kfree(rd); + return NULL; + } + + rd->n_reg_rules = num_rules; + rd->alpha2[0] = '9'; + rd->alpha2[1] = '8'; + + return rd; +} + /* XXX: add support for the rest of enum nl80211_reg_rule_flags, we may * want to just have the channel structure use these */ static u32 map_regdom_flags(u32 rd_flags) @@ -468,6 +606,10 @@ void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby) } } +/* Return value which can be used by ignore_request() to indicate + * it has been determined we should intersect two regulatory domains */ +#define REG_INTERSECT 1 + /* This has the logic which determines when a new request * should be ignored. */ static int ignore_request(struct wiphy *wiphy, enum reg_set_by set_by, @@ -517,14 +659,8 @@ static int ignore_request(struct wiphy *wiphy, enum reg_set_by set_by, return -EALREADY; return 0; case REGDOM_SET_BY_USER: - /* - * If the user wants to override the AP's hint, we may - * need to follow both and use the intersection. For now, - * reject any such attempt (but we don't support country - * IEs right now anyway.) - */ if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) - return -EOPNOTSUPP; + return REG_INTERSECT; return 0; } @@ -536,10 +672,14 @@ int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by, const char *alpha2) { struct regulatory_request *request; + bool intersect = false; int r = 0; r = ignore_request(wiphy, set_by, alpha2); - if (r) + + if (r == REG_INTERSECT) + intersect = true; + else if (r) return r; switch (set_by) { @@ -556,6 +696,7 @@ int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by, request->alpha2[1] = alpha2[1]; request->initiator = set_by; request->wiphy = wiphy; + request->intersect = intersect; kfree(last_request); last_request = request; @@ -648,6 +789,7 @@ static void print_regdomain_info(const struct ieee80211_regdomain *rd) /* Takes ownership of rd only if it doesn't fail */ static int __set_regdom(const struct ieee80211_regdomain *rd) { + const struct ieee80211_regdomain *intersected_rd = NULL; /* Some basic sanity checks first */ if (is_world_regdom(rd->alpha2)) { @@ -697,6 +839,14 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) return -EOPNOTSUPP; } + if (unlikely(last_request->intersect)) { + intersected_rd = regdom_intersect(rd, cfg80211_regdomain); + if (!intersected_rd) + return -EINVAL; + kfree(rd); + rd = intersected_rd; + } + /* Tada! */ cfg80211_regdomain = rd; -- cgit v1.2.3 From 5166ccd220f187b2e9edfc5f01eb49f4a0ebf586 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Thu, 30 Oct 2008 13:33:56 -0700 Subject: cfg80211: Add kdoc for struct regulatory_request As regulatory_request gets bigger there will be more questions of what things means, so clarify documenation for it and keep track of the special alpha2 codes we use internally and on the userspace regulatory agents. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/wireless/reg.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) (limited to 'net/wireless') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 3f18a1d03a55..4c7e39d466c4 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -42,9 +42,24 @@ #include "core.h" #include "reg.h" -/* - * wiphy is set if this request's initiator is - * REGDOM_SET_BY_COUNTRY_IE or _DRIVER +/** + * struct regulatory_request - receipt of last regulatory request + * + * @wiphy: this is set if this request's initiator is + * %REGDOM_SET_BY_COUNTRY_IE or %REGDOM_SET_BY_DRIVER. This + * can be used by the wireless core to deal with conflicts + * and potentially inform users of which devices specifically + * cased the conflicts. + * @initiator: indicates who sent this request, could be any of + * of those set in reg_set_by, %REGDOM_SET_BY_* + * @alpha2: the ISO / IEC 3166 alpha2 country code of the requested + * regulatory domain. We have a few special codes: + * 00 - World regulatory domain + * 99 - built by driver but a specific alpha2 cannot be determined + * 98 - result of an intersection between two regulatory domains + * @intersect: indicates whether the wireless core should intersect + * the requested regulatory domain with the presently set regulatory + * domain. */ struct regulatory_request { struct wiphy *wiphy; @@ -53,6 +68,7 @@ struct regulatory_request { bool intersect; }; +/* Receipt of information from last regulatory request */ static struct regulatory_request *last_request; /* To trigger userspace events */ -- cgit v1.2.3