net80211: provide a set of ieee80211_add_channel*() functions

This change adds few methods for net80211 channel table setup:

- ieee80211_add_channel()
- ieee80211_add_channel_ht40()
(primarily for drivers, that parse EEPROM to get channel list -
they will allow to hide implementation details).

- ieee80211_add_channel_list_2ghz()
- ieee80211_add_channel_list_5ghz()
(mostly as a replacement for ieee80211_init_channels() - they will allow
to specify non-default channel list; may be used in ic_getradiocaps()).

Tested with wpi(4) (add_channel) and rum(4) (add_channel_list_2ghz).

Reviewed by:	adrian
Differential Revision:	https://reviews.freebsd.org/D6124
This commit is contained in:
Andriy Voskoboinyk 2016-04-29 21:18:14 +00:00
parent 794277da54
commit 355fec4842
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=298812
3 changed files with 292 additions and 92 deletions

View File

@ -970,6 +970,261 @@ ieee80211_ieee2mhz(u_int chan, u_int flags)
}
}
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 @@ struct ieee80211_channel *
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 @@ ieee80211_find_channel(struct ieee80211com *ic, int freq, int flags)
(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));
}
/*

View File

@ -98,22 +98,14 @@ ieee80211_regdomain_vdetach(struct ieee80211vap *vap)
{
}
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 @@ int
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;

View File

@ -723,6 +723,14 @@ int ieee80211_mhz2ieee(u_int, u_int);
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 *,