Regulatory fixups:

o add missing channel flags for ECM, indoor, and outdoor constraints
o use HT capabilities to short-circuit HT20/HT40 channel construction
o rewrite 1/2 and 1/4 width channel handling yet again; previously
  we assumed there was a full-width version of the channel in the
  calibration table but that's not always true (e.g. for the Public
  Safety Band), now we first check the calibration table for the
  exact channel we want then fall back to the heuristics we used before
o fix HT channel construction; wasn't adjusting band edges for HT40
  channel bandwidth requirements
This commit is contained in:
Sam Leffler 2009-02-07 01:12:51 +00:00
parent 1447dcd435
commit 47ee60ebbe
3 changed files with 142 additions and 68 deletions

View File

@ -99,10 +99,6 @@
#define IEEE80211_FIXED_RATE_NONE 0xff #define IEEE80211_FIXED_RATE_NONE 0xff
#endif #endif
#define REQ_ECM 0x01000000 /* enable if ECM set */
#define REQ_OUTDOOR 0x02000000 /* enable for outdoor operation */
#define REQ_FLAGS 0xff000000 /* private flags, don't pass to os */
/* XXX need these publicly defined or similar */ /* XXX need these publicly defined or similar */
#ifndef IEEE80211_NODE_AUTH #ifndef IEEE80211_NODE_AUTH
#define IEEE80211_NODE_AUTH 0x0001 /* authorized for data */ #define IEEE80211_NODE_AUTH 0x0001 /* authorized for data */
@ -1802,6 +1798,57 @@ chanfind(const struct ieee80211_channel chans[], int nchans, int flags)
return 0; return 0;
} }
/*
* Check channel compatibility.
*/
static int
checkchan(const struct ieee80211req_chaninfo *avail, int freq, int flags)
{
flags &= ~REQ_FLAGS;
/*
* Check if exact channel is in the calibration table;
* everything below is to deal with channels that we
* want to include but that are not explicitly listed.
*/
if (flags & IEEE80211_CHAN_HT40) {
/* NB: we use an HT40 channel center that matches HT20 */
flags = (flags &~ IEEE80211_CHAN_HT40) | IEEE80211_CHAN_HT20;
}
if (chanlookup(avail->ic_chans, avail->ic_nchans, freq, flags) != NULL)
return 1;
if (flags & IEEE80211_CHAN_GSM) {
/*
* XXX GSM frequency mapping is handled in the kernel
* so we cannot find them in the calibration table;
* just accept the channel and the kernel will reject
* the channel list if it's wrong.
*/
return 1;
}
/*
* If this is a 1/2 or 1/4 width channel allow it if a full
* width channel is present for this frequency, and the device
* supports fractional channels on this band. This is a hack
* that avoids bloating the calibration table; it may be better
* by per-band attributes though (we are effectively calculating
* this attribute by scanning the channel list ourself).
*/
if ((flags & (IEEE80211_CHAN_HALF | IEEE80211_CHAN_QUARTER)) == 0)
return 0;
if (chanlookup(avail->ic_chans, avail->ic_nchans, freq,
flags &~ (IEEE80211_CHAN_HALF | IEEE80211_CHAN_QUARTER)) == NULL)
return 0;
if (flags & IEEE80211_CHAN_HALF) {
return chanfind(avail->ic_chans, avail->ic_nchans,
IEEE80211_CHAN_HALF |
(flags & (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_5GHZ)));
} else {
return chanfind(avail->ic_chans, avail->ic_nchans,
IEEE80211_CHAN_QUARTER |
(flags & (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_5GHZ)));
}
}
static void static void
regdomain_addchans(struct ieee80211req_chaninfo *ci, regdomain_addchans(struct ieee80211req_chaninfo *ci,
const netband_head *bands, const netband_head *bands,
@ -1812,15 +1859,12 @@ regdomain_addchans(struct ieee80211req_chaninfo *ci,
const struct netband *nb; const struct netband *nb;
const struct freqband *b; const struct freqband *b;
struct ieee80211_channel *c, *prev; struct ieee80211_channel *c, *prev;
int freq, channelSep, hasHalfChans, hasQuarterChans; int freq, hi_adj, lo_adj, channelSep;
uint32_t flags;
hi_adj = (chanFlags & IEEE80211_CHAN_HT40U) ? -20 : 0;
lo_adj = (chanFlags & IEEE80211_CHAN_HT40D) ? 20 : 0;
channelSep = (chanFlags & IEEE80211_CHAN_2GHZ) ? 0 : 40; channelSep = (chanFlags & IEEE80211_CHAN_2GHZ) ? 0 : 40;
hasHalfChans = chanfind(avail->ic_chans, avail->ic_nchans,
IEEE80211_CHAN_HALF |
(chanFlags & (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_5GHZ)));
hasQuarterChans = chanfind(avail->ic_chans, avail->ic_nchans,
IEEE80211_CHAN_QUARTER |
(chanFlags & (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_5GHZ)));
LIST_FOREACH(nb, bands, next) { LIST_FOREACH(nb, bands, next) {
b = nb->band; b = nb->band;
if (verbose) { if (verbose) {
@ -1831,63 +1875,80 @@ regdomain_addchans(struct ieee80211req_chaninfo *ci,
putchar('\n'); putchar('\n');
} }
prev = NULL; prev = NULL;
for (freq = b->freqStart; freq <= b->freqEnd; freq += b->chanSep) { for (freq = b->freqStart + lo_adj;
uint32_t flags = nb->flags | b->flags; freq <= b->freqEnd + hi_adj; freq += b->chanSep) {
/* check if device can operate on this frequency */
/* /*
* XXX GSM frequency mapping is handled in the kernel * Construct flags for the new channel. We take
* so we cannot find them in the calibration table; * the attributes from the band descriptions except
* just construct the list and the kernel will reject * for HT40 which is enabled generically (i.e. +/-
* if it's wrong. * extension channel) in the band description and
* then constrained according by channel separation.
*/ */
if (chanlookup(avail->ic_chans, avail->ic_nchans, freq, chanFlags) == NULL && flags = nb->flags | b->flags;
(flags & IEEE80211_CHAN_GSM) == 0) { if (flags & IEEE80211_CHAN_HT) {
/*
* HT channels are generated specially; we're
* called to add HT20, HT40+, and HT40- chan's
* so we need to expand only band specs for
* the HT channel type being added.
*/
if ((chanFlags & IEEE80211_CHAN_HT20) &&
(flags & IEEE80211_CHAN_HT20) == 0) {
if (verbose)
printf("%u: skip, not an "
"HT20 channel\n", freq);
continue;
}
if ((chanFlags & IEEE80211_CHAN_HT40) &&
(flags & IEEE80211_CHAN_HT40) == 0) {
if (verbose)
printf("%u: skip, not an "
"HT40 channel\n", freq);
continue;
}
/*
* DFS and HT40 don't mix. This should be
* expressed in the regdomain database but
* just in case enforce it here.
*/
if ((chanFlags & IEEE80211_CHAN_HT40) &&
(flags & IEEE80211_CHAN_DFS)) {
if (verbose)
printf("%u: skip, HT40+DFS "
"not permitted\n", freq);
continue;
}
/* NB: HT attribute comes from caller */
flags &= ~IEEE80211_CHAN_HT;
flags |= chanFlags & IEEE80211_CHAN_HT;
}
/*
* Check if device can operate on this frequency.
*/
if (!checkchan(avail, freq, flags)) {
if (verbose) { if (verbose) {
printf("%u: skip, ", freq); printf("%u: skip, ", freq);
printb("flags", chanFlags, printb("flags", flags,
IEEE80211_CHAN_BITS); IEEE80211_CHAN_BITS);
printf(" not available\n"); printf(" not available\n");
} }
continue; continue;
} }
if ((flags & IEEE80211_CHAN_HALF) && !hasHalfChans) {
if (verbose)
printf("%u: skip, device does not "
"support half-rate channel\n",
freq);
continue;
}
if ((flags & IEEE80211_CHAN_QUARTER) &&
!hasQuarterChans) {
if (verbose)
printf("%u: skip, device does not "
"support quarter-rate channel\n",
freq);
continue;
}
if ((flags & IEEE80211_CHAN_HT20) &&
(chanFlags & IEEE80211_CHAN_HT20) == 0) {
if (verbose)
printf("%u: skip, device does not "
"support HT20 operation\n", freq);
continue;
}
if ((flags & IEEE80211_CHAN_HT40) &&
(chanFlags & IEEE80211_CHAN_HT40) == 0) {
if (verbose)
printf("%u: skip, device does not "
"support HT40 operation\n", freq);
continue;
}
if ((flags & REQ_ECM) && !reg->ecm) { if ((flags & REQ_ECM) && !reg->ecm) {
if (verbose) if (verbose)
printf("%u: skip, ECM channel\n", freq); printf("%u: skip, ECM channel\n", freq);
continue; continue;
} }
if ((flags & REQ_INDOOR) && reg->location == 'O') {
if (verbose)
printf("%u: skip, indoor channel\n",
freq);
continue;
}
if ((flags & REQ_OUTDOOR) && reg->location == 'I') { if ((flags & REQ_OUTDOOR) && reg->location == 'I') {
if (verbose) if (verbose)
printf("%u: skip, outdoor channel\n", freq); printf("%u: skip, outdoor channel\n",
freq);
continue; continue;
} }
if ((flags & IEEE80211_CHAN_HT40) && if ((flags & IEEE80211_CHAN_HT40) &&
@ -1907,8 +1968,7 @@ regdomain_addchans(struct ieee80211req_chaninfo *ci,
c = &ci->ic_chans[ci->ic_nchans++]; c = &ci->ic_chans[ci->ic_nchans++];
memset(c, 0, sizeof(*c)); memset(c, 0, sizeof(*c));
c->ic_freq = freq; c->ic_freq = freq;
c->ic_flags = chanFlags | c->ic_flags = flags;
(flags &~ (REQ_FLAGS | IEEE80211_CHAN_HT40));
if (c->ic_flags & IEEE80211_CHAN_DFS) if (c->ic_flags & IEEE80211_CHAN_DFS)
c->ic_maxregpower = nb->maxPowerDFS; c->ic_maxregpower = nb->maxPowerDFS;
else else
@ -1973,27 +2033,31 @@ regdomain_makechannels(
if (!LIST_EMPTY(&rd->bands_11a)) if (!LIST_EMPTY(&rd->bands_11a))
regdomain_addchans(ci, &rd->bands_11a, reg, regdomain_addchans(ci, &rd->bands_11a, reg,
IEEE80211_CHAN_A, &dc->dc_chaninfo); IEEE80211_CHAN_A, &dc->dc_chaninfo);
if (!LIST_EMPTY(&rd->bands_11na)) { if (!LIST_EMPTY(&rd->bands_11na) && dc->dc_htcaps != 0) {
regdomain_addchans(ci, &rd->bands_11na, reg, regdomain_addchans(ci, &rd->bands_11na, reg,
IEEE80211_CHAN_A | IEEE80211_CHAN_HT20, IEEE80211_CHAN_A | IEEE80211_CHAN_HT20,
&dc->dc_chaninfo); &dc->dc_chaninfo);
regdomain_addchans(ci, &rd->bands_11na, reg, if (dc->dc_htcaps & IEEE80211_HTCAP_CHWIDTH40) {
IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U, regdomain_addchans(ci, &rd->bands_11na, reg,
&dc->dc_chaninfo); IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U,
regdomain_addchans(ci, &rd->bands_11na, reg, &dc->dc_chaninfo);
IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D, regdomain_addchans(ci, &rd->bands_11na, reg,
&dc->dc_chaninfo); IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D,
&dc->dc_chaninfo);
}
} }
if (!LIST_EMPTY(&rd->bands_11ng)) { if (!LIST_EMPTY(&rd->bands_11ng) && dc->dc_htcaps != 0) {
regdomain_addchans(ci, &rd->bands_11ng, reg, regdomain_addchans(ci, &rd->bands_11ng, reg,
IEEE80211_CHAN_G | IEEE80211_CHAN_HT20, IEEE80211_CHAN_G | IEEE80211_CHAN_HT20,
&dc->dc_chaninfo); &dc->dc_chaninfo);
regdomain_addchans(ci, &rd->bands_11ng, reg, if (dc->dc_htcaps & IEEE80211_HTCAP_CHWIDTH40) {
IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U, regdomain_addchans(ci, &rd->bands_11ng, reg,
&dc->dc_chaninfo); IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U,
regdomain_addchans(ci, &rd->bands_11ng, reg, &dc->dc_chaninfo);
IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D, regdomain_addchans(ci, &rd->bands_11ng, reg,
&dc->dc_chaninfo); IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D,
&dc->dc_chaninfo);
}
} }
qsort(ci->ic_chans, ci->ic_nchans, sizeof(ci->ic_chans[0]), qsort(ci->ic_chans, ci->ic_nchans, sizeof(ci->ic_chans[0]),
regdomain_sort); regdomain_sort);

View File

@ -208,6 +208,9 @@ decode_flag(struct mystate *mt, const char *p, int len)
FLAG(IEEE80211_CHAN_108A), FLAG(IEEE80211_CHAN_108A),
FLAG(IEEE80211_CHAN_108G), FLAG(IEEE80211_CHAN_108G),
#undef FLAG #undef FLAG
{ "ECM", 3, REQ_ECM },
{ "INDOOR", 6, REQ_INDOOR },
{ "OUTDOOR", 7, REQ_OUTDOOR },
}; };
int i; int i;

View File

@ -45,6 +45,13 @@ struct freqband {
LIST_ENTRY(freqband) next; LIST_ENTRY(freqband) next;
}; };
/* private flags, don't pass to os */
#define REQ_ECM 0x1 /* enable if ECM set */
#define REQ_INDOOR 0x2 /* enable only for indoor operation */
#define REQ_OUTDOOR 0x4 /* enable only for outdoor operation */
#define REQ_FLAGS (REQ_ECM|REQ_INDOOR|REQ_OUTDOOR)
struct netband { struct netband {
const struct freqband *band; /* channel list description */ const struct freqband *band; /* channel list description */
uint8_t maxPower; /* regulatory cap on tx power (dBm) */ uint8_t maxPower; /* regulatory cap on tx power (dBm) */