Index: sys/net80211/ieee80211_regdomain.c =================================================================== --- sys/net80211/ieee80211_regdomain.c +++ sys/net80211/ieee80211_regdomain.c @@ -64,17 +64,112 @@ return 0; /* accept anything */ } +/* FCC regdomain limitations. Extracted from regdomain.xml */ +static const struct freqband { + uint16_t freq_min; + uint16_t freq_max; + int8_t maxregpower; + uint32_t flags; +} fcc_bands[] = { + { 2412, 2462, 30, IEEE80211_CHAN_B }, + { 2412, 2462, 30, IEEE80211_CHAN_G }, + { 2412, 2462, 30, IEEE80211_CHAN_G | IEEE80211_CHAN_HT20 }, + { 2432, 2462, 30, IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D }, + { 2412, 2442, 30, IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U }, + { 5180, 5240, 17, IEEE80211_CHAN_A }, + { 5180, 5240, 17, IEEE80211_CHAN_A | IEEE80211_CHAN_HT20 }, + { 5200, 5240, 17, IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D }, + { 5180, 5220, 17, IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U }, + { 5745, 5805, 23, IEEE80211_CHAN_A }, + { 5745, 5805, 23, IEEE80211_CHAN_A | IEEE80211_CHAN_HT20 }, + { 5765, 5805, 23, IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D }, + { 5745, 5785, 23, IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U }, + { 5825, 5825, 23, IEEE80211_CHAN_A | IEEE80211_CHAN_PASSIVE }, + { 0, 0, 0, 0 } +}; + +static void +removechan(struct ieee80211com *ic, int idx) +{ + struct ieee80211_channel *chan = &ic->ic_channels[0]; + + KASSERT(idx < ic->ic_nchans, ("wrong index %d (%d)\n", idx, + ic->ic_nchans)); + + ic->ic_nchans--; + memcpy(&chan[idx], &chan[ic->ic_nchans], sizeof(*chan)); + memset(&chan[ic->ic_nchans], 0, sizeof(*chan)); +} + +static int +checkchan(struct ieee80211_channel *c, const struct freqband fbands[]) +{ + const struct freqband *fb; + uint32_t flags; + int i; + + flags = c->ic_flags & IEEE80211_CHAN_ALLTURBO; + /* brute force search */ + for (i = 0; fbands[i].freq_max != 0; i++) { + fb = &fbands[i]; + + if (flags == (fb->flags & IEEE80211_CHAN_ALLTURBO) && + fb->freq_min <= c->ic_freq && + fb->freq_max >= c->ic_freq) { + if (fb->maxregpower < c->ic_maxregpower) { + c->ic_maxregpower = fb->maxregpower; + c->ic_maxpower = fb->maxregpower * 2; + + if (c->ic_minpower > c->ic_maxpower) + c->ic_minpower = c->ic_maxpower; + } + /* add PASSIVE, DFS etc flags. */ + c->ic_flags |= (fb->flags & ~IEEE80211_CHAN_ALLTURBO); + return (1); + } + } + + return (0); +} + +static void +channel_plan_init(struct ieee80211com *ic, const struct freqband fbands[]) +{ + int i, found; + + for (i = 0; i < ic->ic_nchans;) { + found = checkchan(&ic->ic_channels[i], fbands); + if (!found) + removechan(ic, i); + else + i++; + } + + ieee80211_chan_init(ic); +} + +static void +regdomain_init(struct ieee80211com *ic) +{ + struct ieee80211_regdomain *rd = &ic->ic_regdomain; + + /* init by default to FCC/US */ + rd->regdomain = SKU_FCC; + rd->country = CTRY_UNITED_STATES; + rd->location = ' '; /* both */ + rd->isocc[0] = 'U'; + rd->isocc[1] = 'S'; + + channel_plan_init(ic, fcc_bands); +} + void ieee80211_regdomain_attach(struct ieee80211com *ic) { if (ic->ic_regdomain.regdomain == 0 && - ic->ic_regdomain.country == CTRY_DEFAULT) { - ic->ic_regdomain.country = CTRY_UNITED_STATES; /* XXX */ - ic->ic_regdomain.location = ' '; /* both */ - ic->ic_regdomain.isocc[0] = 'U'; /* XXX */ - ic->ic_regdomain.isocc[1] = 'S'; /* XXX */ - /* NB: driver calls ieee80211_init_channels or similar */ - } + ic->ic_regdomain.country == CTRY_DEFAULT) + regdomain_init(ic); + ic->ic_getradiocaps = null_getradiocaps; ic->ic_setregdomain = null_setregdomain; }