/*
* Copyright (c) 2010-2011 Atheros Communications Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* 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 "htc.h"
/******/
/* TX */
/******/
static const int subtype_txq_to_hwq[] = {
[WME_AC_BE] = ATH_TXQ_AC_BE,
[WME_AC_BK] = ATH_TXQ_AC_BK,
[WME_AC_VI] = ATH_TXQ_AC_VI,
[WME_AC_VO] = ATH_TXQ_AC_VO,
};
#define ATH9K_HTC_INIT_TXQ(subtype) do { \
qi.tqi_subtype = subtype_txq_to_hwq[subtype]; \
qi.tqi_aifs = ATH9K_TXQ_USEDEFAULT; \
qi.tqi_cwmin = ATH9K_TXQ_USEDEFAULT; \
qi.tqi_cwmax = ATH9K_TXQ_USEDEFAULT; \
qi.tqi_physCompBuf = 0; \
qi.tqi_qflags = TXQ_FLAG_TXEOLINT_ENABLE | \
TXQ_FLAG_TXDESCINT_ENABLE; \
} while (0)
int get_hw_qnum(u16 queue, int *hwq_map)
{
switch (queue) {
case 0:
return hwq_map[WME_AC_VO];
case 1:
return hwq_map[WME_AC_VI];
case 2:
return hwq_map[WME_AC_BE];
case 3:
return hwq_map[WME_AC_BK];
default:
return hwq_map[WME_AC_BE];
}
}
void ath9k_htc_check_stop_queues(struct ath9k_htc_priv *priv)
{
spin_lock_bh(&priv->tx.tx_lock);
priv->tx.queued_cnt++;
if ((priv->tx.queued_cnt >= ATH9K_HTC_TX_THRESHOLD) &&
!(priv->tx.flags & ATH9K_HTC_OP_TX_QUEUES_STOP)) {
priv->tx.flags |= ATH9K_HTC_OP_TX_QUEUES_STOP;
ieee80211_stop_queues(priv->hw);
}
spin_unlock_bh(&priv->tx.tx_lock);
}
void ath9k_htc_check_wake_queues(struct ath9k_htc_priv *priv)
{
spin_lock_bh(&priv->tx.tx_lock);
if ((priv->tx.queued_cnt < ATH9K_HTC_TX_THRESHOLD) &&
(priv->tx.flags & ATH9K_HTC_OP_TX_QUEUES_STOP)) {
priv->tx.flags &= ~ATH9K_HTC_OP_TX_QUEUES_STOP;
ieee80211_wake_queues(priv->hw);
}
spin_unlock_bh(&priv->tx.tx_lock);
}
int ath9k_htc_tx_get_slot(struct ath9k_htc_priv *priv)
{
int slot;
spin_lock_bh(&priv->tx.tx_lock);
slot = find_first_zero_bit(priv->tx.tx_slot, MAX_TX_BUF_NUM);
if (slot >= MAX_TX_BUF_NUM) {
spin_unlock_bh(&priv->tx.tx_lock);
return -ENOBUFS;
}
__set_bit(slot, priv->tx.tx_slot);
spin_unlock_bh(&priv->tx.tx_lock);
return slot;
}
void ath9k_htc_tx_clear_slot(struct ath9k_htc_priv *priv, int slot)
{
spin_lock_bh(&priv->tx.tx_lock);
__clear_bit(slot, priv->tx.tx_slot);
spin_unlock_bh(&priv->tx.tx_lock);
}
static inline enum