2008-09-04 01:49:46

by Luis R. Rodriguez

[permalink] [raw]
Subject: [RFC] 802.11d Country IE parsing/support

After the new regulatory infrastructure is merged here is how I had
in mind to add support for parsing of Country IEs.

We parse the Country IE, and build a generous regulatory domain
structure out of it. By generous I mean we give it less restrictions,
the reason being the Country IE is not designed in mind with a lot of
regulatory data. It only has power, channels, and inside/outside flag.
That's it.

So what I recommend we do is after we have the regulatory domain built
intersect that with one provided by CRDA. CRDA will always have more up
to date information. That way we can assume 40 MHz badwidth on Country
IE channels but CRDA's bandwidth capabilities will supercede what the
core picked out from the Country IE.

Another very important piece to this is your driver will only roam if
your struct wiphy has dot11MultiDomainCapabilityEnabled set. Otherwise
we cannot expect the driver to be able to roam. The IEEE 802.11 specs
actually indicate we should assume this is disabled unless specified
so it seems reasonable to follow that in cfg80211.

If you decide you are capable of roaming it should mean you have taken
care to review your driver's regulatory data and have a way to let the
driver work in regions outside of the one it is built for.

Below is an initial implementation of intersection for review. I'm sure
I made some mistakes, let me know if you catch them. I haven't tested any
of this.

static bool reg_is_valid_rule(struct ieee80211_reg_rule *rule)
{
struct ieee80211_freq_range *freq_range = &rule->freq_range;
u32 freq_diff;

if (freq_range->start_freq > freq_range->end_freq)
return false;

freq_diff = freq_range->end_freq - freq_range->start_freq;

if (freq_range->max_bandwidth > freq_diff)
return false;

return true;
}

static int reg_rules_intersect(
struct ieee80211_reg_rule *rule1,
struct ieee80211_reg_rule *rule2,
struct ieee80211_reg_rule **intersected_rule)
{
struct ieee80211_freq_range *freq_range1, *freq_range2, *freq_range;
struct ieee80211_power_rule *power_rule1, *power_rule2, *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 = max(freq_range1->start_freq,
freq_range2->start_freq);
freq_range->end_freq = min(freq_range1->end_freq,
freq_range2->end_freq);
freq_range->max_bandwidth = min(freq_range1->max_bandwidth,
freq_range2->max_bandwidth);

freq_diff = freq_range->end_freq - freq_range->start_freq;
if (freq_range->max_bandwidth > freq_diff)
freq_range->max_bandwidth = freq_diff;

power_rule->max_eirp = min(power_rule1->max_eirp,
power_rule2->max_eirp);
power_rule->max_eirp = min(power_rule1->max_eirp,
power_rule2->max_eirp);

(*intersected_rule)->flags = (rule1->flags | rule2->flags);

if (!reg_is_valid_rule(*intersected_rule))
return -EINVAL;

return 0;
}

static int regdom_intersect(
struct ieee80211_regdomain *rd1,
struct ieee80211_regdomain *rd2,
struct ieee80211_regdomain **rd)
{
int r, size_of_regd;
unsigned int x, y;
unsigned int num_rules = 0, rule_idx = 0;
struct ieee80211_reg_rule *rule1, *rule2, *intersected_rule;

*rd = NULL;

if (!rd1 || !rd2)
return -EINVAL;

/* First we get a count of the rules we'll need, then we actually
* build them. This is to so we can kzalloc() and kfree() a
* regdomain once */

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++;
}
}

if (!num_rules)
return -ENODATA;

size_of_regd = sizeof(struct ieee80211_regdomain) +
(num_rules * sizeof(struct ieee80211_reg_rule));

*rd = kzalloc(size_of_regd, GFP_KERNEL);
if (!*rd)
return -ENOMEM;

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];
r = reg_rules_intersect(rule1, rule2,
&intersected_rule);
if (r)
continue;
memcpy(&(*rd)->reg_rules[rule_idx],
intersected_rule,
sizeof(intersected_rule));
rule_idx++;
}
}

BUG_ON(rule_idx != num_rules);

/* Indicates this regdomain is the product of an intersection */
(*rd)->alpha2[0] = '9';
(*rd)->alpha2[1] = '9';
}



2008-09-04 08:23:30

by Helmut Schaa

[permalink] [raw]
Subject: Re: [RFC] 802.11d Country IE parsing/support

Am Donnerstag, 4. September 2008 schrieb Luis R. Rodriguez:
> static int reg_rules_intersect(
> struct ieee80211_reg_rule *rule1,
> struct ieee80211_reg_rule *rule2,
> struct ieee80211_reg_rule **intersected_rule)
> {
> struct ieee80211_freq_range *freq_range1, *freq_range2, *freq_range;
> struct ieee80211_power_rule *power_rule1, *power_rule2, *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 = max(freq_range1->start_freq,
> freq_range2->start_freq);
> freq_range->end_freq = min(freq_range1->end_freq,
> freq_range2->end_freq);
> freq_range->max_bandwidth = min(freq_range1->max_bandwidth,
> freq_range2->max_bandwidth);
>
> freq_diff = freq_range->end_freq - freq_range->start_freq;
> if (freq_range->max_bandwidth > freq_diff)
> freq_range->max_bandwidth = freq_diff;
>
> power_rule->max_eirp = min(power_rule1->max_eirp,
> power_rule2->max_eirp);
> power_rule->max_eirp = min(power_rule1->max_eirp,
> power_rule2->max_eirp);

max_eirp should be max_antenna_gain in the second block, right?

> (*intersected_rule)->flags = (rule1->flags | rule2->flags);

Wouldn't (rule1->flags & rule2->flags) be more feasible?

> if (!reg_is_valid_rule(*intersected_rule))
> return -EINVAL;
>
> return 0;
> }
>
> static int regdom_intersect(
> struct ieee80211_regdomain *rd1,
> struct ieee80211_regdomain *rd2,
> struct ieee80211_regdomain **rd)
> {
> int r, size_of_regd;
> unsigned int x, y;
> unsigned int num_rules = 0, rule_idx = 0;
> struct ieee80211_reg_rule *rule1, *rule2, *intersected_rule;
>
> *rd = NULL;
>
> if (!rd1 || !rd2)
> return -EINVAL;
>
> /* First we get a count of the rules we'll need, then we actually
> * build them. This is to so we can kzalloc() and kfree() a
> * regdomain once */
>
> 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))

Hmm, &intersected_rule is uninitialized here and reg_rules_intersect does not
allocate anything.

> num_rules++;
> }
> }
>
> if (!num_rules)
> return -ENODATA;
>
> size_of_regd = sizeof(struct ieee80211_regdomain) +
> (num_rules * sizeof(struct ieee80211_reg_rule));
>
> *rd = kzalloc(size_of_regd, GFP_KERNEL);
> if (!*rd)
> return -ENOMEM;
>
> 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];
> r = reg_rules_intersect(rule1, rule2,
> &intersected_rule);

Same here.

> if (r)
> continue;
> memcpy(&(*rd)->reg_rules[rule_idx],
> intersected_rule,
> sizeof(intersected_rule));

sizeof(*intersected_rule)?

Helmut

2008-09-04 18:15:22

by Luis R. Rodriguez

[permalink] [raw]
Subject: Re: [RFC] 802.11d Country IE parsing/support

On Thu, Sep 04, 2008 at 01:23:10AM -0700, Helmut Schaa wrote:
> Am Donnerstag, 4. September 2008 schrieb Luis R. Rodriguez:
> > static int reg_rules_intersect(
> > struct ieee80211_reg_rule *rule1,
> > struct ieee80211_reg_rule *rule2,
> > struct ieee80211_reg_rule **intersected_rule)
> > {
> > struct ieee80211_freq_range *freq_range1, *freq_range2, *freq_range;
> > struct ieee80211_power_rule *power_rule1, *power_rule2, *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 = max(freq_range1->start_freq,
> > freq_range2->start_freq);
> > freq_range->end_freq = min(freq_range1->end_freq,
> > freq_range2->end_freq);
> > freq_range->max_bandwidth = min(freq_range1->max_bandwidth,
> > freq_range2->max_bandwidth);
> >
> > freq_diff = freq_range->end_freq - freq_range->start_freq;
> > if (freq_range->max_bandwidth > freq_diff)
> > freq_range->max_bandwidth = freq_diff;
> >
> > power_rule->max_eirp = min(power_rule1->max_eirp,
> > power_rule2->max_eirp);
> > power_rule->max_eirp = min(power_rule1->max_eirp,
> > power_rule2->max_eirp);
>
> max_eirp should be max_antenna_gain in the second block, right?

Yes, thanks.

>
> > (*intersected_rule)->flags = (rule1->flags | rule2->flags);
>
> Wouldn't (rule1->flags & rule2->flags) be more feasible?

Well the flags are all negatives, so like:

* Thou shall not kill = 1 << 1
* Thou shall not send "fix" patches after merge window = 1 << 2

And you have one rule with 1 << 1 and another with 1 << 2, you'd want
both so you use "or" to be more restrictive. Using "&" would get you
the negatives they both have in common.

>
> > if (!reg_is_valid_rule(*intersected_rule))
> > return -EINVAL;
> >
> > return 0;
> > }
> >
> > static int regdom_intersect(
> > struct ieee80211_regdomain *rd1,
> > struct ieee80211_regdomain *rd2,
> > struct ieee80211_regdomain **rd)
> > {
> > int r, size_of_regd;
> > unsigned int x, y;
> > unsigned int num_rules = 0, rule_idx = 0;
> > struct ieee80211_reg_rule *rule1, *rule2, *intersected_rule;
> >
> > *rd = NULL;
> >
> > if (!rd1 || !rd2)
> > return -EINVAL;
> >
> > /* First we get a count of the rules we'll need, then we actually
> > * build them. This is to so we can kzalloc() and kfree() a
> > * regdomain once */
> >
> > 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))
>
> Hmm, &intersected_rule is uninitialized here and reg_rules_intersect does not
> allocate anything.

Ah yes, since its just a temporary rule we will disregard we can use the
stack then for this, will fix, thanks.

>
> > num_rules++;
> > }
> > }
> >
> > if (!num_rules)
> > return -ENODATA;
> >
> > size_of_regd = sizeof(struct ieee80211_regdomain) +
> > (num_rules * sizeof(struct ieee80211_reg_rule));
> >
> > *rd = kzalloc(size_of_regd, GFP_KERNEL);
> > if (!*rd)
> > return -ENOMEM;
> >
> > 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];
> > r = reg_rules_intersect(rule1, rule2,
> > &intersected_rule);
>
> Same here.

Thanks, we can use the stack.

>
> > if (r)
> > continue;
> > memcpy(&(*rd)->reg_rules[rule_idx],
> > intersected_rule,
> > sizeof(intersected_rule));
>
> sizeof(*intersected_rule)?

Yes, thanks for your review!

Luis