Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 183592
b: refs/heads/master
c: cc5d8a3
h: refs/heads/master
v: v3
  • Loading branch information
Luis R. Rodriguez authored and John W. Linville committed Jan 12, 2010
1 parent 0deafdb commit f90b62a
Show file tree
Hide file tree
Showing 2 changed files with 222 additions and 5 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 08030db6e5275dda19ea1b3ab8a41c992799db4a
refs/heads/master: cc5d8a3772ee4e2ed29558ba548b4747959ba971
225 changes: 221 additions & 4 deletions trunk/net/wireless/reg.c
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,178 @@ static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range,
#undef ONE_GHZ_IN_KHZ
}

/*
* Some APs may send a country IE triplet for each channel they
* support and while this is completely overkill and silly we still
* need to support it. We avoid making a single rule for each channel
* though and to help us with this we use this helper to find the
* actual subband end channel. These type of country IE triplet
* scenerios are handled then, all yielding two regulaotry rules from
* parsing a country IE:
*
* [1]
* [2]
* [36]
* [40]
*
* [1]
* [2-4]
* [5-12]
* [36]
* [40-44]
*
* [1-4]
* [5-7]
* [36-44]
* [48-64]
*
* [36-36]
* [40-40]
* [44-44]
* [48-48]
* [52-52]
* [56-56]
* [60-60]
* [64-64]
* [100-100]
* [104-104]
* [108-108]
* [112-112]
* [116-116]
* [120-120]
* [124-124]
* [128-128]
* [132-132]
* [136-136]
* [140-140]
*
* Returns 0 if the IE has been found to be invalid in the middle
* somewhere.
*/
static int max_subband_chan(int orig_cur_chan,
int orig_end_channel,
s8 orig_max_power,
u8 **country_ie,
u8 *country_ie_len)
{
u8 *triplets_start = *country_ie;
u8 len_at_triplet = *country_ie_len;
int end_subband_chan = orig_end_channel;
enum ieee80211_band band;

/*
* We'll deal with padding for the caller unless
* its not immediate and we don't process any channels
*/
if (*country_ie_len == 1) {
*country_ie += 1;
*country_ie_len -= 1;
return orig_end_channel;
}

/* Move to the next triplet and then start search */
*country_ie += 3;
*country_ie_len -= 3;

if (orig_cur_chan <= 14)
band = IEEE80211_BAND_2GHZ;
else
band = IEEE80211_BAND_5GHZ;

while (*country_ie_len >= 3) {
int end_channel = 0;
struct ieee80211_country_ie_triplet *triplet =
(struct ieee80211_country_ie_triplet *) *country_ie;
int cur_channel = 0, next_expected_chan;
enum ieee80211_band next_band = IEEE80211_BAND_2GHZ;

/* means last triplet is completely unrelated to this one */
if (triplet->ext.reg_extension_id >=
IEEE80211_COUNTRY_EXTENSION_ID) {
*country_ie -= 3;
*country_ie_len += 3;
break;
}

if (triplet->chans.first_channel == 0) {
*country_ie += 1;
*country_ie_len -= 1;
if (*country_ie_len != 0)
return 0;
break;
}

/* Monitonically increasing channel order */
if (triplet->chans.first_channel <= end_subband_chan)
return 0;

/* 2 GHz */
if (triplet->chans.first_channel <= 14) {
end_channel = triplet->chans.first_channel +
triplet->chans.num_channels - 1;
}
else {
end_channel = triplet->chans.first_channel +
(4 * (triplet->chans.num_channels - 1));
next_band = IEEE80211_BAND_5GHZ;
}

if (band != next_band) {
*country_ie -= 3;
*country_ie_len += 3;
break;
}

if (orig_max_power != triplet->chans.max_power) {
*country_ie -= 3;
*country_ie_len += 3;
break;
}

cur_channel = triplet->chans.first_channel;

/* The key is finding the right next expected channel */
if (band == IEEE80211_BAND_2GHZ)
next_expected_chan = end_subband_chan + 1;
else
next_expected_chan = end_subband_chan + 4;

if (cur_channel != next_expected_chan) {
*country_ie -= 3;
*country_ie_len += 3;
break;
}

end_subband_chan = end_channel;

/* Move to the next one */
*country_ie += 3;
*country_ie_len -= 3;

/*
* Padding needs to be dealt with if we processed
* some channels.
*/
if (*country_ie_len == 1) {
*country_ie += 1;
*country_ie_len -= 1;
break;
}

/* If seen, the IE is invalid */
if (*country_ie_len == 2)
return 0;
}

if (end_subband_chan == orig_end_channel) {
*country_ie = triplets_start;
*country_ie_len = len_at_triplet;
return orig_end_channel;
}

return end_subband_chan;
}

/*
* Converts a country IE to a regulatory domain. A regulatory domain
* structure has a lot of information which the IE doesn't yet have,
Expand Down Expand Up @@ -552,6 +724,19 @@ static struct ieee80211_regdomain *country_ie_2_rd(
continue;
}

/*
* APs can add padding to make length divisible
* by two, required by the spec.
*/
if (triplet->chans.first_channel == 0) {
country_ie++;
country_ie_len--;
/* This is expected to be at the very end only */
if (country_ie_len != 0)
return NULL;
break;
}

/* 2 GHz */
if (triplet->chans.first_channel <= 14)
end_channel = triplet->chans.first_channel +
Expand All @@ -570,6 +755,20 @@ static struct ieee80211_regdomain *country_ie_2_rd(
(4 * (triplet->chans.num_channels - 1));

cur_channel = triplet->chans.first_channel;

/*
* Enhancement for APs that send a triplet for every channel
* or for whatever reason sends triplets with multiple channels
* separated when in fact they should be together.
*/
end_channel = max_subband_chan(cur_channel,
end_channel,
triplet->chans.max_power,
&country_ie,
&country_ie_len);
if (!end_channel)
return NULL;

cur_sub_max_channel = end_channel;

/* Basic sanity check */
Expand Down Expand Up @@ -600,10 +799,13 @@ static struct ieee80211_regdomain *country_ie_2_rd(

last_sub_max_channel = cur_sub_max_channel;

country_ie += 3;
country_ie_len -= 3;
num_rules++;

if (country_ie_len >= 3) {
country_ie += 3;
country_ie_len -= 3;
}

/*
* Note: this is not a IEEE requirement but
* simply a memory requirement
Expand Down Expand Up @@ -646,6 +848,12 @@ static struct ieee80211_regdomain *country_ie_2_rd(
continue;
}

if (triplet->chans.first_channel == 0) {
country_ie++;
country_ie_len--;
break;
}

reg_rule = &rd->reg_rules[i];
freq_range = &reg_rule->freq_range;
power_rule = &reg_rule->power_rule;
Expand All @@ -660,6 +868,12 @@ static struct ieee80211_regdomain *country_ie_2_rd(
end_channel = triplet->chans.first_channel +
(4 * (triplet->chans.num_channels - 1));

end_channel = max_subband_chan(triplet->chans.first_channel,
end_channel,
triplet->chans.max_power,
&country_ie,
&country_ie_len);

/*
* The +10 is since the regulatory domain expects
* the actual band edge, not the center of freq for
Expand All @@ -682,10 +896,13 @@ static struct ieee80211_regdomain *country_ie_2_rd(
power_rule->max_antenna_gain = DBI_TO_MBI(100);
power_rule->max_eirp = DBM_TO_MBM(triplet->chans.max_power);

country_ie += 3;
country_ie_len -= 3;
i++;

if (country_ie_len >= 3) {
country_ie += 3;
country_ie_len -= 3;
}

BUG_ON(i > NL80211_MAX_SUPP_REG_RULES);
}

Expand Down

0 comments on commit f90b62a

Please sign in to comment.