Index: head/sys/net80211/ieee80211.c =================================================================== --- head/sys/net80211/ieee80211.c +++ head/sys/net80211/ieee80211.c @@ -970,6 +970,261 @@ } } +static __inline void +set_extchan(struct ieee80211_channel *c) +{ + + /* + * IEEE Std 802.11-2012, page 1738, subclause 20.3.15.4: + * "the secondary channel number shall be 'N + [1,-1] * 4' + */ + if (c->ic_flags & IEEE80211_CHAN_HT40U) + c->ic_extieee = c->ic_ieee + 4; + else if (c->ic_flags & IEEE80211_CHAN_HT40D) + c->ic_extieee = c->ic_ieee - 4; + else + c->ic_extieee = 0; +} + +static int +addchan(struct ieee80211_channel chans[], int maxchans, int *nchans, + uint8_t ieee, uint16_t freq, int8_t maxregpower, uint32_t flags) +{ + struct ieee80211_channel *c; + + if (*nchans >= maxchans) + return (ENOBUFS); + + c = &chans[(*nchans)++]; + c->ic_ieee = ieee; + c->ic_freq = freq != 0 ? freq : ieee80211_ieee2mhz(ieee, flags); + c->ic_maxregpower = maxregpower; + c->ic_maxpower = 2 * maxregpower; + c->ic_flags = flags; + set_extchan(c); + + return (0); +} + +static int +copychan_prev(struct ieee80211_channel chans[], int maxchans, int *nchans, + uint32_t flags) +{ + struct ieee80211_channel *c; + + KASSERT(*nchans > 0, ("channel list is empty\n")); + + if (*nchans >= maxchans) + return (ENOBUFS); + + c = &chans[(*nchans)++]; + c[0] = c[-1]; + c->ic_flags = flags; + set_extchan(c); + + return (0); +} + +static void +getflags_2ghz(const uint8_t bands[], uint32_t flags[], int ht40) +{ + int nmodes; + + nmodes = 0; + if (isset(bands, IEEE80211_MODE_11B)) + flags[nmodes++] = IEEE80211_CHAN_B; + if (isset(bands, IEEE80211_MODE_11G)) + flags[nmodes++] = IEEE80211_CHAN_G; + if (isset(bands, IEEE80211_MODE_11NG)) + flags[nmodes++] = IEEE80211_CHAN_G | IEEE80211_CHAN_HT20; + if (ht40) { + flags[nmodes++] = IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U; + flags[nmodes++] = IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D; + } + flags[nmodes] = 0; +} + +static void +getflags_5ghz(const uint8_t bands[], uint32_t flags[], int ht40) +{ + int nmodes; + + nmodes = 0; + if (isset(bands, IEEE80211_MODE_11A)) + flags[nmodes++] = IEEE80211_CHAN_A; + if (isset(bands, IEEE80211_MODE_11NA)) + flags[nmodes++] = IEEE80211_CHAN_A | IEEE80211_CHAN_HT20; + if (ht40) { + flags[nmodes++] = IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U; + flags[nmodes++] = IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D; + } + flags[nmodes] = 0; +} + +static void +getflags(const uint8_t bands[], uint32_t flags[], int ht40) +{ + + flags[0] = 0; + if (isset(bands, IEEE80211_MODE_11A) || + isset(bands, IEEE80211_MODE_11NA)) { + if (isset(bands, IEEE80211_MODE_11B) || + isset(bands, IEEE80211_MODE_11G) || + isset(bands, IEEE80211_MODE_11NG)) + return; + + getflags_5ghz(bands, flags, ht40); + } else + getflags_2ghz(bands, flags, ht40); +} + +/* + * Add one 20 MHz channel into specified channel list. + */ +int +ieee80211_add_channel(struct ieee80211_channel chans[], int maxchans, + int *nchans, uint8_t ieee, uint16_t freq, int8_t maxregpower, + uint32_t chan_flags, const uint8_t bands[]) +{ + uint32_t flags[IEEE80211_MODE_MAX]; + int i, error; + + getflags(bands, flags, 0); + KASSERT(flags[0] != 0, ("%s: no correct mode provided\n", __func__)); + + error = addchan(chans, maxchans, nchans, ieee, freq, maxregpower, + flags[0] | chan_flags); + for (i = 1; flags[i] != 0 && error == 0; i++) { + error = copychan_prev(chans, maxchans, nchans, + flags[i] | chan_flags); + } + + return (error); +} + +static struct ieee80211_channel * +findchannel(struct ieee80211_channel chans[], int nchans, uint16_t freq, + uint32_t flags) +{ + struct ieee80211_channel *c; + int i; + + flags &= IEEE80211_CHAN_ALLTURBO; + /* brute force search */ + for (i = 0; i < nchans; i++) { + c = &chans[i]; + if (c->ic_freq == freq && + (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags) + return c; + } + return NULL; +} + +/* + * Add 40 MHz channel pair into specified channel list. + */ +int +ieee80211_add_channel_ht40(struct ieee80211_channel chans[], int maxchans, + int *nchans, uint8_t ieee, int8_t maxregpower, uint32_t flags) +{ + struct ieee80211_channel *cent, *extc; + uint16_t freq; + int error; + + freq = ieee80211_ieee2mhz(ieee, flags); + + /* + * Each entry defines an HT40 channel pair; find the + * center channel, then the extension channel above. + */ + flags |= IEEE80211_CHAN_HT20; + cent = findchannel(chans, *nchans, freq, flags); + if (cent == NULL) + return (EINVAL); + + extc = findchannel(chans, *nchans, freq + 20, flags); + if (extc == NULL) + return (ENOENT); + + flags &= ~IEEE80211_CHAN_HT; + error = addchan(chans, maxchans, nchans, cent->ic_ieee, cent->ic_freq, + maxregpower, flags | IEEE80211_CHAN_HT40U); + if (error != 0) + return (error); + + error = addchan(chans, maxchans, nchans, extc->ic_ieee, extc->ic_freq, + maxregpower, flags | IEEE80211_CHAN_HT40D); + + return (error); +} + +/* + * Adds channels into specified channel list (ieee[] array must be sorted). + * Channels are already sorted. + */ +static int +add_chanlist(struct ieee80211_channel chans[], int maxchans, int *nchans, + const uint8_t ieee[], int nieee, uint32_t flags[]) +{ + uint16_t freq; + int i, j, error; + + for (i = 0; i < nieee; i++) { + freq = ieee80211_ieee2mhz(ieee[i], flags[0]); + for (j = 0; flags[j] != 0; j++) { + if (flags[j] & IEEE80211_CHAN_HT40D) + if (i == 0 || ieee[i] < ieee[0] + 4 || + freq - 20 != + ieee80211_ieee2mhz(ieee[i] - 4, flags[j])) + continue; + if (flags[j] & IEEE80211_CHAN_HT40U) + if (i == nieee - 1 || + ieee[i] + 4 > ieee[nieee - 1] || + freq + 20 != + ieee80211_ieee2mhz(ieee[i] + 4, flags[j])) + continue; + + if (j == 0) { + error = addchan(chans, maxchans, nchans, + ieee[i], freq, 0, flags[j]); + } else { + error = copychan_prev(chans, maxchans, nchans, + flags[j]); + } + if (error != 0) + return (error); + } + } + + return (error); +} + +int +ieee80211_add_channel_list_2ghz(struct ieee80211_channel chans[], int maxchans, + int *nchans, const uint8_t ieee[], int nieee, const uint8_t bands[], + int ht40) +{ + uint32_t flags[IEEE80211_MODE_MAX]; + + getflags_2ghz(bands, flags, ht40); + KASSERT(flags[0] != 0, ("%s: no correct mode provided\n", __func__)); + + return (add_chanlist(chans, maxchans, nchans, ieee, nieee, flags)); +} + +int +ieee80211_add_channel_list_5ghz(struct ieee80211_channel chans[], int maxchans, + int *nchans, const uint8_t ieee[], int nieee, const uint8_t bands[], + int ht40) +{ + uint32_t flags[IEEE80211_MODE_MAX]; + + getflags_5ghz(bands, flags, ht40); + KASSERT(flags[0] != 0, ("%s: no correct mode provided\n", __func__)); + + return (add_chanlist(chans, maxchans, nchans, ieee, nieee, flags)); +} + /* * Locate a channel given a frequency+flags. We cache * the previous lookup to optimize switching between two @@ -979,7 +1234,6 @@ ieee80211_find_channel(struct ieee80211com *ic, int freq, int flags) { struct ieee80211_channel *c; - int i; flags &= IEEE80211_CHAN_ALLTURBO; c = ic->ic_prevchan; @@ -987,13 +1241,7 @@ (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags) return c; /* brute force search */ - for (i = 0; i < ic->ic_nchans; i++) { - c = &ic->ic_channels[i]; - if (c->ic_freq == freq && - (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags) - return c; - } - return NULL; + return (findchannel(ic->ic_channels, ic->ic_nchans, freq, flags)); } /* Index: head/sys/net80211/ieee80211_regdomain.c =================================================================== --- head/sys/net80211/ieee80211_regdomain.c +++ head/sys/net80211/ieee80211_regdomain.c @@ -98,22 +98,14 @@ { } -static void -addchan(struct ieee80211com *ic, int ieee, int flags) -{ - struct ieee80211_channel *c; - - c = &ic->ic_channels[ic->ic_nchans++]; - c->ic_freq = ieee80211_ieee2mhz(ieee, flags); - c->ic_ieee = ieee; - c->ic_flags = flags; - if (flags & IEEE80211_CHAN_HT40U) - c->ic_extieee = ieee + 4; - else if (flags & IEEE80211_CHAN_HT40D) - c->ic_extieee = ieee - 4; - else - c->ic_extieee = 0; -} +static const uint8_t def_chan_2ghz[] = + { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }; +static const uint8_t def_chan_5ghz_band1[] = + { 36, 40, 44, 48, 52, 56, 60, 64 }; +static const uint8_t def_chan_5ghz_band2[] = + { 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140 }; +static const uint8_t def_chan_5ghz_band3[] = + { 149, 153, 157, 161 }; /* * Setup the channel list for the specified regulatory domain, @@ -125,82 +117,34 @@ ieee80211_init_channels(struct ieee80211com *ic, const struct ieee80211_regdomain *rd, const uint8_t bands[]) { - int i; + struct ieee80211_channel *chans = ic->ic_channels; + int *nchans = &ic->ic_nchans; + int ht40; /* XXX just do something for now */ - ic->ic_nchans = 0; + ht40 = !!(ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40); + *nchans = 0; if (isset(bands, IEEE80211_MODE_11B) || isset(bands, IEEE80211_MODE_11G) || isset(bands, IEEE80211_MODE_11NG)) { - int maxchan = 11; - if (rd != NULL && rd->ecm) - maxchan = 14; - for (i = 1; i <= maxchan; i++) { - if (isset(bands, IEEE80211_MODE_11B)) - addchan(ic, i, IEEE80211_CHAN_B); - if (isset(bands, IEEE80211_MODE_11G)) - addchan(ic, i, IEEE80211_CHAN_G); - if (isset(bands, IEEE80211_MODE_11NG)) { - addchan(ic, i, - IEEE80211_CHAN_G | IEEE80211_CHAN_HT20); - } - if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) == 0) - continue; - if (i <= 7) { - addchan(ic, i, - IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U); - addchan(ic, i + 4, - IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D); - } - } + int nchan = nitems(def_chan_2ghz); + if (!(rd != NULL && rd->ecm)) + nchan -= 3; + + ieee80211_add_channel_list_2ghz(chans, IEEE80211_CHAN_MAX, + nchans, def_chan_2ghz, nchan, bands, ht40); } if (isset(bands, IEEE80211_MODE_11A) || isset(bands, IEEE80211_MODE_11NA)) { - for (i = 36; i <= 64; i += 4) { - addchan(ic, i, IEEE80211_CHAN_A); - if (isset(bands, IEEE80211_MODE_11NA)) { - addchan(ic, i, - IEEE80211_CHAN_A | IEEE80211_CHAN_HT20); - } - if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) == 0) - continue; - if ((i % 8) == 4) { - addchan(ic, i, - IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U); - addchan(ic, i + 4, - IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D); - } - } - for (i = 100; i <= 140; i += 4) { - addchan(ic, i, IEEE80211_CHAN_A); - if (isset(bands, IEEE80211_MODE_11NA)) { - addchan(ic, i, - IEEE80211_CHAN_A | IEEE80211_CHAN_HT20); - } - if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) == 0) - continue; - if ((i % 8) == 4 && i != 140) { - addchan(ic, i, - IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U); - addchan(ic, i + 4, - IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D); - } - } - for (i = 149; i <= 161; i += 4) { - addchan(ic, i, IEEE80211_CHAN_A); - if (isset(bands, IEEE80211_MODE_11NA)) { - addchan(ic, i, - IEEE80211_CHAN_A | IEEE80211_CHAN_HT20); - } - if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) == 0) - continue; - if ((i % 8) == 5) { - addchan(ic, i, - IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U); - addchan(ic, i + 4, - IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D); - } - } + ieee80211_add_channel_list_5ghz(chans, IEEE80211_CHAN_MAX, + nchans, def_chan_5ghz_band1, nitems(def_chan_5ghz_band1), + bands, ht40); + ieee80211_add_channel_list_5ghz(chans, IEEE80211_CHAN_MAX, + nchans, def_chan_5ghz_band2, nitems(def_chan_5ghz_band2), + bands, ht40); + ieee80211_add_channel_list_5ghz(chans, IEEE80211_CHAN_MAX, + nchans, def_chan_5ghz_band3, nitems(def_chan_5ghz_band3), + bands, ht40); } if (rd != NULL) ic->ic_regdomain = *rd; Index: head/sys/net80211/ieee80211_var.h =================================================================== --- head/sys/net80211/ieee80211_var.h +++ head/sys/net80211/ieee80211_var.h @@ -723,6 +723,14 @@ int ieee80211_chan2ieee(struct ieee80211com *, const struct ieee80211_channel *); u_int ieee80211_ieee2mhz(u_int, u_int); +int ieee80211_add_channel(struct ieee80211_channel[], int, int *, + uint8_t, uint16_t, int8_t, uint32_t, const uint8_t[]); +int ieee80211_add_channel_ht40(struct ieee80211_channel[], int, int *, + uint8_t, int8_t, uint32_t); +int ieee80211_add_channel_list_2ghz(struct ieee80211_channel[], int, int *, + const uint8_t[], int, const uint8_t[], int); +int ieee80211_add_channel_list_5ghz(struct ieee80211_channel[], int, int *, + const uint8_t[], int, const uint8_t[], int); struct ieee80211_channel *ieee80211_find_channel(struct ieee80211com *, int freq, int flags); struct ieee80211_channel *ieee80211_find_channel_byieee(struct ieee80211com *,