summaryrefslogtreecommitdiffstats
path: root/sys/dev/ath
diff options
context:
space:
mode:
authorsam <sam@FreeBSD.org>2006-12-27 19:07:09 +0000
committersam <sam@FreeBSD.org>2006-12-27 19:07:09 +0000
commit0234fef5787742d388b0e2b45b22c02d03a7a4db (patch)
tree7c997ccaf12c636a5d267fffcc5233906b97a851 /sys/dev/ath
parent49d3bbfaa099fad9a14b0aab421022f98ed02ced (diff)
downloadFreeBSD-src-0234fef5787742d388b0e2b45b22c02d03a7a4db.zip
FreeBSD-src-0234fef5787742d388b0e2b45b22c02d03a7a4db.tar.gz
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.
Diffstat (limited to 'sys/dev/ath')
-rw-r--r--sys/dev/ath/if_ath.c159
-rw-r--r--sys/dev/ath/if_athvar.h16
2 files changed, 112 insertions, 63 deletions
diff --git a/sys/dev/ath/if_ath.c b/sys/dev/ath/if_ath.c
index a0bcb9a..706752a 100644
--- a/sys/dev/ath/if_ath.c
+++ b/sys/dev/ath/if_ath.c
@@ -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
@@ -5420,18 +5433,45 @@ ath_sysctl_rfsilent(SYSCTL_HANDLER_ARGS)
}
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");
diff --git a/sys/dev/ath/if_athvar.h b/sys/dev/ath/if_athvar.h
index 40ccb48..f82bd7e 100644
--- a/sys/dev/ath/if_athvar.h
+++ b/sys/dev/ath/if_athvar.h
@@ -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)))
OpenPOWER on IntegriCloud