/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 2008 Atheros Communications Inc.
*
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/mac_provider.h>
#include <sys/ethernet.h>
#include <sys/byteorder.h>
#include <inet/wifi_ioctl.h>
#include <sys/mac_wifi.h>
#include "arn_core.h"
/* 20MHz 40MHz */
{ 26, 54 }, /* 0: BPSK */
{ 52, 108 }, /* 1: QPSK 1/2 */
{ 78, 162 }, /* 2: QPSK 3/4 */
{ 104, 216 }, /* 3: 16-QAM 1/2 */
{ 156, 324 }, /* 4: 16-QAM 3/4 */
{ 208, 432 }, /* 5: 64-QAM 2/3 */
{ 234, 486 }, /* 6: 64-QAM 3/4 */
{ 260, 540 }, /* 7: 64-QAM 5/6 */
{ 52, 108 }, /* 8: BPSK */
{ 104, 216 }, /* 9: QPSK 1/2 */
{ 156, 324 }, /* 10: QPSK 3/4 */
{ 208, 432 }, /* 11: 16-QAM 1/2 */
{ 312, 648 }, /* 12: 16-QAM 3/4 */
{ 416, 864 }, /* 13: 64-QAM 2/3 */
{ 468, 972 }, /* 14: 64-QAM 3/4 */
{ 520, 1080 }, /* 15: 64-QAM 5/6 */
};
#ifdef ARN_TX_AGGREGRATION
#endif
static void
{
/* fill in beacon config data */
}
/* Aggregation logic */
#ifdef ARN_TX_AGGREGATION
/* Check if it's okay to send out aggregates */
static int
{
return (1);
else
return (0);
}
/*
* NB: must be called with txq lock held
*/
static void
{
/* if tid is paused, hold off */
return;
/* add tid to ac atmost once */
return;
/* add node ac to txq atmost once */
return;
}
/* pause a tid */
static void
{
}
/* resume a tid and schedule aggregate */
void
{
goto unlock;
goto unlock;
/*
* Add this TID to scheduler and try to send out aggregates
*/
}
/* flush tid's software queue and send frames as non-ampdu's */
static void
{
return;
}
}
}
/* Update block ack window */
static void
{
}
}
/* Add a sub-frame to block ack window */
static void
{
if (bf_isretried(bf))
return;
(ATH_TID_MAX_BUFS - 1))) {
}
}
/*
* TODO: For frame(s) that are in the retry state, we will reuse the
* sequence number(s) without setting the retry bit. The
* alternative is to give up on these and BAR the receiver's window
* forward.
*/
static void
struct ath_atx_tid *tid)
{
for (;;) {
break;
if (bf_isretried(bf))
}
}
static void
{
bf->bf_retries++;
}
static struct ath_buf *
{
return (tbf);
}
static void
{
if (ATH_DS_TX_BA(ds)) {
WME_BA_BMP_SIZE >> 3);
} else {
/*
* issue happens. Chip needs to be reset.
* But AP code may have sychronization issues
* when perform internal reset in this routine.
* Only enable reset in STA mode for now.
*/
needreset = 1;
}
}
while (bf) {
/*
* transmit completion, subframe is
* acked by block ack
*/
acked_cnt++;
/* transmit completion */
acked_cnt++;
} else {
txpending = 1;
} else {
txfail = 1;
sendbar = 1;
txfail_cnt++;
}
} else {
/*
* cleanup in progress, just fail
* the un-acked sub-frames
*/
txfail = 1;
}
}
/* INIT_LIST_HEAD */
} else {
}
if (!txpending) {
/*
* complete the acked-ones/xretried ones; update
* block-ack window
*/
} else {
}
} else {
/* retry the un-acked ones */
} else {
/*
* Clear descriptor status words for
* software retry
*/
}
/*
* Put this buffer to the temporary pending
* queue to retain ordering
*/
/*
* Insert src list after dst list.
* Empty src list thereafter
*/
/* should re-initialize list here??? */
}
}
tid->addba_exchangeattempts = 0;
/* send buffered frames as singles */
}
return;
}
/*
* prepend un-acked frames to the beginning of
* the pending frame queue
*/
if (!list_empty(&list_pending)) {
}
}
static uint32_t
struct ath_atx_tid *tid)
{
int i;
/* ??? */
/*
* Find the lowest frame length among the rate series that will have a
* 4ms transmit duration.
* TODO - TXOP limit needs to be considered.
*/
for (i = 0; i < 4; i++) {
if (!WLAN_RC_PHY_HT
legacy = 1;
break;
}
frmlen =
}
}
/*
* limit aggregate size by the minimum rate if rate selected is
* not a probe rate, if rate selected is a probe rate then
* avoid aggregation of this packet.
*/
if (legacy)
return (0);
/*
* h/w can accept aggregates upto 16 bit lengths (65535).
* The IE, however can hold upto 65536, which shows up here
* as zero. Ignore 65536 since we are constrained by hw.
*/
if (maxampdu)
return (aggr_limit);
}
/*
* Returns the number of delimiters to be added to
* meet the minimum required mpdudensity.
* caller should make sure that the rate is HT rate .
*/
static int
{
/* Select standard number of delimiters based on frame length alone */
/*
* If encryption enabled, hardware requires some more padding between
* subframes.
* TODO - this could be improved to be dependent on the rate.
* The hardware can keep up at lower rates, but not higher rates
*/
/*
* Convert desired mpdu density from microeconds to bytes based
* on highest rate in rate series (i.e. first rate) to determine
* required minimum length for subframe. Take into account
* whether high rate is 20 or 40Mhz and half or full GI.
*/
/*
* If there is no mpdu density restriction, no further calculation
* is needed.
*/
if (mpdudensity == 0)
return (ndelim);
if (half_gi)
else
if (nsymbols == 0)
nsymbols = 1;
}
return (ndelim);
}
static enum ATH_AGGR_STATUS
{
do {
/* do not step over block-ack window */
break;
}
if (!rl) {
rl = 1;
}
/* do not exceed aggregation limit */
if (nframes &&
break;
}
/* do not exceed subframe limit */
break;
}
nframes++;
/* add padding for previous frame to aggregation length */
/*
* Get the delimiters needed to meet the MPDU
* density for this node.
*/
ndelim =
/* link buffers of this frame to the aggregate */
if (bf_prev) {
}
return (status);
}
static void
struct ath_atx_tid *tid)
{
do {
return;
/* INIT_LIST_HEAD */
/*
* no frames picked up to be aggregated;
* block-ack window is not open.
*/
if (list_empty(&bf_q))
break;
/* if only one frame, send as non-aggregate */
continue;
}
/* setup first desc of aggregate */
/* anchor last desc of aggregate */
txq->axq_aggr_depth++;
}
int
{
}
return (0);
}
int
{
return (0);
txtid->addba_exchangeattempts = 0;
return (0);
}
/* drop all software retried frames and mark this TID */
/* list_first_entry */
if (!bf_isretried(bf)) {
/*
* NB: it's based on the assumption that
* software retried frame will always stay
* at the head of software queue.
*/
break;
}
// ath_tx_complete_buf(sc, bf, &list, 0, 0); /* to do */
}
} else {
txtid->addba_exchangeattempts = 0;
}
return (0);
}
void
struct ieee80211_node *in,
{
}
}
{
return (B_FALSE);
return (B_TRUE);
}
}
return (B_FALSE);
}
/* Queue Management */
static void
{
}
}
}
int
{
int qnum;
switch (qtype) {
case ATH9K_TX_QUEUE_DATA:
"HAL AC %u out of range, max %zu!\n",
return (-1);
}
break;
case ATH9K_TX_QUEUE_BEACON:
break;
case ATH9K_TX_QUEUE_CAB:
break;
default:
qnum = -1;
}
return (qnum);
}
struct ath_txq *
{
int qos_ac;
int qnum;
if ((type & IEEE80211_FC0_TYPE_MASK) ==
switch (tid) {
case 1:
case 2:
case 0:
case 3:
case 4:
case 5:
case 6:
case 7:
}
}
} else {
}
} else if ((type & IEEE80211_FC0_TYPE_MASK) ==
} else if ((type & IEEE80211_FC0_TYPE_MASK) ==
} else {
}
"TX queue: %d is full, depth: %d\n",
/* stop th queue */
return (NULL);
}
return (txq);
}
/* Called only when tx aggregation is enabled and HT is supported */
static void
struct ieee80211_frame *wh)
{
arn_problem("assign_aggr_tid_seqno():"
"failed to find tx node\n");
return;
}
/* Get tidno */
}
}
/* Get seqno */
/*
* For HT capable stations, we save tidno for later use.
* We also override seqno set by upper layer with the one
* in tx aggregation state.
*
* If fragmentation is on, the sequence number is
* not overridden, since it has been
* incremented by the fragmentation routine.
*
* FIXME: check if the fragmentation threshold exceeds
* IEEE80211 max.
*/
/* LINTED E_CONSTANT_CONDITION */
/* release node */
}
/* Compute the number of bad frames */
/* ARGSUSED */
static int
{
int ba_index;
int nbad = 0;
int isaggr = 0;
return (0);
if (isaggr) {
}
while (bf) {
nbad++;
}
return (nbad);
}
static void
struct ath_atx_tid *tid,
{
/* update starting sequence number for subsequent ADDBA request */
}
/*
* Insert a chain of ath_buf (descriptors) on a txq and
* assume the descriptors are already chained together by caller.
*/
static void
{
/*
* Insert the frame on the outbound list and
* pass it on to the hardware.
*/
if (list_empty(list))
return;
txq->axq_totalqueued++;
"TXDP[%u] = %llx (%p)\n",
} else {
}
}
#endif /* ARN_TX_AGGREGATION */
static struct ath_buf *
{
/* Check if a tx buffer is available */
"stop queue\n"));
}
return (bf);
}
static uint32_t
struct ieee80211_frame *wh,
{
int flags = 0;
}
}
return (flags);
}
static void
{
int i;
/* Buf reset */
for (i = 0; i < 4; i++) {
}
/* LINTED E_ASSIGN_NARROW_CONV */
/* Frame type */
/*
* The 802.11 layer marks whether or not we should
* use short preamble based on the current mode and
* negotiated parameters.
*/
/* Crypto */
/* Assign seqno, tidno for tx aggrefation */
#ifdef ARN_TX_AGGREGATION
#endif /* ARN_TX_AGGREGATION */
}
/*
* ath_pkt_dur - compute packet duration (NB: not NAV)
*
* rix - rate index
* pktlen - total bytes (delims + data + fcs + pads + pad delims)
* width - 0 for 20 MHz, 1 for 40 MHz
* half_gi - to use 4us v/s 3.6 us for symbol time
*/
static uint32_t
{
/* for legacy rates, use old function to compute packet duration */
if (!IS_HT_RATE(rc))
rix, shortPreamble));
/* find number of symbols: PLCP + data */
if (!half_gi)
else
return (duration);
}
/* Rate module function to set rate related fields in tx descriptor */
static void
struct ieee80211_frame *wh)
{
if (IEEE80211_HAS_MOREFRAGS(wh) ||
}
/* get the cix for the lowest valid rix */
for (i = 3; i >= 0; i--) {
break;
}
}
/*
*/
rtsctsena = 1;
}
/*
* For 11n, the default behavior is to enable RTS for hw retried frames.
* We enable the global flag here and let rate series flags determine
* which rates will actually use RTS.
*/
/* 802.11g protection not needed, use our default behavior */
if (!rtsctsena)
}
/* Set protection if aggregate protection on */
rtsctsena = 1;
}
/* For AR5416 - RTS cannot be followed by a frame larger than 8K */
flags &= ~(ATH9K_TXDESC_RTSENA);
/*
* CTS transmit rate is derived from the transmit rate by looking in the
* h/w rate table. We must also factor in whether or not a short
*/
for (i = 0; i < 4; i++) {
continue;
(bf_isshpreamble(bf) ?
ATH9K_RATESERIES_RTS_CTS : 0) |
ATH9K_RATESERIES_2040 : 0) |
ATH9K_RATESERIES_HALFGI : 0);
if (rtsctsena)
"series[%d]--flags & ATH9K_TX_RC_USE_RTS_CTS = %08x"
"--flags & ATH9K_TX_RC_40_MHZ_WIDTH = %08x"
"--flags & ATH9K_TX_RC_SHORT_GI = %08x\n",
"series[%d]:"
"dot11rate:%d"
"index:%d"
"retry count:%d\n",
i,
}
/* set dur_update_en for l-sig computation except for PS-Poll frames */
}
static void
struct ath_xmit_status *tx_status)
{
}
/* To complete a chain of buffers associated a frame */
static void
{
/*
* Set retry information.
* NB: Don't use the information in the descriptor, because the frame
* could be software retried.
*/
if (sendbar)
if (!txok) {
if (bf_isxretried(bf))
}
/* complete this frame */
/*
* Return the list of ath_buf of this mpdu to free queue
*/
}
static void
{
"tx queue [%u] %x, link %p\n",
}
/* Drain only the data queues */
/* ARGSUSED */
static void
{
for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
if (ARN_TXQ_SETUP(sc, i)) {
/*
* The TxDMA may not really be stopped.
* Double check the hal tx pending count
*/
}
}
}
if (npend) {
/* TxDMA not stopped, reset the hal */
"Unable to stop TxDMA. Reset HAL!\n"));
if (!ath9k_hw_reset(ah,
"unable to reset hardware; hal status %u\n",
status));
}
}
for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
if (ARN_TXQ_SETUP(sc, i))
}
}
/* Setup a h/w transmit queue */
struct ath_txq *
{
int qnum;
qi.tqi_physCompBuf = 0;
/*
* Enable interrupts only for EOL and DESC conditions.
* We mark tx descriptors to receive a DESC interrupt
* when a tx queue gets deep; otherwise waiting for the
* EOL to reap descriptors. Note that this is done to
* reduce interrupt load and this only defers reaping
* descriptors, never transmitting frames. Aside from
* reducing interrupts this also permits more concurrency.
* The only potential downside is if the tx queue backs
* up in which case the top half of the kernel may backup
* due to a lack of tx descriptors.
*
* The UAPSD queue is an exception, since we take a desc-
* based intr on the EOSP frames.
*/
if (qtype == ATH9K_TX_QUEUE_UAPSD)
else
if (qnum == -1) {
/*
* NB: don't print a message, this happens
* normally on parts with too few tx queues
*/
return (NULL);
}
"hal qnum %u out of range, max %u!\n",
return (NULL);
}
txq->axq_aggr_depth = 0;
txq->axq_totalqueued = 0;
}
}
/* Reclaim resources for a setup queue */
void
{
}
/*
* Setup a hardware data transmit queue for the specified
* access control. The hal may not support all requested
* queues in which case it will return a reference to a
* previously setup queue. We record the mapping from ac's
* to h/w queues for use by arn_tx_start and also track
* the set of h/w queues being used to optimize work in the
* transmit interrupt handler and related routines.
*/
int
{
"HAL AC %u out of range, max %zu!\n",
return (0);
}
return (1);
} else
return (0);
}
void
{
/*
* This assumes output has been stopped.
*/
for (;;) {
break;
}
}
}
/* Drain the transmit queues and reclaim resources */
void
{
/*
* stop beacon queue. The beacon will be freed when
* we go to INIT state
*/
"beacon queue %x\n",
}
}
{
}
{
}
/* Update parameters for a transmit queue */
int
struct ath9k_tx_queue_info *qinfo)
{
int error = 0;
/*
* XXX: for beacon queue, we just save the parameter.
* It will be picked up by arn_beaconq_config() when
* it's necessary.
*/
return (0);
}
"Unable to update hardware queue %u!\n", qnum));
} else {
}
return (error);
}
int
{
/*
* Ensure the readytime % is within the bounds.
*/
return (0);
}
static uint32_t
{
ATH9K_CIPHER_WEP, /* IEEE80211_CIPHER_WEP */
ATH9K_CIPHER_TKIP, /* IEEE80211_CIPHER_TKIP */
ATH9K_CIPHER_AES_OCB, /* IEEE80211_CIPHER_AES_OCB */
ATH9K_CIPHER_AES_CCM, /* IEEE80211_CIPHER_AES_CCM */
ATH9K_CIPHER_CKIP, /* IEEE80211_CIPHER_CKIP */
ATH9K_CIPHER_CLR, /* IEEE80211_CIPHER_NONE */
};
return (ATH9K_KEY_TYPE_WEP);
return (ATH9K_KEY_TYPE_TKIP);
return (ATH9K_KEY_TYPE_AES);
return (ATH9K_KEY_TYPE_CLEAR);
}
/* Display buffer */
void
{
if (isaddress) {
arn_problem("arn: %08x: ", p);
} else {
arn_problem("arn: ");
}
while (len) {
*phex++ = ' ';
grouped = 0;
}
++p;
--len;
}
*phex = '\0';
*pascii = '\0';
}
void
{
while (len) {
p += perline;
}
}
/*
* The input parameter mp has following assumption:
* For data packets, GLDv3 mac_wifi plugin allocates and fills the
* ieee80211 header. For management packets, net80211 allocates and
* fills the ieee80211 header. In both cases, enough spaces in the
* header are left for encryption option.
*/
static int32_t
{
/*
* CRC are added by H/W, not encaped by driver,
* but we must count it in pkt length.
*/
if (hdrlen == 28)
is_padding = B_TRUE;
if (iswep != 0) {
struct ieee80211_key *k;
/*
* Construct the 802.11 header+trailer for an encrypted
* frame. The only reason this can fail is because of an
*/
if (k == NULL) {
"crypto_encap failed\n"));
/*
* This can happen when the key is yanked after the
* frame was queued. Just discard the frame; the
* 802.11 layer counts failures and provides
*/
return (EIO);
}
/*
* Adjust the packet + header lengths for the crypto
* additions and calculate the h/w key index. When
* a s/w mic is done the frame will have had any mic
* added to it prior to entry so m0->m_pkthdr.len above will
* account for it. Otherwise we need to add it to the
* packet length.
*/
if ((k->wk_flags & IEEE80211_KEY_SWMIC) == 0)
/* packet header may have moved, reset our local pointer */
}
}
/* buf setup */
/* setup descriptors */
/*
* Calculate Atheros packet type from IEEE80211 packet header
* and setup for rate calculations.
*/
case IEEE80211_FC0_TYPE_MGT:
if (subtype == IEEE80211_FC0_SUBTYPE_BEACON)
else if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
else if (subtype == IEEE80211_FC0_SUBTYPE_ATIM)
else
/* force all ctl frames to highest queue */
break;
case IEEE80211_FC0_TYPE_CTL:
/* force all ctl frames to highest queue */
break;
case IEEE80211_FC0_TYPE_DATA:
// arn_dump_pkg((unsigned char *)bf->bf_dma.mem_va,
// pktlen, 1, 1);
/* Always use background queue */
break;
default:
/* Unknown 802.11 frame */
return (1);
}
/* setup descriptor */
/*
* Formulate first tx descriptor with tx controls.
*/
(pktlen), /* packet length */
atype, /* Atheros packet type */
MAX_RATE_POWER /* MAX_RATE_POWER */,
keyix /* ATH9K_TXKEYIX_INVALID */,
keytype /* ATH9K_KEY_TYPE_CLEAR */,
/* LINTED E_BAD_PTR_CAST_ALIGN */
"an->an_tx_rate1sp=%d tx_rate2sp=%d tx_rate3sp=%d "
"qnum=%d sht=%d dur = %d\n",
mbslen, /* segment length */
B_TRUE, /* first segment */
B_TRUE, /* last segment */
ds); /* first descriptor */
/* set rate related fields in tx descriptor */
} else {
}
// arn_dump_pkg((unsigned char *)bf->bf_dma.mem_va, pktlen, 1, 1);
return (0);
}
/*
* Transmit a management frame.
* Note that management frames come directly from the 802.11 layer
* and do not honor the send queue flow control.
*/
/* Upon failure caller should free mp */
int
{
int error = 0;
/* should check later */
if ((type & IEEE80211_FC0_TYPE_MASK) !=
}
return (ENXIO);
}
/* Grab a TX buffer */
"no xmit buf\n"));
if ((type & IEEE80211_FC0_TYPE_MASK) ==
} else {
}
return (ENOMEM);
}
/* Locate node */
goto bad;
}
switch (type & IEEE80211_FC0_TYPE_MASK) {
case IEEE80211_FC0_TYPE_DATA:
break;
default:
/* fill time stamp */
/* adjust 100us delay to xmit */
tsf += 100;
/* LINTED E_BAD_PTR_CAST_ALIGN */
}
break;
}
if (error != 0) {
bad:
}
}
error == 0) {
}
return (error);
}
static void
{
" %08x %08x %08x %c\n",
}
/* ARGSUSED */
static void
int nbad,
int txok,
{
sizeof (tx_info_priv->tx));
}
}
}
/* Process completed xmit descriptors from the specified queue */
static int
{
int status;
for (;;) {
/* txq->axq_linkbuf = NULL; */
break;
}
#ifdef DEBUG
#endif
if (status == EINPROGRESS) {
break;
}
/* Successful transmition */
} else {
}
}
}
}
/*
* Hand the descriptor to the rate control algorithm.
*/
/*
* If frame was ack'd update the last rx time
* used to workaround phantom bmiss interrupts.
*/
nacked++;
} else {
}
}
}
if (!bf_isampdu(bf)) {
/*
* This frame is sent out as a single frame.
* Use hardware retry status for this frame.
*/
nbad = 0;
}
// arn_dump_pkg((unsigned char *)bf->bf_dma.mem_va,
// bf->bf_frmlen, 1, 1);
/*
* Reschedule stalled outbound packets
*/
if (sc->sc_resched_needed) {
}
}
return (nacked);
}
static void
{
int i;
int nacked = 0;
/*
* Process each active queue.
*/
for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
}
}
if (nacked)
}
/* Deferred processing of transmit interrupt */
void
{
}
/* Node init & cleanup functions */
#ifdef ARN_TX_AGGREGATION
void
{
tid->addba_exchangeattempts = 0;
}
switch (acno) {
case WME_AC_BE:
break;
case WME_AC_BK:
break;
case WME_AC_VI:
break;
case WME_AC_VO:
break;
}
}
}
void
{
int i;
for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
if (ARN_TXQ_SETUP(sc, i)) {
continue;
}
tid->addba_exchangeattempts = 0;
}
}
}
}
}
#endif /* ARN_TX_AGGREGATION */