Add half/quarter rate 11a channel support:

o change handling of regdomain-related mib knobs so they can be set
  post-attach: regdomain, countrycode, outdoor, and xchanmode; the
  hal will not permit changing the regdomain but we expose it for now
o on regdomain/countrycode change recalculate the channel list and
  push it to the net80211 layer (NB: looks to need more tweaking)
o setup rate tables for half/quarter rate channels
o honor half/quarter rate channel configs when changing channels
o honor half/quarter rate channel configs when setting the slot time
o use hack/nonstandard channel numbering scheme for the public safety
  band to avoid overlapping 2.4G channels on dual-band cards
o remove setup of ic_sup_rates; the net80211 layer can do this for us
  and it simplifies handling of half/quarter rate channels

Tested only in Public Safety Band with cards that have RF5112.
This commit is contained in:
Sam Leffler 2006-12-27 19:07:09 +00:00
parent b0eb2f6935
commit aaa70f2f6f
2 changed files with 112 additions and 63 deletions

View File

@ -170,8 +170,8 @@ static void ath_calibrate(void *);
static int ath_newstate(struct ieee80211com *, enum ieee80211_state, int);
static void ath_setup_stationkey(struct ieee80211_node *);
static void ath_newassoc(struct ieee80211_node *, int);
static int ath_getchannels(struct ath_softc *, u_int cc,
HAL_BOOL outdoor, HAL_BOOL xchanmode);
static int ath_getchannels(struct ath_softc *,
HAL_REG_DOMAIN, HAL_CTRY_CODE, HAL_BOOL, HAL_BOOL);
static void ath_led_event(struct ath_softc *, int);
static void ath_update_txpow(struct ath_softc *);
@ -194,15 +194,15 @@ static int ath_calinterval = 30; /* calibrate every 30 secs */
SYSCTL_INT(_hw_ath, OID_AUTO, calibrate, CTLFLAG_RW, &ath_calinterval,
0, "chip calibration interval (secs)");
static int ath_outdoor = AH_TRUE; /* outdoor operation */
SYSCTL_INT(_hw_ath, OID_AUTO, outdoor, CTLFLAG_RD, &ath_outdoor,
SYSCTL_INT(_hw_ath, OID_AUTO, outdoor, CTLFLAG_RW, &ath_outdoor,
0, "outdoor operation");
TUNABLE_INT("hw.ath.outdoor", &ath_outdoor);
static int ath_xchanmode = AH_TRUE; /* extended channel use */
SYSCTL_INT(_hw_ath, OID_AUTO, xchanmode, CTLFLAG_RD, &ath_xchanmode,
SYSCTL_INT(_hw_ath, OID_AUTO, xchanmode, CTLFLAG_RW, &ath_xchanmode,
0, "extended channel mode");
TUNABLE_INT("hw.ath.xchanmode", &ath_xchanmode);
static int ath_countrycode = CTRY_DEFAULT; /* country code */
SYSCTL_INT(_hw_ath, OID_AUTO, countrycode, CTLFLAG_RD, &ath_countrycode,
SYSCTL_INT(_hw_ath, OID_AUTO, countrycode, CTLFLAG_RW, &ath_countrycode,
0, "country code");
TUNABLE_INT("hw.ath.countrycode", &ath_countrycode);
static int ath_regdomain = 0; /* regulatory domain */
@ -210,11 +210,11 @@ SYSCTL_INT(_hw_ath, OID_AUTO, regdomain, CTLFLAG_RD, &ath_regdomain,
0, "regulatory domain");
static int ath_rxbuf = ATH_RXBUF; /* # rx buffers to allocate */
SYSCTL_INT(_hw_ath, OID_AUTO, rxbuf, CTLFLAG_RD, &ath_rxbuf,
SYSCTL_INT(_hw_ath, OID_AUTO, rxbuf, CTLFLAG_RW, &ath_rxbuf,
0, "rx buffers allocated");
TUNABLE_INT("hw.ath.rxbuf", &ath_rxbuf);
static int ath_txbuf = ATH_TXBUF; /* # tx buffers to allocate */
SYSCTL_INT(_hw_ath, OID_AUTO, txbuf, CTLFLAG_RD, &ath_txbuf,
SYSCTL_INT(_hw_ath, OID_AUTO, txbuf, CTLFLAG_RW, &ath_txbuf,
0, "tx buffers allocated");
TUNABLE_INT("hw.ath.txbuf", &ath_txbuf);
@ -351,8 +351,8 @@ ath_attach(u_int16_t devid, struct ath_softc *sc)
* is resposible for filtering this list based on settings
* like the phy mode.
*/
error = ath_getchannels(sc, ath_countrycode,
ath_outdoor, ath_xchanmode);
error = ath_getchannels(sc, ath_regdomain, ath_countrycode,
ath_xchanmode != 0, ath_outdoor != 0);
if (error != 0)
goto bad;
@ -364,6 +364,9 @@ ath_attach(u_int16_t devid, struct ath_softc *sc)
ath_rate_setup(sc, IEEE80211_MODE_11G);
ath_rate_setup(sc, IEEE80211_MODE_TURBO_A);
ath_rate_setup(sc, IEEE80211_MODE_TURBO_G);
ath_rate_setup(sc, IEEE80211_MODE_11A_HALF);
ath_rate_setup(sc, IEEE80211_MODE_11A_QUARTER);
/* NB: setup here so ath_rate_update is happy */
ath_setcurmode(sc, IEEE80211_MODE_11A);
@ -899,6 +902,10 @@ ath_chan2flags(struct ieee80211com *ic, struct ieee80211_channel *chan)
KASSERT(mode < N(modeflags), ("unexpected phy mode %u", mode));
KASSERT(modeflags[mode] != 0, ("mode %u undefined", mode));
if (IEEE80211_IS_CHAN_HALF(chan))
return modeflags[mode] | CHANNEL_HALF;
if (IEEE80211_IS_CHAN_QUARTER(chan))
return modeflags[mode] | CHANNEL_QUARTER;
return modeflags[mode];
#undef N
}
@ -1850,11 +1857,26 @@ ath_setslottime(struct ath_softc *sc)
{
struct ieee80211com *ic = &sc->sc_ic;
struct ath_hal *ah = sc->sc_ah;
u_int usec;
if (ic->ic_flags & IEEE80211_F_SHSLOT)
ath_hal_setslottime(ah, HAL_SLOT_TIME_9);
if (IEEE80211_IS_CHAN_A(ic->ic_curchan)) {
if (IEEE80211_IS_CHAN_HALF(ic->ic_curchan))
usec = 13;
else if (IEEE80211_IS_CHAN_QUARTER(ic->ic_curchan))
usec = 21;
else
usec = HAL_SLOT_TIME_9;
} else if (ic->ic_flags & IEEE80211_F_SHSLOT)
usec = HAL_SLOT_TIME_9;
else
ath_hal_setslottime(ah, HAL_SLOT_TIME_20);
usec = HAL_SLOT_TIME_20;
DPRINTF(sc, ATH_DEBUG_RESET,
"%s: chan %u MHz flags 0x%x %s slot, %u usec\n",
__func__, ic->ic_curchan->ic_freq, ic->ic_curchan->ic_flags,
ic->ic_flags & IEEE80211_F_SHSLOT ? "short" : "long", usec);
ath_hal_setslottime(ah, usec);
sc->sc_updateslot = OK;
}
@ -4297,6 +4319,12 @@ ath_chan_change(struct ath_softc *sc, struct ieee80211_channel *chan)
* if we're switching; e.g. 11a to 11b/g.
*/
mode = ieee80211_chan2mode(ic, chan);
if (mode == IEEE80211_MODE_11A) {
if (IEEE80211_IS_CHAN_HALF(chan))
mode = IEEE80211_MODE_11A_HALF;
else if (IEEE80211_IS_CHAN_QUARTER(chan))
mode = IEEE80211_MODE_11A_QUARTER;
}
if (mode != sc->sc_curmode)
ath_setcurmode(sc, mode);
/*
@ -4306,8 +4334,7 @@ ath_chan_change(struct ath_softc *sc, struct ieee80211_channel *chan)
if (IEEE80211_IS_CHAN_A(chan))
flags = IEEE80211_CHAN_A;
/* XXX 11g schizophrenia */
else if (IEEE80211_IS_CHAN_G(chan) ||
IEEE80211_IS_CHAN_PUREG(chan))
else if (IEEE80211_IS_CHAN_ANYG(chan))
flags = IEEE80211_CHAN_G;
else
flags = IEEE80211_CHAN_B;
@ -4741,10 +4768,11 @@ ath_newassoc(struct ieee80211_node *ni, int isnew)
}
static int
ath_getchannels(struct ath_softc *sc, u_int cc,
HAL_BOOL outdoor, HAL_BOOL xchanmode)
ath_getchannels(struct ath_softc *sc,
HAL_REG_DOMAIN rd, HAL_CTRY_CODE cc, HAL_BOOL outdoor, HAL_BOOL xchanmode)
{
#define COMPAT (CHANNEL_ALL_NOTURBO|CHANNEL_PASSIVE)
#define COMPAT \
(CHANNEL_ALL_NOTURBO|CHANNEL_PASSIVE|CHANNEL_HALF|CHANNEL_QUARTER)
#define IS_CHAN_PUBLIC_SAFETY(_c) \
(((_c)->channelFlags & CHANNEL_5GHZ) && \
((_c)->channel > 4940 && (_c)->channel < 4990))
@ -4753,6 +4781,7 @@ ath_getchannels(struct ath_softc *sc, u_int cc,
struct ath_hal *ah = sc->sc_ah;
HAL_CHANNEL *chans;
int i, ix, nchan;
u_int32_t regdomain;
chans = malloc(IEEE80211_CHAN_MAX * sizeof(HAL_CHANNEL),
M_TEMP, M_NOWAIT);
@ -4761,13 +4790,10 @@ ath_getchannels(struct ath_softc *sc, u_int cc,
return ENOMEM;
}
if (!ath_hal_init_channels(ah, chans, IEEE80211_CHAN_MAX, &nchan,
NULL, 0, NULL,
cc, HAL_MODE_ALL, outdoor, xchanmode)) {
u_int32_t rd;
ath_hal_getregdomain(ah, &rd);
NULL, 0, NULL, cc, HAL_MODE_ALL, outdoor, xchanmode)) {
ath_hal_getregdomain(ah, &regdomain);
if_printf(ifp, "unable to collect channel list from hal; "
"regdomain likely %u country code %u\n", rd, cc);
"regdomain likely %u country code %u\n", regdomain, cc);
free(chans, M_TEMP);
return EINVAL;
}
@ -4776,6 +4802,7 @@ ath_getchannels(struct ath_softc *sc, u_int cc,
* Convert HAL channels to ieee80211 ones and insert
* them in the table according to their channel number.
*/
memset(ic->ic_channels, 0, sizeof(ic->ic_channels));
for (i = 0; i < nchan; i++) {
HAL_CHANNEL *c = &chans[i];
u_int16_t flags;
@ -4783,13 +4810,12 @@ ath_getchannels(struct ath_softc *sc, u_int cc,
/*
* XXX we're not ready to handle the ieee number mapping
* for public safety channels as they overlap with any
* 2GHz channels; for now use the non-public safety
* numbering which is non-overlapping.
* 2GHz channels; for now use a non-public safety
* numbering that is non-overlapping.
*/
ix = ath_hal_mhz2ieee(ah, c->channel, c->channelFlags);
if (IS_CHAN_PUBLIC_SAFETY(c))
ix = (c->channel - 4000) / 5;
else
ix = ath_hal_mhz2ieee(ah, c->channel, c->channelFlags);
ix += 37; /* XXX */
if (ix > IEEE80211_CHAN_MAX) {
if_printf(ifp, "bad hal channel %d (%u/%x) ignored\n",
ix, c->channel, c->channelFlags);
@ -4821,6 +4847,10 @@ ath_getchannels(struct ath_softc *sc, u_int cc,
}
}
free(chans, M_TEMP);
ath_hal_getregdomain(ah, &sc->sc_regdomain);
ath_hal_getcountrycode(ah, &sc->sc_countrycode);
sc->sc_xchanmode = xchanmode;
sc->sc_outdoor = outdoor;
return 0;
#undef IS_CHAN_PUBLIC_SAFETY
#undef COMPAT
@ -4903,35 +4933,22 @@ ath_update_txpow(struct ath_softc *sc)
ic->ic_bss->ni_txpower = txpow;
}
static void
rate_setup(struct ath_softc *sc,
const HAL_RATE_TABLE *rt, struct ieee80211_rateset *rs)
{
int i, maxrates;
if (rt->rateCount > IEEE80211_RATE_MAXSIZE) {
DPRINTF(sc, ATH_DEBUG_ANY,
"%s: rate table too small (%u > %u)\n",
__func__, rt->rateCount, IEEE80211_RATE_MAXSIZE);
maxrates = IEEE80211_RATE_MAXSIZE;
} else
maxrates = rt->rateCount;
for (i = 0; i < maxrates; i++)
rs->rs_rates[i] = rt->info[i].dot11Rate;
rs->rs_nrates = maxrates;
}
static int
ath_rate_setup(struct ath_softc *sc, u_int mode)
{
struct ath_hal *ah = sc->sc_ah;
struct ieee80211com *ic = &sc->sc_ic;
const HAL_RATE_TABLE *rt;
switch (mode) {
case IEEE80211_MODE_11A:
rt = ath_hal_getratetable(ah, HAL_MODE_11A);
break;
case IEEE80211_MODE_11A_HALF:
rt = ath_hal_getratetable(ah, HAL_MODE_11A_HALF_RATE);
break;
case IEEE80211_MODE_11A_QUARTER:
rt = ath_hal_getratetable(ah, HAL_MODE_11A_QUARTER_RATE);
break;
case IEEE80211_MODE_11B:
rt = ath_hal_getratetable(ah, HAL_MODE_11B);
break;
@ -4951,11 +4968,7 @@ ath_rate_setup(struct ath_softc *sc, u_int mode)
return 0;
}
sc->sc_rates[mode] = rt;
if (rt != NULL) {
rate_setup(sc, rt, &ic->ic_sup_rates[mode]);
return 1;
} else
return 0;
return (rt != NULL);
}
static void
@ -5419,19 +5432,46 @@ ath_sysctl_rfsilent(SYSCTL_HANDLER_ARGS)
return 0;
}
static int
ath_sysctl_countrycode(SYSCTL_HANDLER_ARGS)
{
struct ath_softc *sc = arg1;
u_int32_t cc = sc->sc_countrycode;
struct ieee80211com *ic = &sc->sc_ic;
int error;
error = sysctl_handle_int(oidp, &cc, 0, req);
if (error || !req->newptr)
return error;
error = ath_getchannels(sc, sc->sc_regdomain, cc,
sc->sc_outdoor, sc->sc_xchanmode);
if (error != 0)
return error;
ieee80211_media_init(ic, ath_media_change, ieee80211_media_status);
/* setcurmode? */
return 0;
}
static int
ath_sysctl_regdomain(SYSCTL_HANDLER_ARGS)
{
struct ath_softc *sc = arg1;
u_int32_t rd;
u_int32_t rd = sc->sc_regdomain;
struct ieee80211com *ic = &sc->sc_ic;
int error;
if (!ath_hal_getregdomain(sc->sc_ah, &rd))
return EINVAL;
error = sysctl_handle_int(oidp, &rd, 0, req);
if (error || !req->newptr)
return error;
return !ath_hal_setregdomain(sc->sc_ah, rd) ? EINVAL : 0;
if (!ath_hal_setregdomain(sc->sc_ah, rd))
return EINVAL;
error = ath_getchannels(sc, rd, sc->sc_countrycode,
sc->sc_outdoor, sc->sc_xchanmode);
if (error != 0)
return error;
ieee80211_media_init(ic, ath_media_change, ieee80211_media_status);
/* setcurmode? */
return 0;
}
static int
@ -5469,10 +5509,9 @@ ath_sysctlattach(struct ath_softc *sc)
struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev);
struct ath_hal *ah = sc->sc_ah;
ath_hal_getcountrycode(sc->sc_ah, &sc->sc_countrycode);
SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
"countrycode", CTLFLAG_RD, &sc->sc_countrycode, 0,
"EEPROM country code");
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
"countrycode", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
ath_sysctl_countrycode, "I", "country code");
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
"regdomain", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
ath_sysctl_regdomain, "I", "EEPROM regdomain code");

View File

@ -173,8 +173,9 @@ struct ath_softc {
struct ifnet *sc_ifp; /* interface common */
struct ath_stats sc_stats; /* interface statistics */
struct ieee80211com sc_ic; /* IEEE 802.11 common */
int sc_countrycode;
int sc_debug;
u_int32_t sc_countrycode;
u_int32_t sc_regdomain;
void (*sc_recv_mgmt)(struct ieee80211com *,
struct mbuf *,
struct ieee80211_node *,
@ -203,9 +204,13 @@ struct ath_softc {
sc_blinking: 1, /* LED blink operation active */
sc_mcastkey: 1, /* mcast key cache search */
sc_syncbeacon:1,/* sync/resync beacon timers */
sc_hasclrkey:1; /* CLR key supported */
sc_hasclrkey:1, /* CLR key supported */
sc_xchanmode: 1,/* extended channel mode */
sc_outdoor : 1;/* outdoor operation */
/* rate tables */
const HAL_RATE_TABLE *sc_rates[IEEE80211_MODE_MAX];
#define IEEE80211_MODE_11A_HALF (IEEE80211_MODE_MAX+0)
#define IEEE80211_MODE_11A_QUARTER (IEEE80211_MODE_MAX+1)
const HAL_RATE_TABLE *sc_rates[IEEE80211_MODE_MAX+2];
const HAL_RATE_TABLE *sc_currates; /* current rate table */
enum ieee80211_phymode sc_curmode; /* current phy mode */
HAL_OPMODE sc_opmode; /* current operating mode */
@ -549,6 +554,11 @@ void ath_intr(void *);
#define HAL_TXQ_TXEOLINT_ENABLE TXQ_FLAG_TXEOLINT_ENABLE
#define HAL_TXQ_TXURNINT_ENABLE TXQ_FLAG_TXURNINT_ENABLE
#endif
#if HAL_ABI_VERSION < 0x06102501
#define ath_hal_ispublicsafetysku(ah) \
(((ah)->ah_regdomain == 0 && (ah)->ah_countryCode == 842) || \
(ah)->ah_regdomain == 0x12)
#endif
#define ath_hal_setuprxdesc(_ah, _ds, _size, _intreq) \
((*(_ah)->ah_setupRxDesc)((_ah), (_ds), (_size), (_intreq)))