diff options
Diffstat (limited to 'sys/dev/ath')
36 files changed, 1341 insertions, 249 deletions
diff --git a/sys/dev/ath/ath_hal/ah.c b/sys/dev/ath/ath_hal/ah.c index dea1736..36815f1 100644 --- a/sys/dev/ath/ath_hal/ah.c +++ b/sys/dev/ath/ath_hal/ah.c @@ -657,7 +657,8 @@ ath_hal_getcapability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type, } case HAL_CAP_RXBUFSIZE: case HAL_CAP_NUM_MR_RETRIES: - return HAL_EINVAL; /* XXX not yet */ + *result = pCap->halNumMRRetries; + return HAL_OK; case HAL_CAP_BT_COEX: return pCap->halBtCoexSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_HT20_SGI: diff --git a/sys/dev/ath/ath_hal/ah.h b/sys/dev/ath/ath_hal/ah.h index aa3e8b6..a22c25d 100644 --- a/sys/dev/ath/ath_hal/ah.h +++ b/sys/dev/ath/ath_hal/ah.h @@ -220,6 +220,8 @@ typedef enum { #define HAL_NUM_RX_QUEUES 2 /* max possible # of queues */ +#define HAL_TXFIFO_DEPTH 8 /* transmit fifo depth */ + /* * Transmit queue subtype. These map directly to * WME Access Categories (except for UPSD). Refer @@ -580,13 +582,16 @@ typedef enum { typedef struct { u_int Tries; - u_int Rate; + u_int Rate; /* hardware rate code */ + u_int RateIndex; /* rate series table index */ u_int PktDuration; u_int ChSel; u_int RateFlags; #define HAL_RATESERIES_RTS_CTS 0x0001 /* use rts/cts w/this series */ #define HAL_RATESERIES_2040 0x0002 /* use ext channel for series */ #define HAL_RATESERIES_HALFGI 0x0004 /* use half-gi for series */ +#define HAL_RATESERIES_STBC 0x0008 /* use STBC for series */ + u_int tx_power_cap; } HAL_11N_RATE_SERIES; typedef enum { @@ -1084,6 +1089,15 @@ struct ath_hal { void __ahdecl(*ah_reqTxIntrDesc)(struct ath_hal *, struct ath_desc*); HAL_BOOL __ahdecl(*ah_getTxCompletionRates)(struct ath_hal *, const struct ath_desc *ds, int *rates, int *tries); + void __ahdecl(*ah_setTxDescLink)(struct ath_hal *ah, void *ds, + uint32_t link); + void __ahdecl(*ah_getTxDescLink)(struct ath_hal *ah, void *ds, + uint32_t *link); + void __ahdecl(*ah_getTxDescLinkPtr)(struct ath_hal *ah, void *ds, + uint32_t **linkptr); + void __ahdecl(*ah_setupTxStatusRing)(struct ath_hal *, + void *ts_start, uint32_t ts_paddr_start, + uint16_t size); /* Receive Functions */ uint32_t __ahdecl(*ah_getRxDP)(struct ath_hal*, HAL_RX_QUEUE); @@ -1224,7 +1238,7 @@ struct ath_hal { struct ath_desc *, u_int, u_int, HAL_11N_RATE_SERIES [], u_int, u_int); void __ahdecl(*ah_set11nAggrFirst)(struct ath_hal *, - struct ath_desc *, u_int, u_int); + struct ath_desc *, u_int); void __ahdecl(*ah_set11nAggrMiddle)(struct ath_hal *, struct ath_desc *, u_int); void __ahdecl(*ah_set11nAggrLast)(struct ath_hal *, diff --git a/sys/dev/ath/ath_hal/ah_desc.h b/sys/dev/ath/ath_hal/ah_desc.h index 1203ebb..542453f 100644 --- a/sys/dev/ath/ath_hal/ah_desc.h +++ b/sys/dev/ath/ath_hal/ah_desc.h @@ -57,16 +57,19 @@ struct ath_tx_status { uint8_t ts_finaltsi; /* final transmit series index */ #ifdef AH_SUPPORT_AR5416 /* 802.11n status */ - uint8_t ts_flags; /* misc flags */ - int8_t ts_rssi_ctl[3]; /* tx ack RSSI [ctl, chain 0-2] */ - int8_t ts_rssi_ext[3]; /* tx ack RSSI [ext, chain 0-2] */ -/* #define ts_rssi ts_rssi_combined */ - uint32_t ts_ba_low; /* blockack bitmap low */ - uint32_t ts_ba_high; /* blockack bitmap high */ + uint8_t ts_flags; /* misc flags */ + uint8_t ts_queue_id; /* AR9300: TX queue id */ + uint8_t ts_desc_id; /* AR9300: TX descriptor id */ uint8_t ts_tid; /* TID */ - uint32_t ts_evm0; /* evm bytes */ - uint32_t ts_evm1; - uint32_t ts_evm2; +/* #define ts_rssi ts_rssi_combined */ + uint32_t ts_ba_low; /* blockack bitmap low */ + uint32_t ts_ba_high; /* blockack bitmap high */ + uint32_t ts_evm0; /* evm bytes */ + uint32_t ts_evm1; + uint32_t ts_evm2; + int8_t ts_rssi_ctl[3]; /* tx ack RSSI [ctl, chain 0-2] */ + int8_t ts_rssi_ext[3]; /* tx ack RSSI [ext, chain 0-2] */ + uint8_t ts_pad[2]; #endif /* AH_SUPPORT_AR5416 */ }; @@ -243,6 +246,8 @@ struct ath_desc_status { #define HAL_TXDESC_EXT_ONLY 0x0080 /* send on ext channel only (11n) */ #define HAL_TXDESC_EXT_AND_CTL 0x0100 /* send on ext + ctl channels (11n) */ #define HAL_TXDESC_VMF 0x0200 /* virtual more frag */ +#define HAL_TXDESC_LOWRXCHAIN 0x0400 /* switch to low RX chain */ +#define HAL_TXDESC_LDPC 0x1000 /* flags passed to rx descriptor setup methods */ #define HAL_RXDESC_INTREQ 0x0020 /* enable per-descriptor interrupt */ diff --git a/sys/dev/ath/ath_hal/ah_internal.h b/sys/dev/ath/ath_hal/ah_internal.h index 65ec50e..200b84e 100644 --- a/sys/dev/ath/ath_hal/ah_internal.h +++ b/sys/dev/ath/ath_hal/ah_internal.h @@ -252,6 +252,7 @@ typedef struct { int halRxStatusLen; int halRxHpFifoDepth; int halRxLpFifoDepth; + int halNumMRRetries; } HAL_CAPABILITIES; struct regDomain; diff --git a/sys/dev/ath/ath_hal/ar5210/ar5210.h b/sys/dev/ath/ath_hal/ar5210/ar5210.h index 657e250..b7cfea3 100644 --- a/sys/dev/ath/ath_hal/ar5210/ar5210.h +++ b/sys/dev/ath/ath_hal/ar5210/ar5210.h @@ -179,6 +179,12 @@ extern void ar5210GetTxIntrQueue(struct ath_hal *ah, uint32_t *); extern void ar5210IntrReqTxDesc(struct ath_hal *ah, struct ath_desc *); extern HAL_BOOL ar5210GetTxCompletionRates(struct ath_hal *ah, const struct ath_desc *, int *rates, int *tries); +extern void ar5210SetTxDescLink(struct ath_hal *ah, void *ds, + uint32_t link); +extern void ar5210GetTxDescLink(struct ath_hal *ah, void *ds, + uint32_t *link); +extern void ar5210GetTxDescLinkPtr(struct ath_hal *ah, void *ds, + uint32_t **linkptr); extern uint32_t ar5210GetRxDP(struct ath_hal *, HAL_RX_QUEUE); extern void ar5210SetRxDP(struct ath_hal *, uint32_t rxdp, HAL_RX_QUEUE); diff --git a/sys/dev/ath/ath_hal/ar5210/ar5210_attach.c b/sys/dev/ath/ath_hal/ar5210/ar5210_attach.c index 03c5f93..c62f936 100644 --- a/sys/dev/ath/ath_hal/ar5210/ar5210_attach.c +++ b/sys/dev/ath/ath_hal/ar5210/ar5210_attach.c @@ -75,6 +75,9 @@ static const struct ath_hal_private ar5210hal = {{ .ah_getTxIntrQueue = ar5210GetTxIntrQueue, .ah_reqTxIntrDesc = ar5210IntrReqTxDesc, .ah_getTxCompletionRates = ar5210GetTxCompletionRates, + .ah_setTxDescLink = ar5210SetTxDescLink, + .ah_getTxDescLink = ar5210GetTxDescLink, + .ah_getTxDescLinkPtr = ar5210GetTxDescLinkPtr, /* RX Functions */ .ah_getRxDP = ar5210GetRxDP, @@ -358,6 +361,7 @@ ar5210FillCapabilityInfo(struct ath_hal *ah) pCap->halSleepAfterBeaconBroken = AH_TRUE; pCap->halPSPollBroken = AH_FALSE; + pCap->halNumMRRetries = 1; /* No hardware MRR support */ pCap->halTotalQueues = HAL_NUM_TX_QUEUES; pCap->halKeyCacheSize = 64; diff --git a/sys/dev/ath/ath_hal/ar5210/ar5210_xmit.c b/sys/dev/ath/ath_hal/ar5210/ar5210_xmit.c index f7c6030..ac3cb65 100644 --- a/sys/dev/ath/ath_hal/ar5210/ar5210_xmit.c +++ b/sys/dev/ath/ath_hal/ar5210/ar5210_xmit.c @@ -630,3 +630,36 @@ ar5210GetTxCompletionRates(struct ath_hal *ah, const struct ath_desc *ds0, int * { return AH_FALSE; } + +/* + * Set the TX descriptor link pointer + */ +void +ar5210SetTxDescLink(struct ath_hal *ah, void *ds, uint32_t link) +{ + struct ar5210_desc *ads = AR5210DESC(ds); + + ads->ds_link = link; +} + +/* + * Get the TX descriptor link pointer + */ +void +ar5210GetTxDescLink(struct ath_hal *ah, void *ds, uint32_t *link) +{ + struct ar5210_desc *ads = AR5210DESC(ds); + + *link = ads->ds_link; +} + +/* + * Get a pointer to the TX descriptor link pointer + */ +void +ar5210GetTxDescLinkPtr(struct ath_hal *ah, void *ds, uint32_t **linkptr) +{ + struct ar5210_desc *ads = AR5210DESC(ds); + + *linkptr = &ads->ds_link; +} diff --git a/sys/dev/ath/ath_hal/ar5211/ar5211.h b/sys/dev/ath/ath_hal/ar5211/ar5211.h index 1d6c8af..2d631fb 100644 --- a/sys/dev/ath/ath_hal/ar5211/ar5211.h +++ b/sys/dev/ath/ath_hal/ar5211/ar5211.h @@ -204,6 +204,12 @@ extern void ar5211GetTxIntrQueue(struct ath_hal *ah, uint32_t *); extern void ar5211IntrReqTxDesc(struct ath_hal *ah, struct ath_desc *); extern HAL_BOOL ar5211GetTxCompletionRates(struct ath_hal *ah, const struct ath_desc *ds0, int *rates, int *tries); +extern void ar5211SetTxDescLink(struct ath_hal *ah, void *ds, + uint32_t link); +extern void ar5211GetTxDescLink(struct ath_hal *ah, void *ds, + uint32_t *link); +extern void ar5211GetTxDescLinkPtr(struct ath_hal *ah, void *ds, + uint32_t **linkptr); extern uint32_t ar5211GetRxDP(struct ath_hal *, HAL_RX_QUEUE); extern void ar5211SetRxDP(struct ath_hal *, uint32_t rxdp, HAL_RX_QUEUE); diff --git a/sys/dev/ath/ath_hal/ar5211/ar5211_attach.c b/sys/dev/ath/ath_hal/ar5211/ar5211_attach.c index 186ece2..40a1f8a 100644 --- a/sys/dev/ath/ath_hal/ar5211/ar5211_attach.c +++ b/sys/dev/ath/ath_hal/ar5211/ar5211_attach.c @@ -75,6 +75,9 @@ static const struct ath_hal_private ar5211hal = {{ .ah_getTxIntrQueue = ar5211GetTxIntrQueue, .ah_reqTxIntrDesc = ar5211IntrReqTxDesc, .ah_getTxCompletionRates = ar5211GetTxCompletionRates, + .ah_setTxDescLink = ar5211SetTxDescLink, + .ah_getTxDescLink = ar5211GetTxDescLink, + .ah_getTxDescLinkPtr = ar5211GetTxDescLinkPtr, /* RX Functions */ .ah_getRxDP = ar5211GetRxDP, @@ -493,6 +496,7 @@ ar5211FillCapabilityInfo(struct ath_hal *ah) pCap->halSleepAfterBeaconBroken = AH_TRUE; pCap->halPSPollBroken = AH_TRUE; pCap->halVEOLSupport = AH_TRUE; + pCap->halNumMRRetries = 1; /* No hardware MRR support */ pCap->halTotalQueues = HAL_NUM_TX_QUEUES; pCap->halKeyCacheSize = 128; diff --git a/sys/dev/ath/ath_hal/ar5211/ar5211_xmit.c b/sys/dev/ath/ath_hal/ar5211/ar5211_xmit.c index e1e7f73..da6e5c9 100644 --- a/sys/dev/ath/ath_hal/ar5211/ar5211_xmit.c +++ b/sys/dev/ath/ath_hal/ar5211/ar5211_xmit.c @@ -671,3 +671,27 @@ ar5211GetTxCompletionRates(struct ath_hal *ah, const struct ath_desc *ds0, int * return AH_FALSE; } + +void +ar5211SetTxDescLink(struct ath_hal *ah, void *ds, uint32_t link) +{ + struct ar5211_desc *ads = AR5211DESC(ds); + + ads->ds_link = link; +} + +void +ar5211GetTxDescLink(struct ath_hal *ah, void *ds, uint32_t *link) +{ + struct ar5211_desc *ads = AR5211DESC(ds); + + *link = ads->ds_link; +} + +void +ar5211GetTxDescLinkPtr(struct ath_hal *ah, void *ds, uint32_t **linkptr) +{ + struct ar5211_desc *ads = AR5211DESC(ds); + + *linkptr = &ads->ds_link; +} diff --git a/sys/dev/ath/ath_hal/ar5212/ar5212.h b/sys/dev/ath/ath_hal/ar5212/ar5212.h index a8b95d1..24770ce 100644 --- a/sys/dev/ath/ath_hal/ar5212/ar5212.h +++ b/sys/dev/ath/ath_hal/ar5212/ar5212.h @@ -602,6 +602,12 @@ extern void ar5212GetTxIntrQueue(struct ath_hal *ah, uint32_t *); extern void ar5212IntrReqTxDesc(struct ath_hal *ah, struct ath_desc *); extern HAL_BOOL ar5212GetTxCompletionRates(struct ath_hal *ah, const struct ath_desc *ds0, int *rates, int *tries); +extern void ar5212SetTxDescLink(struct ath_hal *ah, void *ds, + uint32_t link); +extern void ar5212GetTxDescLink(struct ath_hal *ah, void *ds, + uint32_t *link); +extern void ar5212GetTxDescLinkPtr(struct ath_hal *ah, void *ds, + uint32_t **linkptr); extern const HAL_RATE_TABLE *ar5212GetRateTable(struct ath_hal *, u_int mode); diff --git a/sys/dev/ath/ath_hal/ar5212/ar5212_attach.c b/sys/dev/ath/ath_hal/ar5212/ar5212_attach.c index 43cbe7f..c3fd1c7 100644 --- a/sys/dev/ath/ath_hal/ar5212/ar5212_attach.c +++ b/sys/dev/ath/ath_hal/ar5212/ar5212_attach.c @@ -71,6 +71,9 @@ static const struct ath_hal_private ar5212hal = {{ .ah_getTxIntrQueue = ar5212GetTxIntrQueue, .ah_reqTxIntrDesc = ar5212IntrReqTxDesc, .ah_getTxCompletionRates = ar5212GetTxCompletionRates, + .ah_setTxDescLink = ar5212SetTxDescLink, + .ah_getTxDescLink = ar5212GetTxDescLink, + .ah_getTxDescLinkPtr = ar5212GetTxDescLinkPtr, /* RX Functions */ .ah_getRxDP = ar5212GetRxDP, @@ -821,6 +824,7 @@ ar5212FillCapabilityInfo(struct ath_hal *ah) pCap->halTurboGSupport = pCap->halWirelessModes & HAL_MODE_108G; pCap->halPSPollBroken = AH_TRUE; /* XXX fixed in later revs? */ + pCap->halNumMRRetries = 4; /* Hardware supports 4 MRR */ pCap->halVEOLSupport = AH_TRUE; pCap->halBssIdMaskSupport = AH_TRUE; pCap->halMcastKeySrchSupport = AH_TRUE; diff --git a/sys/dev/ath/ath_hal/ar5212/ar5212_xmit.c b/sys/dev/ath/ath_hal/ar5212/ar5212_xmit.c index 008330a..773d5f4 100644 --- a/sys/dev/ath/ath_hal/ar5212/ar5212_xmit.c +++ b/sys/dev/ath/ath_hal/ar5212/ar5212_xmit.c @@ -971,3 +971,27 @@ ar5212GetTxCompletionRates(struct ath_hal *ah, const struct ath_desc *ds0, int * return AH_TRUE; } + +void +ar5212SetTxDescLink(struct ath_hal *ah, void *ds, uint32_t link) +{ + struct ar5212_desc *ads = AR5212DESC(ds); + + ads->ds_link = link; +} + +void +ar5212GetTxDescLink(struct ath_hal *ah, void *ds, uint32_t *link) +{ + struct ar5212_desc *ads = AR5212DESC(ds); + + *link = ads->ds_link; +} + +void +ar5212GetTxDescLinkPtr(struct ath_hal *ah, void *ds, uint32_t **linkptr) +{ + struct ar5212_desc *ads = AR5212DESC(ds); + + *linkptr = &ads->ds_link; +} diff --git a/sys/dev/ath/ath_hal/ar5416/ar5416.h b/sys/dev/ath/ath_hal/ar5416/ar5416.h index 5a0e7ef..75cd3df 100644 --- a/sys/dev/ath/ath_hal/ar5416/ar5416.h +++ b/sys/dev/ath/ath_hal/ar5416/ar5416.h @@ -387,7 +387,7 @@ extern void ar5416Set11nRateScenario(struct ath_hal *ah, struct ath_desc *ds, u_int nseries, u_int flags); extern void ar5416Set11nAggrFirst(struct ath_hal *ah, struct ath_desc *ds, - u_int aggrLen, u_int numDelims); + u_int aggrLen); extern void ar5416Set11nAggrMiddle(struct ath_hal *ah, struct ath_desc *ds, u_int numDelims); extern void ar5416Set11nAggrLast(struct ath_hal *ah, struct ath_desc *ds); diff --git a/sys/dev/ath/ath_hal/ar5416/ar5416_attach.c b/sys/dev/ath/ath_hal/ar5416/ar5416_attach.c index fc5eefb..295124f7 100644 --- a/sys/dev/ath/ath_hal/ar5416/ar5416_attach.c +++ b/sys/dev/ath/ath_hal/ar5416/ar5416_attach.c @@ -892,6 +892,7 @@ ar5416FillCapabilityInfo(struct ath_hal *ah) pCap->halTurboGSupport = pCap->halWirelessModes & HAL_MODE_108G; pCap->halPSPollBroken = AH_TRUE; /* XXX fixed in later revs? */ + pCap->halNumMRRetries = 4; /* Hardware supports 4 MRR */ pCap->halVEOLSupport = AH_TRUE; pCap->halBssIdMaskSupport = AH_TRUE; pCap->halMcastKeySrchSupport = AH_TRUE; /* Works on AR5416 and later */ diff --git a/sys/dev/ath/ath_hal/ar5416/ar5416_xmit.c b/sys/dev/ath/ath_hal/ar5416/ar5416_xmit.c index e33a55d..bc1b7ee4 100644 --- a/sys/dev/ath/ath_hal/ar5416/ar5416_xmit.c +++ b/sys/dev/ath/ath_hal/ar5416/ar5416_xmit.c @@ -135,6 +135,7 @@ ar5416StopTxDma(struct ath_hal *ah, u_int q) #define set11nRateFlags(_series, _index) \ ((_series)[_index].RateFlags & HAL_RATESERIES_2040 ? AR_2040_##_index : 0) \ |((_series)[_index].RateFlags & HAL_RATESERIES_HALFGI ? AR_GI##_index : 0) \ + |((_series)[_index].RateFlags & HAL_RATESERIES_STBC ? AR_STBC##_index : 0) \ |SM((_series)[_index].ChSel, AR_ChainSel##_index) /* @@ -727,16 +728,14 @@ ar5416Set11nRateScenario(struct ath_hal *ah, struct ath_desc *ds, } void -ar5416Set11nAggrFirst(struct ath_hal *ah, struct ath_desc *ds, - u_int aggrLen, u_int numDelims) +ar5416Set11nAggrFirst(struct ath_hal *ah, struct ath_desc *ds, u_int aggrLen) { struct ar5416_desc *ads = AR5416DESC(ds); ads->ds_ctl1 |= (AR_IsAggr | AR_MoreAggr); ads->ds_ctl6 &= ~(AR_AggrLen | AR_PadDelim); - ads->ds_ctl6 |= SM(aggrLen, AR_AggrLen) | - SM(numDelims, AR_PadDelim); + ads->ds_ctl6 |= SM(aggrLen, AR_AggrLen); } void diff --git a/sys/dev/ath/ath_rate/amrr/amrr.c b/sys/dev/ath/ath_rate/amrr/amrr.c index efba5dd..c252009 100644 --- a/sys/dev/ath/ath_rate/amrr/amrr.c +++ b/sys/dev/ath/ath_rate/amrr/amrr.c @@ -421,6 +421,14 @@ ath_rate_ctl(void *arg, struct ieee80211_node *ni) } } +static int +ath_rate_fetch_node_stats(struct ath_softc *sc, struct ath_node *an, + struct ath_rateioctl *re) +{ + + return (EINVAL); +} + static void ath_rate_sysctlattach(struct ath_softc *sc) { diff --git a/sys/dev/ath/ath_rate/onoe/onoe.c b/sys/dev/ath/ath_rate/onoe/onoe.c index 7160346..7c73926 100644 --- a/sys/dev/ath/ath_rate/onoe/onoe.c +++ b/sys/dev/ath/ath_rate/onoe/onoe.c @@ -407,6 +407,14 @@ ath_rate_sysctlattach(struct ath_softc *sc) "rate control: # good periods before raising rate"); } +static int +ath_rate_fetch_node_stats(struct ath_softc *sc, struct ath_node *an, + struct ath_rateioctl *re) +{ + + return (EINVAL); +} + struct ath_ratectrl * ath_rate_attach(struct ath_softc *sc) { diff --git a/sys/dev/ath/ath_rate/sample/sample.c b/sys/dev/ath/ath_rate/sample/sample.c index ae77e5e..985551b 100644 --- a/sys/dev/ath/ath_rate/sample/sample.c +++ b/sys/dev/ath/ath_rate/sample/sample.c @@ -105,8 +105,6 @@ __FBSDID("$FreeBSD$"); static void ath_rate_ctl_reset(struct ath_softc *, struct ieee80211_node *); -static const int packet_size_bins[NUM_PACKET_SIZE_BINS] = { 250, 1600 }; - static __inline int size_to_bin(int size) { @@ -128,12 +126,6 @@ size_to_bin(int size) return NUM_PACKET_SIZE_BINS-1; } -static __inline int -bin_to_size(int index) -{ - return packet_size_bins[index]; -} - void ath_rate_node_init(struct ath_softc *sc, struct ath_node *an) { @@ -510,8 +502,10 @@ ath_rate_findrate(struct ath_softc *sc, struct ath_node *an, goto done; } - /* XXX TODO: this doesn't know about 11gn vs 11g protection; teach it */ - mrr = sc->sc_mrretry && !(ic->ic_flags & IEEE80211_F_USEPROT); + mrr = sc->sc_mrretry; + /* XXX check HT protmode too */ + if (mrr && (ic->ic_flags & IEEE80211_F_USEPROT && !sc->sc_mrrprot)) + mrr = 0; best_rix = pick_best_rate(an, rt, size_bin, !mrr); if (best_rix >= 0) { @@ -918,7 +912,11 @@ ath_rate_tx_complete(struct ath_softc *sc, struct ath_node *an, short_tries, long_tries); return; } - mrr = sc->sc_mrretry && !(ic->ic_flags & IEEE80211_F_USEPROT); + mrr = sc->sc_mrretry; + /* XXX check HT protmode too */ + if (mrr && (ic->ic_flags & IEEE80211_F_USEPROT && !sc->sc_mrrprot)) + mrr = 0; + if (!mrr || ts->ts_finaltsi == 0) { if (!IS_RATE_DEFINED(sn, final_rix)) { badrate(ifp, 0, ts->ts_rate, long_tries, status); @@ -1198,6 +1196,93 @@ ath_rate_ctl_reset(struct ath_softc *sc, struct ieee80211_node *ni) #undef DOT11RATE } +/* + * Fetch the statistics for the given node. + * + * The ieee80211 node must be referenced and unlocked, however the ath_node + * must be locked. + * + * The main difference here is that we convert the rate indexes + * to 802.11 rates, or the userland output won't make much sense + * as it has no access to the rix table. + */ +int +ath_rate_fetch_node_stats(struct ath_softc *sc, struct ath_node *an, + struct ath_rateioctl *rs) +{ + struct sample_node *sn = ATH_NODE_SAMPLE(an); + const HAL_RATE_TABLE *rt = sc->sc_currates; + struct ath_rateioctl_tlv av; + struct ath_rateioctl_rt *tv; + int y; + int o = 0; + + ATH_NODE_LOCK_ASSERT(an); + + /* + * Ensure there's enough space for the statistics. + */ + if (rs->len < + sizeof(struct ath_rateioctl_tlv) + + sizeof(struct ath_rateioctl_rt) + + sizeof(struct ath_rateioctl_tlv) + + sizeof(struct sample_node)) { + device_printf(sc->sc_dev, "%s: len=%d, too short\n", + __func__, + rs->len); + return (EINVAL); + } + + /* + * Take a temporary copy of the sample node state so we can + * modify it before we copy it. + */ + tv = malloc(sizeof(struct ath_rateioctl_rt), M_TEMP, + M_NOWAIT | M_ZERO); + if (tv == NULL) { + return (ENOMEM); + } + + /* + * Populate the rate table mapping TLV. + */ + tv->nentries = rt->rateCount; + for (y = 0; y < rt->rateCount; y++) { + tv->ratecode[y] = rt->info[y].dot11Rate & IEEE80211_RATE_VAL; + if (rt->info[y].phy == IEEE80211_T_HT) + tv->ratecode[y] |= IEEE80211_RATE_MCS; + } + + o = 0; + /* + * First TLV - rate code mapping + */ + av.tlv_id = ATH_RATE_TLV_RATETABLE; + av.tlv_len = sizeof(struct ath_rateioctl_rt); + copyout(&av, rs->buf + o, sizeof(struct ath_rateioctl_tlv)); + o += sizeof(struct ath_rateioctl_tlv); + copyout(tv, rs->buf + o, sizeof(struct ath_rateioctl_rt)); + o += sizeof(struct ath_rateioctl_rt); + + /* + * Second TLV - sample node statistics + */ + av.tlv_id = ATH_RATE_TLV_SAMPLENODE; + av.tlv_len = sizeof(struct sample_node); + copyout(&av, rs->buf + o, sizeof(struct ath_rateioctl_tlv)); + o += sizeof(struct ath_rateioctl_tlv); + + /* + * Copy the statistics over to the provided buffer. + */ + copyout(sn, rs->buf + o, sizeof(struct sample_node)); + o += sizeof(struct sample_node); + + free(tv, M_TEMP); + + return (0); +} + static void sample_stats(void *arg, struct ieee80211_node *ni) { diff --git a/sys/dev/ath/ath_rate/sample/sample.h b/sys/dev/ath/ath_rate/sample/sample.h index bb72c0a..b9e7230 100644 --- a/sys/dev/ath/ath_rate/sample/sample.h +++ b/sys/dev/ath/ath_rate/sample/sample.h @@ -79,10 +79,18 @@ struct txschedule { */ #define NUM_PACKET_SIZE_BINS 2 +static const int packet_size_bins[NUM_PACKET_SIZE_BINS] = { 250, 1600 }; + +static inline int +bin_to_size(int index) +{ + return packet_size_bins[index]; +} + /* per-node state */ struct sample_node { int static_rix; /* rate index of fixed tx rate */ -#define SAMPLE_MAXRATES 32 /* NB: corresponds to hal info[32] */ +#define SAMPLE_MAXRATES 64 /* NB: corresponds to hal info[32] */ uint32_t ratemask; /* bit mask of valid rate indices */ const struct txschedule *sched; /* tx schedule table */ @@ -101,6 +109,9 @@ struct sample_node { int packets_since_sample[NUM_PACKET_SIZE_BINS]; unsigned sample_tt[NUM_PACKET_SIZE_BINS]; }; + +#ifdef _KERNEL + #define ATH_NODE_SAMPLE(an) ((struct sample_node *)&(an)[1]) #define IS_RATE_DEFINED(sn, rix) (((sn)->ratemask & (1<<(rix))) != 0) @@ -225,4 +236,7 @@ static unsigned calc_usecs_unicast_packet(struct ath_softc *sc, } return tt; } + +#endif /* _KERNEL */ + #endif /* _DEV_ATH_RATE_SAMPLE_H */ diff --git a/sys/dev/ath/if_ath.c b/sys/dev/ath/if_ath.c index 965a1fd..caae6308 100644 --- a/sys/dev/ath/if_ath.c +++ b/sys/dev/ath/if_ath.c @@ -109,6 +109,7 @@ __FBSDID("$FreeBSD$"); #include <dev/ath/if_ath_keycache.h> #include <dev/ath/if_ath_rx.h> #include <dev/ath/if_ath_rx_edma.h> +#include <dev/ath/if_ath_tx_edma.h> #include <dev/ath/if_ath_beacon.h> #include <dev/ath/if_athdfs.h> @@ -253,6 +254,28 @@ SYSCTL_INT(_hw_ath, OID_AUTO, bstuck, CTLFLAG_RW, &ath_bstuck_threshold, MALLOC_DEFINE(M_ATHDEV, "athdev", "ath driver dma buffers"); +void +ath_legacy_attach_comp_func(struct ath_softc *sc) +{ + + /* + * Special case certain configurations. Note the + * CAB queue is handled by these specially so don't + * include them when checking the txq setup mask. + */ + switch (sc->sc_txqsetup &~ (1<<sc->sc_cabq->axq_qnum)) { + case 0x01: + TASK_INIT(&sc->sc_txtask, 0, ath_tx_proc_q0, sc); + break; + case 0x0f: + TASK_INIT(&sc->sc_txtask, 0, ath_tx_proc_q0123, sc); + break; + default: + TASK_INIT(&sc->sc_txtask, 0, ath_tx_proc, sc); + break; + } +} + #define HAL_MODE_HT20 (HAL_MODE_11NG_HT20 | HAL_MODE_11NA_HT20) #define HAL_MODE_HT40 \ (HAL_MODE_11NG_HT40PLUS | HAL_MODE_11NG_HT40MINUS | \ @@ -306,8 +329,11 @@ ath_attach(u_int16_t devid, struct ath_softc *sc) if (ath_hal_hasedma(sc->sc_ah)) { sc->sc_isedma = 1; ath_recv_setup_edma(sc); - } else + ath_xmit_setup_edma(sc); + } else { ath_recv_setup_legacy(sc); + ath_xmit_setup_legacy(sc); + } /* * Check if the MAC has multi-rate retry support. @@ -367,14 +393,24 @@ ath_attach(u_int16_t devid, struct ath_softc *sc) ath_setcurmode(sc, IEEE80211_MODE_11A); /* - * Allocate tx+rx descriptors and populate the lists. + * Allocate TX descriptors and populate the lists. */ error = ath_desc_alloc(sc); if (error != 0) { - if_printf(ifp, "failed to allocate descriptors: %d\n", error); + if_printf(ifp, "failed to allocate TX descriptors: %d\n", + error); + goto bad; + } + error = ath_txdma_setup(sc); + if (error != 0) { + if_printf(ifp, "failed to allocate TX descriptors: %d\n", + error); goto bad; } + /* + * Allocate RX descriptors and populate the lists. + */ error = ath_rxdma_setup(sc); if (error != 0) { if_printf(ifp, "failed to allocate RX descriptors: %d\n", @@ -446,21 +482,12 @@ ath_attach(u_int16_t devid, struct ath_softc *sc) } /* - * Special case certain configurations. Note the - * CAB queue is handled by these specially so don't - * include them when checking the txq setup mask. + * Attach the TX completion function. + * + * The non-EDMA chips may have some special case optimisations; + * this method gives everyone a chance to attach cleanly. */ - switch (sc->sc_txqsetup &~ (1<<sc->sc_cabq->axq_qnum)) { - case 0x01: - TASK_INIT(&sc->sc_txtask, 0, ath_tx_proc_q0, sc); - break; - case 0x0f: - TASK_INIT(&sc->sc_txtask, 0, ath_tx_proc_q0123, sc); - break; - default: - TASK_INIT(&sc->sc_txtask, 0, ath_tx_proc, sc); - break; - } + sc->sc_tx.xmit_attach_comp_func(sc); /* * Setup rate control. Some rate control modules @@ -678,6 +705,12 @@ ath_attach(u_int16_t devid, struct ath_softc *sc) (void) ath_hal_settxchainmask(sc->sc_ah, tx_chainmask); } + /* + * Disable MRR with protected frames by default. + * Only 802.11n series NICs can handle this. + */ + sc->sc_mrrprot = 0; /* XXX should be a capability */ + #ifdef ATH_ENABLE_11N /* * Query HT capabilities @@ -687,6 +720,9 @@ ath_attach(u_int16_t devid, struct ath_softc *sc) int rxs, txs; device_printf(sc->sc_dev, "[HT] enabling HT modes\n"); + + sc->sc_mrrprot = 1; /* XXX should be a capability */ + ic->ic_htcaps = IEEE80211_HTC_HT /* HT operation */ | IEEE80211_HTC_AMPDU /* A-MPDU tx/rx */ | IEEE80211_HTC_AMSDU /* A-MSDU tx/rx */ @@ -858,6 +894,7 @@ ath_attach(u_int16_t devid, struct ath_softc *sc) bad2: ath_tx_cleanup(sc); ath_desc_free(sc); + ath_txdma_teardown(sc); ath_rxdma_teardown(sc); bad: if (ah) @@ -901,6 +938,7 @@ ath_detach(struct ath_softc *sc) ath_dfs_detach(sc); ath_desc_free(sc); + ath_txdma_teardown(sc); ath_rxdma_teardown(sc); ath_tx_cleanup(sc); ath_hal_detach(sc->sc_ah); /* NB: sets chip in full sleep */ @@ -2749,28 +2787,32 @@ ath_load_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error) *paddr = segs->ds_addr; } +/* + * Allocate the descriptors and appropriate DMA tag/setup. + * + * For some situations (eg EDMA TX completion), there isn't a requirement + * for the ath_buf entries to be allocated. + */ int -ath_descdma_setup(struct ath_softc *sc, +ath_descdma_alloc_desc(struct ath_softc *sc, struct ath_descdma *dd, ath_bufhead *head, - const char *name, int nbuf, int ndesc) + const char *name, int ds_size, int ndesc) { #define DS2PHYS(_dd, _ds) \ ((_dd)->dd_desc_paddr + ((caddr_t)(_ds) - (caddr_t)(_dd)->dd_desc)) #define ATH_DESC_4KB_BOUND_CHECK(_daddr, _len) \ ((((u_int32_t)(_daddr) & 0xFFF) > (0x1000 - (_len))) ? 1 : 0) struct ifnet *ifp = sc->sc_ifp; - uint8_t *ds; - struct ath_buf *bf; - int i, bsize, error; - int desc_len; + int error; - desc_len = sizeof(struct ath_desc); + dd->dd_descsize = ds_size; - DPRINTF(sc, ATH_DEBUG_RESET, "%s: %s DMA: %u buffers %u desc/buf\n", - __func__, name, nbuf, ndesc); + DPRINTF(sc, ATH_DEBUG_RESET, + "%s: %s DMA: %u desc, %d bytes per descriptor\n", + __func__, name, ndesc, dd->dd_descsize); dd->dd_name = name; - dd->dd_desc_len = desc_len * nbuf * ndesc; + dd->dd_desc_len = dd->dd_descsize * ndesc; /* * Merlin work-around: @@ -2778,8 +2820,8 @@ ath_descdma_setup(struct ath_softc *sc, * Assume one skipped descriptor per 4KB page. */ if (! ath_hal_split4ktrans(sc->sc_ah)) { - int numdescpage = 4096 / (desc_len * ndesc); - dd->dd_desc_len = (nbuf / numdescpage + 1) * 4096; + int numpages = dd->dd_desc_len / 4096; + dd->dd_desc_len += ds_size * numpages; } /* @@ -2815,7 +2857,7 @@ ath_descdma_setup(struct ath_softc *sc, &dd->dd_dmamap); if (error != 0) { if_printf(ifp, "unable to alloc memory for %u %s descriptors, " - "error %u\n", nbuf * ndesc, dd->dd_name, error); + "error %u\n", ndesc, dd->dd_name, error); goto fail1; } @@ -2829,10 +2871,49 @@ ath_descdma_setup(struct ath_softc *sc, goto fail2; } - ds = (uint8_t *) dd->dd_desc; DPRINTF(sc, ATH_DEBUG_RESET, "%s: %s DMA map: %p (%lu) -> %p (%lu)\n", - __func__, dd->dd_name, ds, (u_long) dd->dd_desc_len, - (caddr_t) dd->dd_desc_paddr, /*XXX*/ (u_long) dd->dd_desc_len); + __func__, dd->dd_name, (uint8_t *) dd->dd_desc, + (u_long) dd->dd_desc_len, (caddr_t) dd->dd_desc_paddr, + /*XXX*/ (u_long) dd->dd_desc_len); + + return (0); + +fail2: + bus_dmamem_free(dd->dd_dmat, dd->dd_desc, dd->dd_dmamap); +fail1: + bus_dmamap_destroy(dd->dd_dmat, dd->dd_dmamap); +fail0: + bus_dma_tag_destroy(dd->dd_dmat); + memset(dd, 0, sizeof(*dd)); + return error; +#undef DS2PHYS +#undef ATH_DESC_4KB_BOUND_CHECK +} + +int +ath_descdma_setup(struct ath_softc *sc, + struct ath_descdma *dd, ath_bufhead *head, + const char *name, int ds_size, int nbuf, int ndesc) +{ +#define DS2PHYS(_dd, _ds) \ + ((_dd)->dd_desc_paddr + ((caddr_t)(_ds) - (caddr_t)(_dd)->dd_desc)) +#define ATH_DESC_4KB_BOUND_CHECK(_daddr, _len) \ + ((((u_int32_t)(_daddr) & 0xFFF) > (0x1000 - (_len))) ? 1 : 0) + struct ifnet *ifp = sc->sc_ifp; + uint8_t *ds; + struct ath_buf *bf; + int i, bsize, error; + + /* Allocate descriptors */ + error = ath_descdma_alloc_desc(sc, dd, head, name, ds_size, + nbuf * ndesc); + + /* Assume any errors during allocation were dealt with */ + if (error != 0) { + return (error); + } + + ds = (uint8_t *) dd->dd_desc; /* allocate rx buffers */ bsize = sizeof(struct ath_buf) * nbuf; @@ -2845,7 +2926,7 @@ ath_descdma_setup(struct ath_softc *sc, dd->dd_bufptr = bf; TAILQ_INIT(head); - for (i = 0; i < nbuf; i++, bf++, ds += (ndesc * desc_len)) { + for (i = 0; i < nbuf; i++, bf++, ds += (ndesc * dd->dd_descsize)) { bf->bf_desc = (struct ath_desc *) ds; bf->bf_daddr = DS2PHYS(dd, ds); if (! ath_hal_split4ktrans(sc->sc_ah)) { @@ -2855,7 +2936,7 @@ ath_descdma_setup(struct ath_softc *sc, * in the descriptor. */ if (ATH_DESC_4KB_BOUND_CHECK(bf->bf_daddr, - desc_len * ndesc)) { + dd->dd_descsize)) { /* Start at the next page */ ds += 0x1000 - (bf->bf_daddr & 0xFFF); bf->bf_desc = (struct ath_desc *) ds; @@ -2873,14 +2954,18 @@ ath_descdma_setup(struct ath_softc *sc, bf->bf_lastds = bf->bf_desc; /* Just an initial value */ TAILQ_INSERT_TAIL(head, bf, bf_list); } + + /* + * XXX TODO: ensure that ds doesn't overflow the descriptor + * allocation otherwise weird stuff will occur and crash your + * machine. + */ return 0; + /* XXX this should likely just call ath_descdma_cleanup() */ fail3: bus_dmamap_unload(dd->dd_dmat, dd->dd_dmamap); -fail2: bus_dmamem_free(dd->dd_dmat, dd->dd_desc, dd->dd_dmamap); -fail1: bus_dmamap_destroy(dd->dd_dmat, dd->dd_dmamap); -fail0: bus_dma_tag_destroy(dd->dd_dmat); memset(dd, 0, sizeof(*dd)); return error; @@ -2888,6 +2973,69 @@ fail0: #undef ATH_DESC_4KB_BOUND_CHECK } +/* + * Allocate ath_buf entries but no descriptor contents. + * + * This is for RX EDMA where the descriptors are the header part of + * the RX buffer. + */ +int +ath_descdma_setup_rx_edma(struct ath_softc *sc, + struct ath_descdma *dd, ath_bufhead *head, + const char *name, int nbuf, int rx_status_len) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ath_buf *bf; + int i, bsize, error; + + DPRINTF(sc, ATH_DEBUG_RESET, "%s: %s DMA: %u buffers\n", + __func__, name, nbuf); + + dd->dd_name = name; + /* + * This is (mostly) purely for show. We're not allocating any actual + * descriptors here as EDMA RX has the descriptor be part + * of the RX buffer. + * + * However, dd_desc_len is used by ath_descdma_free() to determine + * whether we have already freed this DMA mapping. + */ + dd->dd_desc_len = rx_status_len * nbuf; + dd->dd_descsize = rx_status_len; + + /* allocate rx buffers */ + bsize = sizeof(struct ath_buf) * nbuf; + bf = malloc(bsize, M_ATHDEV, M_NOWAIT | M_ZERO); + if (bf == NULL) { + if_printf(ifp, "malloc of %s buffers failed, size %u\n", + dd->dd_name, bsize); + error = ENOMEM; + goto fail3; + } + dd->dd_bufptr = bf; + + TAILQ_INIT(head); + for (i = 0; i < nbuf; i++, bf++) { + bf->bf_desc = NULL; + bf->bf_daddr = 0; + bf->bf_lastds = NULL; /* Just an initial value */ + + error = bus_dmamap_create(sc->sc_dmat, BUS_DMA_NOWAIT, + &bf->bf_dmamap); + if (error != 0) { + if_printf(ifp, "unable to create dmamap for %s " + "buffer %u, error %u\n", dd->dd_name, i, error); + ath_descdma_cleanup(sc, dd, head); + return error; + } + TAILQ_INSERT_TAIL(head, bf, bf_list); + } + return 0; +fail3: + memset(dd, 0, sizeof(*dd)); + return error; +} + void ath_descdma_cleanup(struct ath_softc *sc, struct ath_descdma *dd, ath_bufhead *head) @@ -2895,32 +3043,39 @@ ath_descdma_cleanup(struct ath_softc *sc, struct ath_buf *bf; struct ieee80211_node *ni; - bus_dmamap_unload(dd->dd_dmat, dd->dd_dmamap); - bus_dmamem_free(dd->dd_dmat, dd->dd_desc, dd->dd_dmamap); - bus_dmamap_destroy(dd->dd_dmat, dd->dd_dmamap); - bus_dma_tag_destroy(dd->dd_dmat); + if (dd->dd_dmamap != 0) { + bus_dmamap_unload(dd->dd_dmat, dd->dd_dmamap); + bus_dmamem_free(dd->dd_dmat, dd->dd_desc, dd->dd_dmamap); + bus_dmamap_destroy(dd->dd_dmat, dd->dd_dmamap); + bus_dma_tag_destroy(dd->dd_dmat); + } - TAILQ_FOREACH(bf, head, bf_list) { - if (bf->bf_m) { - m_freem(bf->bf_m); - bf->bf_m = NULL; - } - if (bf->bf_dmamap != NULL) { - bus_dmamap_destroy(sc->sc_dmat, bf->bf_dmamap); - bf->bf_dmamap = NULL; - } - ni = bf->bf_node; - bf->bf_node = NULL; - if (ni != NULL) { - /* - * Reclaim node reference. - */ - ieee80211_free_node(ni); + if (head != NULL) { + TAILQ_FOREACH(bf, head, bf_list) { + if (bf->bf_m) { + m_freem(bf->bf_m); + bf->bf_m = NULL; + } + if (bf->bf_dmamap != NULL) { + bus_dmamap_destroy(sc->sc_dmat, bf->bf_dmamap); + bf->bf_dmamap = NULL; + } + ni = bf->bf_node; + bf->bf_node = NULL; + if (ni != NULL) { + /* + * Reclaim node reference. + */ + ieee80211_free_node(ni); + } } } - TAILQ_INIT(head); - free(dd->dd_bufptr, M_ATHDEV); + if (head != NULL) + TAILQ_INIT(head); + + if (dd->dd_bufptr != NULL) + free(dd->dd_bufptr, M_ATHDEV); memset(dd, 0, sizeof(*dd)); } @@ -2930,14 +3085,15 @@ ath_desc_alloc(struct ath_softc *sc) int error; error = ath_descdma_setup(sc, &sc->sc_txdma, &sc->sc_txbuf, - "tx", ath_txbuf, ATH_TXDESC); + "tx", sc->sc_tx_desclen, ath_txbuf, ATH_TXDESC); if (error != 0) { return error; } sc->sc_txbuf_cnt = ath_txbuf; error = ath_descdma_setup(sc, &sc->sc_txdma_mgmt, &sc->sc_txbuf_mgmt, - "tx_mgmt", ath_txbuf_mgmt, ATH_TXDESC); + "tx_mgmt", sc->sc_tx_desclen, ath_txbuf_mgmt, + ATH_TXDESC); if (error != 0) { ath_descdma_cleanup(sc, &sc->sc_txdma, &sc->sc_txbuf); return error; @@ -2949,7 +3105,7 @@ ath_desc_alloc(struct ath_softc *sc) */ error = ath_descdma_setup(sc, &sc->sc_bdma, &sc->sc_bbuf, - "beacon", ATH_BCBUF, 1); + "beacon", sc->sc_tx_desclen, ATH_BCBUF, 1); if (error != 0) { ath_descdma_cleanup(sc, &sc->sc_txdma, &sc->sc_txbuf); ath_descdma_cleanup(sc, &sc->sc_txdma_mgmt, @@ -3429,8 +3585,8 @@ ath_tx_update_busy(struct ath_softc *sc) * Kick the packet scheduler if needed. This can occur from this * particular task. */ -static int -ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq, int dosched) +int +ath_legacy_tx_processq(struct ath_softc *sc, struct ath_txq *txq, int dosched) { struct ath_hal *ah = sc->sc_ah; struct ath_buf *bf; @@ -3830,7 +3986,7 @@ ath_tx_freebuf(struct ath_softc *sc, struct ath_buf *bf, int status) } void -ath_tx_draintxq(struct ath_softc *sc, struct ath_txq *txq) +ath_legacy_tx_draintxq(struct ath_softc *sc, struct ath_txq *txq) { #ifdef ATH_DEBUG struct ath_hal *ah = sc->sc_ah; @@ -4861,6 +5017,43 @@ ath_watchdog(void *arg) callout_schedule(&sc->sc_wd_ch, hz); } +/* + * Fetch the rate control statistics for the given node. + */ +static int +ath_ioctl_ratestats(struct ath_softc *sc, struct ath_rateioctl *rs) +{ + struct ath_node *an; + struct ieee80211com *ic = sc->sc_ifp->if_l2com; + struct ieee80211_node *ni; + int error = 0; + + /* Perform a lookup on the given node */ + ni = ieee80211_find_node(&ic->ic_sta, rs->is_u.macaddr); + if (ni == NULL) { + error = EINVAL; + goto bad; + } + + /* Lock the ath_node */ + an = ATH_NODE(ni); + ATH_NODE_LOCK(an); + + /* Fetch the rate control stats for this node */ + error = ath_rate_fetch_node_stats(sc, an, rs); + + /* No matter what happens here, just drop through */ + + /* Unlock the ath_node */ + ATH_NODE_UNLOCK(an); + + /* Unref the node */ + ieee80211_node_decref(ni); + +bad: + return (error); +} + #ifdef ATH_DIAGAPI /* * Diagnostic interface to the HAL. This is used by various @@ -5009,6 +5202,9 @@ ath_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) error = ath_ioctl_phyerr(sc,(struct ath_diag*) ifr); break; #endif + case SIOCGATHNODERATESTATS: + error = ath_ioctl_ratestats(sc, (struct ath_rateioctl *) ifr); + break; case SIOCGIFADDR: error = ether_ioctl(ifp, cmd, data); break; diff --git a/sys/dev/ath/if_ath_ahb.c b/sys/dev/ath/if_ath_ahb.c index a5bb413..1d8d7ac 100644 --- a/sys/dev/ath/if_ath_ahb.c +++ b/sys/dev/ath/if_ath_ahb.c @@ -193,11 +193,15 @@ ath_ahb_attach(device_t dev) ATH_LOCK_INIT(sc); ATH_PCU_LOCK_INIT(sc); + ATH_RX_LOCK_INIT(sc); + ATH_TXSTATUS_LOCK_INIT(sc); error = ath_attach(AR9130_DEVID, sc); if (error == 0) /* success */ return 0; + ATH_TXSTATUS_LOCK_DESTROY(sc); + ATH_RX_LOCK_DESTROY(sc); ATH_PCU_LOCK_DESTROY(sc); ATH_LOCK_DESTROY(sc); bus_dma_tag_destroy(sc->sc_dmat); @@ -238,6 +242,8 @@ ath_ahb_detach(device_t dev) if (sc->sc_eepromdata) free(sc->sc_eepromdata, M_TEMP); + ATH_TXSTATUS_LOCK_DESTROY(sc); + ATH_RX_LOCK_DESTROY(sc); ATH_PCU_LOCK_DESTROY(sc); ATH_LOCK_DESTROY(sc); diff --git a/sys/dev/ath/if_ath_beacon.c b/sys/dev/ath/if_ath_beacon.c index c4b159a..4ebdcf0 100644 --- a/sys/dev/ath/if_ath_beacon.c +++ b/sys/dev/ath/if_ath_beacon.c @@ -277,14 +277,15 @@ ath_beacon_setup(struct ath_softc *sc, struct ath_buf *bf) flags = HAL_TXDESC_NOACK; if (ic->ic_opmode == IEEE80211_M_IBSS && sc->sc_hasveol) { - ds->ds_link = bf->bf_daddr; /* self-linked */ + /* self-linked descriptor */ + ath_hal_settxdesclink(sc->sc_ah, ds, bf->bf_daddr); flags |= HAL_TXDESC_VEOL; /* * Let hardware handle antenna switching. */ antenna = sc->sc_txantenna; } else { - ds->ds_link = 0; + ath_hal_settxdesclink(sc->sc_ah, ds, 0); /* * Switch antenna every 4 beacons. * XXX assumes two antenna @@ -405,8 +406,10 @@ ath_beacon_proc(void *arg, int pending) if (vap != NULL && vap->iv_state >= IEEE80211_S_RUN) { bf = ath_beacon_generate(sc, vap); if (bf != NULL) { + /* XXX should do this using the ds */ *bflink = bf->bf_daddr; - bflink = &bf->bf_desc->ds_link; + ath_hal_gettxdesclinkptr(sc->sc_ah, + bf->bf_desc, &bflink); } } } diff --git a/sys/dev/ath/if_ath_misc.h b/sys/dev/ath/if_ath_misc.h index 0a51fee..d34c72c 100644 --- a/sys/dev/ath/if_ath_misc.h +++ b/sys/dev/ath/if_ath_misc.h @@ -66,7 +66,6 @@ extern void ath_returnbuf_head(struct ath_softc *sc, struct ath_buf *bf); extern void ath_returnbuf_tail(struct ath_softc *sc, struct ath_buf *bf); extern int ath_reset(struct ifnet *, ATH_RESET_TYPE); -extern void ath_tx_draintxq(struct ath_softc *sc, struct ath_txq *txq); extern void ath_tx_default_comp(struct ath_softc *sc, struct ath_buf *bf, int fail); extern void ath_tx_update_ratectrl(struct ath_softc *sc, @@ -84,11 +83,23 @@ extern void ath_setdefantenna(struct ath_softc *sc, u_int antenna); extern void ath_setslottime(struct ath_softc *sc); +extern int ath_descdma_alloc_desc(struct ath_softc *sc, + struct ath_descdma *dd, ath_bufhead *head, const char *name, + int ds_size, int ndesc); extern int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd, - ath_bufhead *head, const char *name, int nbuf, int ndesc); + ath_bufhead *head, const char *name, int ds_size, int nbuf, + int ndesc); +extern int ath_descdma_setup_rx_edma(struct ath_softc *sc, + struct ath_descdma *dd, ath_bufhead *head, const char *name, + int nbuf, int desclen); extern void ath_descdma_cleanup(struct ath_softc *sc, struct ath_descdma *dd, ath_bufhead *head); +extern void ath_legacy_attach_comp_func(struct ath_softc *sc); +extern void ath_legacy_tx_draintxq(struct ath_softc *sc, struct ath_txq *txq); +extern int ath_legacy_tx_processq(struct ath_softc *sc, struct ath_txq *txq, + int dosched); + /* * This is only here so that the RX proc function can call it. * It's very likely that the "start TX after RX" call should be diff --git a/sys/dev/ath/if_ath_pci.c b/sys/dev/ath/if_ath_pci.c index 773b79d..5973dc3 100644 --- a/sys/dev/ath/if_ath_pci.c +++ b/sys/dev/ath/if_ath_pci.c @@ -249,12 +249,16 @@ ath_pci_attach(device_t dev) ATH_LOCK_INIT(sc); ATH_PCU_LOCK_INIT(sc); + ATH_RX_LOCK_INIT(sc); + ATH_TXSTATUS_LOCK_INIT(sc); error = ath_attach(pci_get_device(dev), sc); if (error == 0) /* success */ return 0; + ATH_TXSTATUS_LOCK_DESTROY(sc); ATH_PCU_LOCK_DESTROY(sc); + ATH_RX_LOCK_DESTROY(sc); ATH_LOCK_DESTROY(sc); bus_dma_tag_destroy(sc->sc_dmat); bad3: @@ -293,7 +297,9 @@ ath_pci_detach(device_t dev) if (sc->sc_eepromdata) free(sc->sc_eepromdata, M_TEMP); + ATH_TXSTATUS_LOCK_DESTROY(sc); ATH_PCU_LOCK_DESTROY(sc); + ATH_RX_LOCK_DESTROY(sc); ATH_LOCK_DESTROY(sc); return (0); diff --git a/sys/dev/ath/if_ath_rx.c b/sys/dev/ath/if_ath_rx.c index eba6ba5..4027bfe 100644 --- a/sys/dev/ath/if_ath_rx.c +++ b/sys/dev/ath/if_ath_rx.c @@ -534,6 +534,14 @@ ath_rx_pkt(struct ath_softc *sc, struct ath_rx_status *rs, HAL_STATUS status, goto rx_accept; sc->sc_stats.ast_rx_badcrypt++; } + /* + * Similar as above - if the failure was a keymiss + * just punt it up to the upper layers for now. + */ + if (rs->rs_status & HAL_RXERR_KEYMISS) { + sc->sc_stats.ast_rx_keymiss++; + goto rx_accept; + } if (rs->rs_status & HAL_RXERR_MIC) { sc->sc_stats.ast_rx_badmic++; /* @@ -1067,7 +1075,7 @@ ath_legacy_dma_rxsetup(struct ath_softc *sc) device_printf(sc->sc_dev, "%s: called\n", __func__); error = ath_descdma_setup(sc, &sc->sc_rxdma, &sc->sc_rxbuf, - "rx", ath_rxbuf, 1); + "rx", sizeof(struct ath_desc), ath_rxbuf, 1); if (error != 0) return (error); @@ -1091,6 +1099,9 @@ ath_recv_setup_legacy(struct ath_softc *sc) device_printf(sc->sc_dev, "DMA setup: legacy\n"); + /* Sensible legacy defaults */ + sc->sc_rx_statuslen = 0; + sc->sc_rx.recv_start = ath_legacy_startrecv; sc->sc_rx.recv_stop = ath_legacy_stoprecv; sc->sc_rx.recv_flush = ath_legacy_flushrecv; diff --git a/sys/dev/ath/if_ath_rx_edma.c b/sys/dev/ath/if_ath_rx_edma.c index 9e3580d..b6f7dfd 100644 --- a/sys/dev/ath/if_ath_rx_edma.c +++ b/sys/dev/ath/if_ath_rx_edma.c @@ -156,6 +156,7 @@ ath_edma_stoprecv(struct ath_softc *sc, int dodelay) { struct ath_hal *ah = sc->sc_ah; + ATH_RX_LOCK(sc); ath_hal_stoppcurecv(ah); ath_hal_setrxfilter(ah, 0); ath_hal_stopdmarecv(ah); @@ -173,6 +174,7 @@ ath_edma_stoprecv(struct ath_softc *sc, int dodelay) m_freem(sc->sc_rxedma[HAL_RX_QUEUE_LP].m_rxpending); sc->sc_rxedma[HAL_RX_QUEUE_LP].m_rxpending = NULL; } + ATH_RX_UNLOCK(sc); } /* @@ -187,6 +189,8 @@ ath_edma_reinit_fifo(struct ath_softc *sc, HAL_RX_QUEUE qtype) struct ath_buf *bf; int i, j; + ATH_RX_LOCK_ASSERT(sc); + i = re->m_fifo_head; for (j = 0; j < re->m_fifo_depth; j++) { bf = re->m_fifo[i]; @@ -221,6 +225,8 @@ ath_edma_startrecv(struct ath_softc *sc) { struct ath_hal *ah = sc->sc_ah; + ATH_RX_LOCK(sc); + /* Enable RX FIFO */ ath_hal_rxena(ah); @@ -266,6 +272,9 @@ ath_edma_startrecv(struct ath_softc *sc) ath_mode_init(sc); ath_hal_startpcurecv(ah); + + ATH_RX_UNLOCK(sc); + return (0); } @@ -275,8 +284,16 @@ ath_edma_recv_flush(struct ath_softc *sc) device_printf(sc->sc_dev, "%s: called\n", __func__); + ATH_PCU_LOCK(sc); + sc->sc_rxproc_cnt++; + ATH_PCU_UNLOCK(sc); + ath_edma_recv_proc_queue(sc, HAL_RX_QUEUE_HP, 0); ath_edma_recv_proc_queue(sc, HAL_RX_QUEUE_LP, 0); + + ATH_PCU_LOCK(sc); + sc->sc_rxproc_cnt--; + ATH_PCU_UNLOCK(sc); } /* @@ -300,16 +317,21 @@ ath_edma_recv_proc_queue(struct ath_softc *sc, HAL_RX_QUEUE qtype, struct ath_desc *ds; struct ath_buf *bf; struct mbuf *m; - HAL_STATUS status; struct ath_hal *ah = sc->sc_ah; uint64_t tsf; int16_t nf; - int ngood = 0; + int ngood = 0, npkts = 0; + ath_bufhead rxlist; + struct ath_buf *next; + + TAILQ_INIT(&rxlist); tsf = ath_hal_gettsf64(ah); nf = ath_hal_getchannoise(ah, sc->sc_curchan); sc->sc_stats.ast_rx_noise = nf; + ATH_RX_LOCK(sc); + do { bf = re->m_fifo[re->m_fifo_head]; /* This shouldn't occur! */ @@ -330,12 +352,13 @@ ath_edma_recv_proc_queue(struct ath_softc *sc, HAL_RX_QUEUE qtype, bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_POSTREAD); rs = &bf->bf_status.ds_rxstat; - status = ath_hal_rxprocdesc(ah, ds, bf->bf_daddr, NULL, rs); + bf->bf_rxstatus = ath_hal_rxprocdesc(ah, ds, bf->bf_daddr, + NULL, rs); #ifdef ATH_DEBUG if (sc->sc_debug & ATH_DEBUG_RECV_DESC) - ath_printrxbuf(sc, bf, 0, status == HAL_OK); + ath_printrxbuf(sc, bf, 0, bf->bf_rxstatus == HAL_OK); #endif - if (status == HAL_EINPROGRESS) + if (bf->bf_rxstatus == HAL_EINPROGRESS) break; /* @@ -347,36 +370,67 @@ ath_edma_recv_proc_queue(struct ath_softc *sc, HAL_RX_QUEUE qtype, */ DPRINTF(sc, ATH_DEBUG_EDMA_RX, "%s: Q%d: completed!\n", __func__, qtype); + npkts++; /* - * Remove the FIFO entry! + * Remove the FIFO entry and place it on the completion + * queue. */ re->m_fifo[re->m_fifo_head] = NULL; + TAILQ_INSERT_TAIL(&rxlist, bf, bf_list); + /* Bump the descriptor FIFO stats */ + INCR(re->m_fifo_head, re->m_fifolen); + re->m_fifo_depth--; + /* XXX check it doesn't fall below 0 */ + } while (re->m_fifo_depth > 0); + + /* Append some more fresh frames to the FIFO */ + if (dosched) + ath_edma_rxfifo_alloc(sc, qtype, re->m_fifolen); + + ATH_RX_UNLOCK(sc); + + /* Handle the completed descriptors */ + TAILQ_FOREACH_SAFE(bf, &rxlist, bf_list, next) { /* * Skip the RX descriptor status - start at the data offset */ - m_adj(m, sc->sc_rx_statuslen); + m_adj(bf->bf_m, sc->sc_rx_statuslen); /* Handle the frame */ - if (ath_rx_pkt(sc, rs, status, tsf, nf, qtype, bf)) + /* + * Note: this may or may not free bf->bf_m and sync/unmap + * the frame. + */ + rs = &bf->bf_status.ds_rxstat; + if (ath_rx_pkt(sc, rs, bf->bf_rxstatus, tsf, nf, qtype, bf)) ngood++; + } + /* Free in one set, inside the lock */ + ATH_RX_LOCK(sc); + TAILQ_FOREACH_SAFE(bf, &rxlist, bf_list, next) { /* Free the buffer/mbuf */ ath_edma_rxbuf_free(sc, bf); + } + ATH_RX_UNLOCK(sc); - /* Bump the descriptor FIFO stats */ - INCR(re->m_fifo_head, re->m_fifolen); - re->m_fifo_depth--; - /* XXX check it doesn't fall below 0 */ - } while (re->m_fifo_depth > 0); + /* rx signal state monitoring */ + ath_hal_rxmonitor(ah, &sc->sc_halstats, sc->sc_curchan); + if (ngood) + sc->sc_lastrx = tsf; + + CTR2(ATH_KTR_INTR, "ath edma rx proc: npkts=%d, ngood=%d", + npkts, ngood); /* Handle resched and kickpcu appropriately */ ATH_PCU_LOCK(sc); if (dosched && sc->sc_kickpcu) { CTR0(ATH_KTR_ERR, "ath_edma_recv_proc_queue(): kickpcu"); - device_printf(sc->sc_dev, "%s: handled %d descriptors\n", - __func__, ngood); + device_printf(sc->sc_dev, + "%s: handled npkts %d ngood %d\n", + __func__, npkts, ngood); /* * XXX TODO: what should occur here? Just re-poke and @@ -386,10 +440,6 @@ ath_edma_recv_proc_queue(struct ath_softc *sc, HAL_RX_QUEUE qtype, } ATH_PCU_UNLOCK(sc); - /* Append some more fresh frames to the FIFO */ - if (dosched) - ath_edma_rxfifo_alloc(sc, qtype, re->m_fifolen); - return (ngood); } @@ -397,6 +447,10 @@ static void ath_edma_recv_tasklet(void *arg, int npending) { struct ath_softc *sc = (struct ath_softc *) arg; + struct ifnet *ifp = sc->sc_ifp; +#ifdef IEEE80211_SUPPORT_SUPERG + struct ieee80211com *ic = ifp->if_l2com; +#endif DPRINTF(sc, ATH_DEBUG_EDMA_RX, "%s: called; npending=%d\n", __func__, @@ -409,10 +463,26 @@ ath_edma_recv_tasklet(void *arg, int npending) ATH_PCU_UNLOCK(sc); return; } + sc->sc_rxproc_cnt++; ATH_PCU_UNLOCK(sc); ath_edma_recv_proc_queue(sc, HAL_RX_QUEUE_HP, 1); ath_edma_recv_proc_queue(sc, HAL_RX_QUEUE_LP, 1); + + /* XXX inside IF_LOCK ? */ + if ((ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) { +#ifdef IEEE80211_SUPPORT_SUPERG + ieee80211_ff_age_all(ic, 100); +#endif + if (! IFQ_IS_EMPTY(&ifp->if_snd)) + ath_tx_kick(sc); + } + if (ath_dfs_tasklet_needed(sc, sc->sc_curchan)) + taskqueue_enqueue(sc->sc_tq, &sc->sc_dfstask); + + ATH_PCU_LOCK(sc); + sc->sc_rxproc_cnt--; + ATH_PCU_UNLOCK(sc); } /* @@ -435,6 +505,8 @@ ath_edma_rxbuf_init(struct ath_softc *sc, struct ath_buf *bf) int error; int len; + ATH_RX_LOCK_ASSERT(sc); + // device_printf(sc->sc_dev, "%s: called; bf=%p\n", __func__, bf); m = m_getm(NULL, sc->sc_edma_bufsize, M_DONTWAIT, MT_DATA); @@ -498,6 +570,8 @@ ath_edma_rxbuf_alloc(struct ath_softc *sc) struct ath_buf *bf; int error; + ATH_RX_LOCK_ASSERT(sc); + /* Allocate buffer */ bf = TAILQ_FIRST(&sc->sc_rxbuf); /* XXX shouldn't happen upon startup? */ @@ -526,6 +600,9 @@ static void ath_edma_rxbuf_free(struct ath_softc *sc, struct ath_buf *bf) { + ATH_RX_LOCK_ASSERT(sc); + + /* We're doing this multiple times? */ bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); if (bf->bf_m) { @@ -550,6 +627,8 @@ ath_edma_rxfifo_alloc(struct ath_softc *sc, HAL_RX_QUEUE qtype, int nbufs) struct ath_buf *bf; int i; + ATH_RX_LOCK_ASSERT(sc); + /* * Allocate buffers until the FIFO is full or nbufs is reached. */ @@ -616,6 +695,8 @@ ath_edma_rxfifo_flush(struct ath_softc *sc, HAL_RX_QUEUE qtype) struct ath_rx_edma *re = &sc->sc_rxedma[qtype]; int i; + ATH_RX_LOCK_ASSERT(sc); + for (i = 0; i < re->m_fifolen; i++) { if (re->m_fifo[i] != NULL) { #ifdef ATH_DEBUG @@ -647,6 +728,8 @@ ath_edma_setup_rxfifo(struct ath_softc *sc, HAL_RX_QUEUE qtype) { struct ath_rx_edma *re = &sc->sc_rxedma[qtype]; + ATH_RX_LOCK_ASSERT(sc); + if (! ath_hal_getrxfifodepth(sc->sc_ah, qtype, &re->m_fifolen)) { device_printf(sc->sc_dev, "%s: qtype=%d, failed\n", __func__, @@ -696,16 +779,18 @@ ath_edma_dma_rxsetup(struct ath_softc *sc) { int error; - /* Create RX DMA tag */ - /* Create RX ath_buf array */ - - error = ath_descdma_setup(sc, &sc->sc_rxdma, &sc->sc_rxbuf, - "rx", ath_rxbuf, 1); + /* + * Create RX DMA tag and buffers. + */ + error = ath_descdma_setup_rx_edma(sc, &sc->sc_rxdma, &sc->sc_rxbuf, + "rx", ath_rxbuf, sc->sc_rx_statuslen); if (error != 0) return error; + ATH_RX_LOCK(sc); (void) ath_edma_setup_rxfifo(sc, HAL_RX_QUEUE_HP); (void) ath_edma_setup_rxfifo(sc, HAL_RX_QUEUE_LP); + ATH_RX_UNLOCK(sc); return (0); } @@ -716,11 +801,13 @@ ath_edma_dma_rxteardown(struct ath_softc *sc) device_printf(sc->sc_dev, "%s: called\n", __func__); + ATH_RX_LOCK(sc); ath_edma_rxfifo_flush(sc, HAL_RX_QUEUE_HP); ath_edma_rxfifo_free(sc, HAL_RX_QUEUE_HP); ath_edma_rxfifo_flush(sc, HAL_RX_QUEUE_LP); ath_edma_rxfifo_free(sc, HAL_RX_QUEUE_LP); + ATH_RX_UNLOCK(sc); /* Free RX ath_buf */ /* Free RX DMA tag */ @@ -739,25 +826,17 @@ ath_recv_setup_edma(struct ath_softc *sc) /* Set buffer size to 4k */ sc->sc_edma_bufsize = 4096; - /* Configure the hardware with this */ - (void) ath_hal_setrxbufsize(sc->sc_ah, sc->sc_edma_bufsize); - /* Fetch EDMA field and buffer sizes */ (void) ath_hal_getrxstatuslen(sc->sc_ah, &sc->sc_rx_statuslen); - (void) ath_hal_gettxdesclen(sc->sc_ah, &sc->sc_tx_desclen); - (void) ath_hal_gettxstatuslen(sc->sc_ah, &sc->sc_tx_statuslen); - (void) ath_hal_getntxmaps(sc->sc_ah, &sc->sc_tx_nmaps); + + /* Configure the hardware with the RX buffer size */ + (void) ath_hal_setrxbufsize(sc->sc_ah, sc->sc_edma_bufsize - + sc->sc_rx_statuslen); device_printf(sc->sc_dev, "RX status length: %d\n", sc->sc_rx_statuslen); - device_printf(sc->sc_dev, "TX descriptor length: %d\n", - sc->sc_tx_desclen); - device_printf(sc->sc_dev, "TX status length: %d\n", - sc->sc_tx_statuslen); - device_printf(sc->sc_dev, "TX/RX buffer size: %d\n", + device_printf(sc->sc_dev, "RX buffer size: %d\n", sc->sc_edma_bufsize); - device_printf(sc->sc_dev, "TX buffers per descriptor: %d\n", - sc->sc_tx_nmaps); sc->sc_rx.recv_stop = ath_edma_stoprecv; sc->sc_rx.recv_start = ath_edma_startrecv; diff --git a/sys/dev/ath/if_ath_sysctl.c b/sys/dev/ath/if_ath_sysctl.c index ff75888..e1e9426 100644 --- a/sys/dev/ath/if_ath_sysctl.c +++ b/sys/dev/ath/if_ath_sysctl.c @@ -934,6 +934,8 @@ ath_sysctl_stats_attach(struct ath_softc *sc) SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_mcastq_overflow", CTLFLAG_RD, &sc->sc_stats.ast_tx_mcastq_overflow, 0, "Number of multicast frames exceeding maximum mcast queue depth"); + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_keymiss", CTLFLAG_RD, + &sc->sc_stats.ast_rx_keymiss, 0, ""); /* Attach the RX phy error array */ ath_sysctl_stats_attach_rxphyerr(sc, child); diff --git a/sys/dev/ath/if_ath_tx.c b/sys/dev/ath/if_ath_tx.c index 934f9dc..699478a 100644 --- a/sys/dev/ath/if_ath_tx.c +++ b/sys/dev/ath/if_ath_tx.c @@ -302,6 +302,11 @@ ath_tx_chaindesclist(struct ath_softc *sc, struct ath_buf *bf) struct ath_hal *ah = sc->sc_ah; struct ath_desc *ds, *ds0; int i; + /* + * XXX There's txdma and txdma_mgmt; the descriptor + * sizes must match. + */ + struct ath_descdma *dd = &sc->sc_txdma; /* * Fillin the remainder of the descriptor info. @@ -310,9 +315,10 @@ ath_tx_chaindesclist(struct ath_softc *sc, struct ath_buf *bf) for (i = 0; i < bf->bf_nseg; i++, ds++) { ds->ds_data = bf->bf_segs[i].ds_addr; if (i == bf->bf_nseg - 1) - ds->ds_link = 0; + ath_hal_settxdesclink(ah, ds, 0); else - ds->ds_link = bf->bf_daddr + sizeof(*ds) * (i + 1); + ath_hal_settxdesclink(ah, ds, + bf->bf_daddr + dd->dd_descsize * (i + 1)); ath_hal_filltxdesc(ah, ds , bf->bf_segs[i].ds_len /* segment length */ , i == 0 /* first segment */ @@ -340,6 +346,11 @@ ath_tx_chaindesclist_subframe(struct ath_softc *sc, struct ath_buf *bf) struct ath_hal *ah = sc->sc_ah; struct ath_desc *ds, *ds0; int i; + /* + * XXX There's txdma and txdma_mgmt; the descriptor + * sizes must match. + */ + struct ath_descdma *dd = &sc->sc_txdma; ds0 = ds = bf->bf_desc; @@ -350,9 +361,10 @@ ath_tx_chaindesclist_subframe(struct ath_softc *sc, struct ath_buf *bf) for (i = 0; i < bf->bf_nseg; i++, ds++) { ds->ds_data = bf->bf_segs[i].ds_addr; if (i == bf->bf_nseg - 1) - ds->ds_link = 0; + ath_hal_settxdesclink(ah, ds, 0); else - ds->ds_link = bf->bf_daddr + sizeof(*ds) * (i + 1); + ath_hal_settxdesclink(ah, ds, + bf->bf_daddr + dd->dd_descsize * (i + 1)); /* * This performs the setup for an aggregate frame. @@ -382,6 +394,50 @@ ath_tx_chaindesclist_subframe(struct ath_softc *sc, struct ath_buf *bf) } /* + * Set the rate control fields in the given descriptor based on + * the bf_state fields and node state. + * + * The bfs fields should already be set with the relevant rate + * control information, including whether MRR is to be enabled. + * + * Since the FreeBSD HAL currently sets up the first TX rate + * in ath_hal_setuptxdesc(), this will setup the MRR + * conditionally for the pre-11n chips, and call ath_buf_set_rate + * unconditionally for 11n chips. These require the 11n rate + * scenario to be set if MCS rates are enabled, so it's easier + * to just always call it. The caller can then only set rates 2, 3 + * and 4 if multi-rate retry is needed. + */ +static void +ath_tx_set_ratectrl(struct ath_softc *sc, struct ieee80211_node *ni, + struct ath_buf *bf) +{ + struct ath_rc_series *rc = bf->bf_state.bfs_rc; + + /* If mrr is disabled, blank tries 1, 2, 3 */ + if (! bf->bf_state.bfs_ismrr) + rc[1].tries = rc[2].tries = rc[3].tries = 0; + + /* + * Always call - that way a retried descriptor will + * have the MRR fields overwritten. + * + * XXX TODO: see if this is really needed - setting up + * the first descriptor should set the MRR fields to 0 + * for us anyway. + */ + if (ath_tx_is_11n(sc)) { + ath_buf_set_rate(sc, ni, bf); + } else { + ath_hal_setupxtxdesc(sc->sc_ah, bf->bf_desc + , rc[1].ratecode, rc[1].tries + , rc[2].ratecode, rc[2].tries + , rc[3].ratecode, rc[3].tries + ); + } +} + +/* * Setup segments+descriptors for an 11n aggregate. * bf_first is the first buffer in the aggregate. * The descriptor list must already been linked together using @@ -414,7 +470,8 @@ ath_tx_setds_11n(struct ath_softc *sc, struct ath_buf *bf_first) * to the beginning descriptor of this frame. */ if (bf_prev != NULL) - bf_prev->bf_lastds->ds_link = bf->bf_daddr; + ath_hal_settxdesclink(sc->sc_ah, bf_prev->bf_lastds, + bf->bf_daddr); /* Save a copy so we can link the next descriptor in */ bf_prev = bf; @@ -439,13 +496,6 @@ ath_tx_setds_11n(struct ath_softc *sc, struct ath_buf *bf_first) bf_first->bf_state.bfs_ctsduration); /* - * Setup the last descriptor in the list. - * bf_prev points to the last; bf is NULL here. - */ - ath_hal_setuplasttxdesc(sc->sc_ah, bf_prev->bf_desc, - bf_first->bf_desc); - - /* * Set the first descriptor bf_lastds field to point to * the last descriptor in the last subframe, that's where * the status update will occur. @@ -458,6 +508,21 @@ ath_tx_setds_11n(struct ath_softc *sc, struct ath_buf *bf_first) */ bf_first->bf_last = bf_prev; + /* + * setup first desc with rate and aggr info + */ + ath_tx_set_ratectrl(sc, bf_first->bf_node, bf_first); + + /* + * Setup the last descriptor in the list. + * + * bf_first->bf_lastds already points to it; the rate + * control information needs to be squirreled away here + * as well ans clearing the moreaggr/paddelim fields. + */ + ath_hal_setuplasttxdesc(sc->sc_ah, bf_first->bf_lastds, + bf_first->bf_desc); + DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: end\n", __func__); } @@ -482,7 +547,7 @@ ath_tx_handoff_mcast(struct ath_softc *sc, struct ath_txq *txq, *txq->axq_link = bf->bf_daddr; } ATH_TXQ_INSERT_TAIL(txq, bf, bf_list); - txq->axq_link = &bf->bf_lastds->ds_link; + ath_hal_gettxdesclinkptr(sc->sc_ah, bf->bf_lastds, &txq->axq_link); } /* @@ -616,7 +681,7 @@ ath_tx_handoff_hw(struct ath_softc *sc, struct ath_txq *txq, #endif /* IEEE80211_SUPPORT_TDMA */ if (bf->bf_state.bfs_aggr) txq->axq_aggr_depth++; - txq->axq_link = &bf->bf_lastds->ds_link; + ath_hal_gettxdesclinkptr(ah, bf->bf_lastds, &txq->axq_link); ath_hal_txstart(ah, txq->axq_qnum); } } @@ -626,8 +691,8 @@ ath_tx_handoff_hw(struct ath_softc *sc, struct ath_txq *txq, * * This must be called whether the queue is empty or not. */ -void -ath_txq_restart_dma(struct ath_softc *sc, struct ath_txq *txq) +static void +ath_legacy_tx_dma_restart(struct ath_softc *sc, struct ath_txq *txq) { struct ath_hal *ah = sc->sc_ah; struct ath_buf *bf, *bf_last; @@ -645,7 +710,7 @@ ath_txq_restart_dma(struct ath_softc *sc, struct ath_txq *txq) return; ath_hal_puttxbuf(ah, txq->axq_qnum, bf->bf_daddr); - txq->axq_link = &bf_last->bf_lastds->ds_link; + ath_hal_gettxdesclinkptr(ah, bf->bf_lastds, &txq->axq_link); ath_hal_txstart(ah, txq->axq_qnum); } @@ -655,7 +720,8 @@ ath_txq_restart_dma(struct ath_softc *sc, struct ath_txq *txq) * The relevant hardware txq should be locked. */ static void -ath_tx_handoff(struct ath_softc *sc, struct ath_txq *txq, struct ath_buf *bf) +ath_legacy_xmit_handoff(struct ath_softc *sc, struct ath_txq *txq, + struct ath_buf *bf) { ATH_TXQ_LOCK_ASSERT(txq); @@ -988,11 +1054,12 @@ ath_tx_set_rtscts(struct ath_softc *sc, struct ath_buf *bf) /* * Must disable multi-rate retry when using RTS/CTS. - * XXX TODO: only for pre-11n NICs. */ - bf->bf_state.bfs_ismrr = 0; - bf->bf_state.bfs_try0 = - bf->bf_state.bfs_rc[0].tries = ATH_TXMGTTRY; /* XXX ew */ + if (!sc->sc_mrrprot) { + bf->bf_state.bfs_ismrr = 0; + bf->bf_state.bfs_try0 = + bf->bf_state.bfs_rc[0].tries = ATH_TXMGTTRY; /* XXX ew */ + } } /* @@ -1025,7 +1092,9 @@ ath_tx_setds(struct ath_softc *sc, struct ath_buf *bf) bf->bf_lastds = ds; bf->bf_last = bf; - /* XXX TODO: Setup descriptor chain */ + /* Set rate control and descriptor chain for this frame */ + ath_tx_set_ratectrl(sc, bf->bf_node, bf); + ath_tx_chaindesclist(sc, bf); } /* @@ -1074,50 +1143,6 @@ ath_tx_do_ratelookup(struct ath_softc *sc, struct ath_buf *bf) } /* - * Set the rate control fields in the given descriptor based on - * the bf_state fields and node state. - * - * The bfs fields should already be set with the relevant rate - * control information, including whether MRR is to be enabled. - * - * Since the FreeBSD HAL currently sets up the first TX rate - * in ath_hal_setuptxdesc(), this will setup the MRR - * conditionally for the pre-11n chips, and call ath_buf_set_rate - * unconditionally for 11n chips. These require the 11n rate - * scenario to be set if MCS rates are enabled, so it's easier - * to just always call it. The caller can then only set rates 2, 3 - * and 4 if multi-rate retry is needed. - */ -static void -ath_tx_set_ratectrl(struct ath_softc *sc, struct ieee80211_node *ni, - struct ath_buf *bf) -{ - struct ath_rc_series *rc = bf->bf_state.bfs_rc; - - /* If mrr is disabled, blank tries 1, 2, 3 */ - if (! bf->bf_state.bfs_ismrr) - rc[1].tries = rc[2].tries = rc[3].tries = 0; - - /* - * Always call - that way a retried descriptor will - * have the MRR fields overwritten. - * - * XXX TODO: see if this is really needed - setting up - * the first descriptor should set the MRR fields to 0 - * for us anyway. - */ - if (ath_tx_is_11n(sc)) { - ath_buf_set_rate(sc, ni, bf); - } else { - ath_hal_setupxtxdesc(sc->sc_ah, bf->bf_desc - , rc[1].ratecode, rc[1].tries - , rc[2].ratecode, rc[2].tries - , rc[3].ratecode, rc[3].tries - ); - } -} - -/* * Transmit the given frame to the hardware. * * The frame must already be setup; rate control must already have @@ -1142,8 +1167,6 @@ ath_tx_xmit_normal(struct ath_softc *sc, struct ath_txq *txq, ath_tx_set_rtscts(sc, bf); ath_tx_rate_fill_rcflags(sc, bf); ath_tx_setds(sc, bf); - ath_tx_set_ratectrl(sc, bf->bf_node, bf); - ath_tx_chaindesclist(sc, bf); /* Hand off to hardware */ ath_tx_handoff(sc, txq, bf); @@ -2391,8 +2414,6 @@ ath_tx_xmit_aggr(struct ath_softc *sc, struct ath_node *an, struct ath_buf *bf) ath_tx_set_rtscts(sc, bf); ath_tx_rate_fill_rcflags(sc, bf); ath_tx_setds(sc, bf); - ath_tx_set_ratectrl(sc, bf->bf_node, bf); - ath_tx_chaindesclist(sc, bf); /* Statistics */ sc->sc_aggr_stats.aggr_low_hwq_single_pkt++; @@ -3834,7 +3855,6 @@ ath_tx_tid_hw_queue_aggr(struct ath_softc *sc, struct ath_node *an, struct ath_buf *bf; struct ath_txq *txq = sc->sc_ac2q[tid->ac]; struct ieee80211_tx_ampdu *tap; - struct ieee80211_node *ni = &an->an_node; ATH_AGGR_STATUS status; ath_bufhead bf_q; @@ -3882,9 +3902,7 @@ ath_tx_tid_hw_queue_aggr(struct ath_softc *sc, struct ath_node *an, ath_tx_set_rtscts(sc, bf); ath_tx_rate_fill_rcflags(sc, bf); ath_tx_setds(sc, bf); - ath_tx_chaindesclist(sc, bf); ath_hal_clr11n_aggr(sc->sc_ah, bf->bf_desc); - ath_tx_set_ratectrl(sc, ni, bf); sc->sc_aggr_stats.aggr_nonbaw_pkt++; @@ -3942,9 +3960,7 @@ ath_tx_tid_hw_queue_aggr(struct ath_softc *sc, struct ath_node *an, "%s: single-frame aggregate\n", __func__); bf->bf_state.bfs_aggr = 0; ath_tx_setds(sc, bf); - ath_tx_chaindesclist(sc, bf); ath_hal_clr11n_aggr(sc->sc_ah, bf->bf_desc); - ath_tx_set_ratectrl(sc, ni, bf); if (status == ATH_AGGR_BAW_CLOSED) sc->sc_aggr_stats.aggr_baw_closed_single_pkt++; else @@ -3979,10 +3995,6 @@ ath_tx_tid_hw_queue_aggr(struct ath_softc *sc, struct ath_node *an, */ ath_tx_setds_11n(sc, bf); - /* - * setup first desc with rate and aggr info - */ - ath_tx_set_ratectrl(sc, ni, bf); } queuepkt: //txq = bf->bf_state.bfs_txq; @@ -4022,7 +4034,6 @@ ath_tx_tid_hw_queue_norm(struct ath_softc *sc, struct ath_node *an, { struct ath_buf *bf; struct ath_txq *txq = sc->sc_ac2q[tid->ac]; - struct ieee80211_node *ni = &an->an_node; DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: node %p: TID %d: called\n", __func__, an, tid->tid); @@ -4071,8 +4082,6 @@ ath_tx_tid_hw_queue_norm(struct ath_softc *sc, struct ath_node *an, ath_tx_set_rtscts(sc, bf); ath_tx_rate_fill_rcflags(sc, bf); ath_tx_setds(sc, bf); - ath_tx_chaindesclist(sc, bf); - ath_tx_set_ratectrl(sc, ni, bf); /* Track outstanding buffer count to hardware */ /* aggregates are "one" buffer */ @@ -4450,3 +4459,40 @@ ath_addba_response_timeout(struct ieee80211_node *ni, ath_tx_tid_resume(sc, atid); ATH_TXQ_UNLOCK(sc->sc_ac2q[atid->ac]); } + +static int +ath_legacy_dma_txsetup(struct ath_softc *sc) +{ + + /* nothing new needed */ + return (0); +} + +static int +ath_legacy_dma_txteardown(struct ath_softc *sc) +{ + + /* nothing new needed */ + return (0); +} + +void +ath_xmit_setup_legacy(struct ath_softc *sc) +{ + /* + * For now, just set the descriptor length to sizeof(ath_desc); + * worry about extracting the real length out of the HAL later. + */ + sc->sc_tx_desclen = sizeof(struct ath_desc); + sc->sc_tx_statuslen = 0; + sc->sc_tx_nmaps = 1; /* only one buffer per TX desc */ + + sc->sc_tx.xmit_setup = ath_legacy_dma_txsetup; + sc->sc_tx.xmit_teardown = ath_legacy_dma_txteardown; + sc->sc_tx.xmit_attach_comp_func = ath_legacy_attach_comp_func; + + sc->sc_tx.xmit_dma_restart = ath_legacy_tx_dma_restart; + sc->sc_tx.xmit_handoff = ath_legacy_xmit_handoff; + sc->sc_tx.xmit_processq = ath_legacy_tx_processq; + sc->sc_tx.xmit_drainq = ath_legacy_tx_draintxq; +} diff --git a/sys/dev/ath/if_ath_tx.h b/sys/dev/ath/if_ath_tx.h index 958acf9..4ac4589 100644 --- a/sys/dev/ath/if_ath_tx.h +++ b/sys/dev/ath/if_ath_tx.h @@ -79,7 +79,6 @@ #define BAW_WITHIN(_start, _bawsz, _seqno) \ ((((_seqno) - (_start)) & 4095) < (_bawsz)) -extern void ath_txq_restart_dma(struct ath_softc *sc, struct ath_txq *txq); extern void ath_freetx(struct mbuf *m); extern void ath_tx_node_flush(struct ath_softc *sc, struct ath_node *an); extern void ath_tx_txq_drain(struct ath_softc *sc, struct ath_txq *txq); @@ -124,4 +123,22 @@ extern void ath_bar_response(struct ieee80211_node *ni, extern void ath_addba_response_timeout(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap); +/* + * Setup path + */ +#define ath_txdma_setup(_sc) \ + (_sc)->sc_tx.xmit_setup(_sc) +#define ath_txdma_teardown(_sc) \ + (_sc)->sc_tx.xmit_teardown(_sc) +#define ath_txq_restart_dma(_sc, _txq) \ + (_sc)->sc_tx.xmit_dma_restart((_sc), (_txq)) +#define ath_tx_handoff(_sc, _txq, _bf) \ + (_sc)->sc_tx.xmit_handoff((_sc), (_txq), (_bf)) +#define ath_tx_draintxq(_sc, _txq) \ + (_sc)->sc_tx.xmit_drainq((_sc), (_txq)) +#define ath_tx_processq(_sc, _txq, _dosched) \ + (_sc)->sc_tx.xmit_processq((_sc), (_txq), (_dosched)) + +extern void ath_xmit_setup_legacy(struct ath_softc *sc); + #endif diff --git a/sys/dev/ath/if_ath_tx_edma.c b/sys/dev/ath/if_ath_tx_edma.c new file mode 100644 index 0000000..5991d1c --- /dev/null +++ b/sys/dev/ath/if_ath_tx_edma.c @@ -0,0 +1,311 @@ +/*- + * Copyright (c) 2012 Adrian Chadd <adrian@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any + * redistribution must be conditioned upon including a substantially + * similar Disclaimer requirement for further binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * Driver for the Atheros Wireless LAN controller. + * + * This software is derived from work of Atsushi Onoe; his contribution + * is greatly appreciated. + */ + +#include "opt_inet.h" +#include "opt_ath.h" +/* + * This is needed for register operations which are performed + * by the driver - eg, calls to ath_hal_gettsf32(). + * + * It's also required for any AH_DEBUG checks in here, eg the + * module dependencies. + */ +#include "opt_ah.h" +#include "opt_wlan.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/sysctl.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/errno.h> +#include <sys/callout.h> +#include <sys/bus.h> +#include <sys/endian.h> +#include <sys/kthread.h> +#include <sys/taskqueue.h> +#include <sys/priv.h> +#include <sys/module.h> +#include <sys/ktr.h> +#include <sys/smp.h> /* for mp_ncpus */ + +#include <machine/bus.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_types.h> +#include <net/if_arp.h> +#include <net/ethernet.h> +#include <net/if_llc.h> + +#include <net80211/ieee80211_var.h> +#include <net80211/ieee80211_regdomain.h> +#ifdef IEEE80211_SUPPORT_SUPERG +#include <net80211/ieee80211_superg.h> +#endif +#ifdef IEEE80211_SUPPORT_TDMA +#include <net80211/ieee80211_tdma.h> +#endif + +#include <net/bpf.h> + +#ifdef INET +#include <netinet/in.h> +#include <netinet/if_ether.h> +#endif + +#include <dev/ath/if_athvar.h> +#include <dev/ath/ath_hal/ah_devid.h> /* XXX for softled */ +#include <dev/ath/ath_hal/ah_diagcodes.h> + +#include <dev/ath/if_ath_debug.h> +#include <dev/ath/if_ath_misc.h> +#include <dev/ath/if_ath_tsf.h> +#include <dev/ath/if_ath_tx.h> +#include <dev/ath/if_ath_sysctl.h> +#include <dev/ath/if_ath_led.h> +#include <dev/ath/if_ath_keycache.h> +#include <dev/ath/if_ath_rx.h> +#include <dev/ath/if_ath_beacon.h> +#include <dev/ath/if_athdfs.h> + +#ifdef ATH_TX99_DIAG +#include <dev/ath/ath_tx99/ath_tx99.h> +#endif + +#include <dev/ath/if_ath_tx_edma.h> + +/* + * some general macros + */ +#define INCR(_l, _sz) (_l) ++; (_l) &= ((_sz) - 1) +#define DECR(_l, _sz) (_l) --; (_l) &= ((_sz) - 1) + +/* + * XXX doesn't belong here, and should be tunable + */ +#define ATH_TXSTATUS_RING_SIZE 512 + +MALLOC_DECLARE(M_ATHDEV); + +/* + * Re-initialise the DMA FIFO with the current contents of + * said FIFO. + * + * This should only be called as part of the chip reset path, as it + * assumes the FIFO is currently empty. + * + * TODO: verify that a cold/warm reset does clear the TX FIFO, so + * writing in a partially-filled FIFO will not cause double-entries + * to appear. + */ +static void +ath_edma_dma_restart(struct ath_softc *sc, struct ath_txq *txq) +{ + + device_printf(sc->sc_dev, "%s: called: txq=%p, qnum=%d\n", + __func__, + txq, + txq->axq_qnum); +} + +/* + * Handoff this frame to the hardware. + * + * For the multicast queue, this will treat it as a software queue + * and append it to the list, after updating the MORE_DATA flag + * in the previous frame. The cabq processing code will ensure + * that the queue contents gets transferred over. + * + * For the hardware queues, this will queue a frame to the queue + * like before, then populate the FIFO from that. Since the + * EDMA hardware has 8 FIFO slots per TXQ, this ensures that + * frames such as management frames don't get prematurely dropped. + * + * This does imply that a similar flush-hwq-to-fifoq method will + * need to be called from the processq function, before the + * per-node software scheduler is called. + */ +static void +ath_edma_xmit_handoff(struct ath_softc *sc, struct ath_txq *txq, + struct ath_buf *bf) +{ + + device_printf(sc->sc_dev, "%s: called; bf=%p, txq=%p, qnum=%d\n", + __func__, + bf, + txq, + txq->axq_qnum); + + /* + * XXX For now this is a placeholder; free the buffer + * and inform the stack that the TX failed. + */ + ath_tx_default_comp(sc, bf, 1); +} + +static int +ath_edma_setup_txfifo(struct ath_softc *sc, int qnum) +{ + struct ath_tx_edma_fifo *te = &sc->sc_txedma[qnum]; + + te->m_fifo = malloc(sizeof(struct ath_buf *) * HAL_TXFIFO_DEPTH, + M_ATHDEV, + M_NOWAIT | M_ZERO); + if (te->m_fifo == NULL) { + device_printf(sc->sc_dev, "%s: malloc failed\n", + __func__); + return (-ENOMEM); + } + + /* + * Set initial "empty" state. + */ + te->m_fifo_head = te->m_fifo_tail = te->m_fifo_depth = 0; + + return (0); +} + +static int +ath_edma_free_txfifo(struct ath_softc *sc, int qnum) +{ + struct ath_tx_edma_fifo *te = &sc->sc_txedma[qnum]; + + /* XXX TODO: actually deref the ath_buf entries? */ + free(te->m_fifo, M_ATHDEV); + return (0); +} + +static int +ath_edma_dma_txsetup(struct ath_softc *sc) +{ + int error; + int i; + + error = ath_descdma_alloc_desc(sc, &sc->sc_txsdma, + NULL, "txcomp", sc->sc_tx_statuslen, ATH_TXSTATUS_RING_SIZE); + if (error != 0) + return (error); + + ath_hal_setuptxstatusring(sc->sc_ah, + (void *) sc->sc_txsdma.dd_desc, + sc->sc_txsdma.dd_desc_paddr, + ATH_TXSTATUS_RING_SIZE); + + for (i = 0; i < HAL_NUM_TX_QUEUES; i++) { + ath_edma_setup_txfifo(sc, i); + } + + + return (0); +} + +static int +ath_edma_dma_txteardown(struct ath_softc *sc) +{ + int i; + + for (i = 0; i < HAL_NUM_TX_QUEUES; i++) { + ath_edma_free_txfifo(sc, i); + } + + ath_descdma_cleanup(sc, &sc->sc_txsdma, NULL); + return (0); +} + +static int +ath_edma_tx_processq(struct ath_softc *sc, struct ath_txq *txq, int dosched) +{ + + return (0); +} + +static void +ath_edma_tx_draintxq(struct ath_softc *sc, struct ath_txq *txq) +{ + +} + +static void +ath_edma_tx_proc(void *arg, int npending) +{ + struct ath_softc *sc = (struct ath_softc *) arg; + + device_printf(sc->sc_dev, "%s: called, npending=%d\n", + __func__, npending); +} + +static void +ath_edma_attach_comp_func(struct ath_softc *sc) +{ + + TASK_INIT(&sc->sc_txtask, 0, ath_edma_tx_proc, sc); +} + +void +ath_xmit_setup_edma(struct ath_softc *sc) +{ + + /* Fetch EDMA field and buffer sizes */ + (void) ath_hal_gettxdesclen(sc->sc_ah, &sc->sc_tx_desclen); + (void) ath_hal_gettxstatuslen(sc->sc_ah, &sc->sc_tx_statuslen); + (void) ath_hal_getntxmaps(sc->sc_ah, &sc->sc_tx_nmaps); + + device_printf(sc->sc_dev, "TX descriptor length: %d\n", + sc->sc_tx_desclen); + device_printf(sc->sc_dev, "TX status length: %d\n", + sc->sc_tx_statuslen); + device_printf(sc->sc_dev, "TX buffers per descriptor: %d\n", + sc->sc_tx_nmaps); + + sc->sc_tx.xmit_setup = ath_edma_dma_txsetup; + sc->sc_tx.xmit_teardown = ath_edma_dma_txteardown; + sc->sc_tx.xmit_attach_comp_func = ath_edma_attach_comp_func; + + sc->sc_tx.xmit_dma_restart = ath_edma_dma_restart; + sc->sc_tx.xmit_handoff = ath_edma_xmit_handoff; + sc->sc_tx.xmit_processq = ath_edma_tx_processq; + sc->sc_tx.xmit_drainq = ath_edma_tx_draintxq; +} diff --git a/sys/dev/ath/if_ath_tx_edma.h b/sys/dev/ath/if_ath_tx_edma.h new file mode 100644 index 0000000..d4975a6 --- /dev/null +++ b/sys/dev/ath/if_ath_tx_edma.h @@ -0,0 +1,36 @@ +/*- + * Copyright (c) 2012 Adrian Chadd <adrian@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any + * redistribution must be conditioned upon including a substantially + * similar Disclaimer requirement for further binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGES. + * + * $FreeBSD$ + */ +#ifndef __IF_ATH_TX_EDMA_H__ +#define __IF_ATH_TX_EDMA_H__ + +extern void ath_xmit_setup_edma(struct ath_softc *sc); + +#endif diff --git a/sys/dev/ath/if_ath_tx_ht.c b/sys/dev/ath/if_ath_tx_ht.c index e9ec632..6495a04 100644 --- a/sys/dev/ath/if_ath_tx_ht.c +++ b/sys/dev/ath/if_ath_tx_ht.c @@ -511,6 +511,8 @@ ath_rateseries_setup(struct ath_softc *sc, struct ieee80211_node *ni, series[i].RateFlags |= HAL_RATESERIES_HALFGI; series[i].Rate = rt->info[rc[i].rix].rateCode; + series[i].RateIndex = rc[i].rix; + series[i].tx_power_cap = 0x3f; /* XXX for now */ /* * PktDuration doesn't include slot, ACK, RTS, etc timing - @@ -558,14 +560,12 @@ ath_rateseries_print(struct ath_softc *sc, HAL_11N_RATE_SERIES *series) * This isn't useful for sending beacon frames, which has different needs * wrt what's passed into the rate scenario function. */ - void ath_buf_set_rate(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf) { HAL_11N_RATE_SERIES series[4]; struct ath_desc *ds = bf->bf_desc; - struct ath_desc *lastds = NULL; struct ath_hal *ah = sc->sc_ah; int is_pspoll = (bf->bf_state.bfs_atype == HAL_PKT_TYPE_PSPOLL); int ctsrate = bf->bf_state.bfs_ctsrate; @@ -576,13 +576,6 @@ ath_buf_set_rate(struct ath_softc *sc, struct ieee80211_node *ni, ath_rateseries_setup(sc, ni, bf, series); - /* Enforce AR5416 aggregate limit - can't do RTS w/ an agg frame > 8k */ - - /* Enforce RTS and CTS are mutually exclusive */ - - /* Get a pointer to the last tx descriptor in the list */ - lastds = bf->bf_lastds; - #if 0 printf("pktlen: %d; flags 0x%x\n", pktlen, flags); ath_rateseries_print(sc, series); @@ -600,21 +593,6 @@ ath_buf_set_rate(struct ath_softc *sc, struct ieee80211_node *ni, 4, /* number of series */ flags); - /* Setup the last descriptor in the chain */ - /* - * XXX Why is this done here, and not in the upper layer? - * The rate control code stores a copy of the RC info in - * the last descriptor as well as the first, then uses - * the shadow copy in the last descriptor to see what the RC - * decisions were. I'm not sure why; perhaps earlier hardware - * overwrote the first descriptor contents. - * - * In the 802.11n case, it also clears the moreaggr/delim - * fields. Again, this should be done by the caller of - * ath_buf_set_rate(). - */ - ath_hal_setuplasttxdesc(ah, lastds, ds); - /* Set burst duration */ /* * This is only required when doing 11n burst, not aggregation diff --git a/sys/dev/ath/if_athioctl.h b/sys/dev/ath/if_athioctl.h index 71813ae..514e5a7 100644 --- a/sys/dev/ath/if_athioctl.h +++ b/sys/dev/ath/if_athioctl.h @@ -161,7 +161,9 @@ struct ath_stats { u_int32_t ast_tx_aggr_ok; /* aggregate TX ok */ u_int32_t ast_tx_aggr_fail; /* aggregate TX failed */ u_int32_t ast_tx_mcastq_overflow; /* multicast queue overflow */ - u_int32_t ast_pad[1]; + u_int32_t ast_rx_keymiss; + + u_int32_t ast_pad[16]; }; #define SIOCGATHSTATS _IOWR('i', 137, struct ifreq) @@ -184,6 +186,53 @@ struct ath_diag { #define SIOCGATHDIAG _IOWR('i', 138, struct ath_diag) #define SIOCGATHPHYERR _IOWR('i', 140, struct ath_diag) + +/* + * The rate control ioctl has to support multiple potential rate + * control classes. For now, instead of trying to support an + * abstraction for this in the API, let's just use a TLV + * representation for the payload and let userspace sort it out. + */ +struct ath_rateioctl_tlv { + uint16_t tlv_id; + uint16_t tlv_len; /* length excluding TLV header */ +}; + +/* + * This is purely the six byte MAC address. + */ +#define ATH_RATE_TLV_MACADDR 0xaab0 + +/* + * The rate control modules may decide to push a mapping table + * of rix -> net80211 ratecode as part of the update. + */ +#define ATH_RATE_TLV_RATETABLE_NENTRIES 64 +struct ath_rateioctl_rt { + uint16_t nentries; + uint16_t pad[1]; + uint8_t ratecode[ATH_RATE_TLV_RATETABLE_NENTRIES]; +}; +#define ATH_RATE_TLV_RATETABLE 0xaab1 + +/* + * This is the sample node statistics structure. + * More in ath_rate/sample/sample.h. + */ +#define ATH_RATE_TLV_SAMPLENODE 0xaab2 + +struct ath_rateioctl { + char if_name[IFNAMSIZ]; /* if name */ + union { + uint8_t macaddr[IEEE80211_ADDR_LEN]; + uint64_t pad; + } is_u; + uint32_t len; + caddr_t buf; +}; +#define SIOCGATHNODERATESTATS _IOWR('i', 149, struct ath_rateioctl) +#define SIOCGATHRATESTATS _IOWR('i', 150, struct ath_rateioctl) + /* * Radio capture format. */ diff --git a/sys/dev/ath/if_athrate.h b/sys/dev/ath/if_athrate.h index 10f6040..d07c9ca 100644 --- a/sys/dev/ath/if_athrate.h +++ b/sys/dev/ath/if_athrate.h @@ -150,4 +150,16 @@ struct ath_buf; void ath_rate_tx_complete(struct ath_softc *, struct ath_node *, const struct ath_rc_series *, const struct ath_tx_status *, int pktlen, int nframes, int nbad); + +/* + * Fetch the global rate control statistics. + */ +int ath_rate_fetch_stats(struct ath_softc *sc, struct ath_rateioctl *rs); + +/* + * Fetch the per-node statistics. + */ +int ath_rate_fetch_node_stats(struct ath_softc *sc, struct ath_node *an, + struct ath_rateioctl *rs); + #endif /* _ATH_RATECTRL_H_ */ diff --git a/sys/dev/ath/if_athvar.h b/sys/dev/ath/if_athvar.h index 7370e01..94097ad 100644 --- a/sys/dev/ath/if_athvar.h +++ b/sys/dev/ath/if_athvar.h @@ -189,6 +189,7 @@ struct ath_buf { TAILQ_ENTRY(ath_buf) bf_list; struct ath_buf * bf_next; /* next buffer in the aggregate */ int bf_nseg; + HAL_STATUS bf_rxstatus; uint16_t bf_flags; /* status flags (below) */ struct ath_desc *bf_desc; /* virtual addr of desc */ struct ath_desc_status bf_status; /* tx/rx status */ @@ -276,6 +277,7 @@ typedef TAILQ_HEAD(ath_bufhead_s, ath_buf) ath_bufhead; struct ath_descdma { const char* dd_name; struct ath_desc *dd_desc; /* descriptors */ + int dd_descsize; /* size of single descriptor */ bus_addr_t dd_desc_paddr; /* physical addr of dd_desc */ bus_size_t dd_desc_len; /* size of dd_desc */ bus_dma_segment_t dd_dseg; @@ -395,6 +397,30 @@ struct ath_rx_edma { struct mbuf *m_rxpending; }; +struct ath_tx_edma_fifo { + struct ath_buf **m_fifo; + int m_fifolen; + int m_fifo_head; + int m_fifo_tail; + int m_fifo_depth; +}; + +struct ath_tx_methods { + int (*xmit_setup)(struct ath_softc *sc); + int (*xmit_teardown)(struct ath_softc *sc); + void (*xmit_attach_comp_func)(struct ath_softc *sc); + + void (*xmit_dma_restart)(struct ath_softc *sc, + struct ath_txq *txq); + void (*xmit_handoff)(struct ath_softc *sc, + struct ath_txq *txq, struct ath_buf *bf); + + void (*xmit_drainq)(struct ath_softc *sc, + struct ath_txq *txq); + int (*xmit_processq)(struct ath_softc *sc, + struct ath_txq *txq, int dosched); +}; + struct ath_softc { struct ifnet *sc_ifp; /* interface common */ struct ath_stats sc_stats; /* interface statistics */ @@ -409,7 +435,10 @@ struct ath_softc { uint32_t sc_bssidmask; /* bssid mask */ struct ath_rx_methods sc_rx; - struct ath_rx_edma sc_rxedma[2]; /* HP/LP queues */ + struct ath_rx_edma sc_rxedma[HAL_NUM_RX_QUEUES]; /* HP/LP queues */ + struct ath_tx_methods sc_tx; + struct ath_tx_edma_fifo sc_txedma[HAL_NUM_TX_QUEUES]; + int sc_rx_statuslen; int sc_tx_desclen; int sc_tx_statuslen; @@ -425,6 +454,8 @@ struct ath_softc { struct mtx sc_mtx; /* master lock (recursive) */ struct mtx sc_pcu_mtx; /* PCU access mutex */ char sc_pcu_mtx_name[32]; + struct mtx sc_rx_mtx; /* RX access mutex */ + char sc_rx_mtx_name[32]; struct taskqueue *sc_tq; /* private task queue */ struct ath_hal *sc_ah; /* Atheros HAL */ struct ath_ratectrl *sc_rc; /* tx rate control support */ @@ -432,6 +463,7 @@ struct ath_softc { void (*sc_setdefantenna)(struct ath_softc *, u_int); unsigned int sc_invalid : 1,/* disable hardware accesses */ sc_mrretry : 1,/* multi-rate retry support */ + sc_mrrprot : 1,/* MRR + protection support */ sc_softled : 1,/* enable LED gpio status */ sc_hardled : 1,/* enable MAC LED status */ sc_splitmic : 1,/* split TKIP MIC keys */ @@ -546,6 +578,7 @@ struct ath_softc { int sc_txbuf_cnt; /* how many buffers avail */ struct ath_descdma sc_txdma_mgmt; /* mgmt TX descriptors */ ath_bufhead sc_txbuf_mgmt; /* mgmt transmit buffer */ + struct ath_descdma sc_txsdma; /* EDMA TX status desc's */ struct mtx sc_txbuflock; /* txbuf lock */ char sc_txname[12]; /* e.g. "ath0_buf" */ u_int sc_txqsetup; /* h/w queues setup */ @@ -554,6 +587,11 @@ struct ath_softc { struct ath_txq *sc_ac2q[5]; /* WME AC -> h/w q map */ struct task sc_txtask; /* tx int processing */ struct task sc_txqtask; /* tx proc processing */ + + struct ath_descdma sc_txcompdma; /* TX EDMA completion */ + struct mtx sc_txcomplock; /* TX EDMA completion lock */ + char sc_txcompname[12]; /* eg ath0_txcomp */ + int sc_wd_timer; /* count down for wd timer */ struct callout sc_wd_ch; /* tx watchdog timer */ struct ath_tx_radiotap_header sc_tx_th; @@ -696,6 +734,28 @@ struct ath_softc { #define ATH_PCU_UNLOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_pcu_mtx, \ MA_NOTOWNED) +/* + * The RX lock is primarily a(nother) workaround to ensure that the + * RX FIFO/list isn't modified by various execution paths. + * Even though RX occurs in a single context (the ath taskqueue), the + * RX path can be executed via various reset/channel change paths. + */ +#define ATH_RX_LOCK_INIT(_sc) do {\ + snprintf((_sc)->sc_rx_mtx_name, \ + sizeof((_sc)->sc_rx_mtx_name), \ + "%s RX lock", \ + device_get_nameunit((_sc)->sc_dev)); \ + mtx_init(&(_sc)->sc_rx_mtx, (_sc)->sc_rx_mtx_name, \ + NULL, MTX_DEF); \ + } while (0) +#define ATH_RX_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_rx_mtx) +#define ATH_RX_LOCK(_sc) mtx_lock(&(_sc)->sc_rx_mtx) +#define ATH_RX_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_rx_mtx) +#define ATH_RX_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_rx_mtx, \ + MA_OWNED) +#define ATH_RX_UNLOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_rx_mtx, \ + MA_NOTOWNED) + #define ATH_TXQ_SETUP(sc, i) ((sc)->sc_txqsetup & (1<<i)) #define ATH_TXBUF_LOCK_INIT(_sc) do { \ @@ -709,6 +769,19 @@ struct ath_softc { #define ATH_TXBUF_LOCK_ASSERT(_sc) \ mtx_assert(&(_sc)->sc_txbuflock, MA_OWNED) +#define ATH_TXSTATUS_LOCK_INIT(_sc) do { \ + snprintf((_sc)->sc_txcompname, sizeof((_sc)->sc_txcompname), \ + "%s_buf", \ + device_get_nameunit((_sc)->sc_dev)); \ + mtx_init(&(_sc)->sc_txcomplock, (_sc)->sc_txcompname, NULL, \ + MTX_DEF); \ +} while (0) +#define ATH_TXSTATUS_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_txcomplock) +#define ATH_TXSTATUS_LOCK(_sc) mtx_lock(&(_sc)->sc_txcomplock) +#define ATH_TXSTATUS_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_txcomplock) +#define ATH_TXSTATUS_LOCK_ASSERT(_sc) \ + mtx_assert(&(_sc)->sc_txcomplock, MA_OWNED) + int ath_attach(u_int16_t, struct ath_softc *); int ath_detach(struct ath_softc *); void ath_resume(struct ath_softc *); @@ -1042,6 +1115,15 @@ void ath_intr(void *); ((*(_ah)->ah_getTxIntrQueue)((_ah), (_txqs))) #define ath_hal_gettxcompletionrates(_ah, _ds, _rates, _tries) \ ((*(_ah)->ah_getTxCompletionRates)((_ah), (_ds), (_rates), (_tries))) +#define ath_hal_settxdesclink(_ah, _ds, _link) \ + ((*(_ah)->ah_setTxDescLink)((_ah), (_ds), (_link))) +#define ath_hal_gettxdesclink(_ah, _ds, _link) \ + ((*(_ah)->ah_getTxDescLink)((_ah), (_ds), (_link))) +#define ath_hal_gettxdesclinkptr(_ah, _ds, _linkptr) \ + ((*(_ah)->ah_getTxDescLinkPtr)((_ah), (_ds), (_linkptr))) +#define ath_hal_setuptxstatusring(_ah, _tsstart, _tspstart, _size) \ + ((*(_ah)->ah_setupTxStatusRing)((_ah), (_tsstart), (_tspstart), \ + (_size))) #define ath_hal_setupfirsttxdesc(_ah, _ds, _aggrlen, _flags, _txpower, \ _txr0, _txtr0, _antm, _rcr, _rcd) \ @@ -1060,7 +1142,7 @@ void ath_intr(void *); (_series), (_ns), (_flags))) #define ath_hal_set11n_aggr_first(_ah, _ds, _len, _num) \ - ((*(_ah)->ah_set11nAggrFirst)((_ah), (_ds), (_len), (_num))) + ((*(_ah)->ah_set11nAggrFirst)((_ah), (_ds), (_len))) #define ath_hal_set11naggrmiddle(_ah, _ds, _num) \ ((*(_ah)->ah_set11nAggrMiddle)((_ah), (_ds), (_num))) #define ath_hal_set11n_aggr_last(_ah, _ds) \ |