* Copyright (c) 2000-2005 ZyDAS Technology Corporation
* Copyright (c) 2007-2009 Atheros Communications, Inc.
* Copyright 2009 Johannes Berg <johannes@sipsolutions.net>
- * Copyright 2009, 2010 Christian Lamparter <chunkeey@googlemail.com>
+ * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
return !((d0[0] ^ d1[0]) | (unsigned short)(d0[1] ^ d1[1]));
}
-#ifdef CONFIG_CARL9170FW_TX_AMPDU
+/* This function will only work on uint32_t-aligned pointers! */
+static bool same_hdr(const void *_d0, const void *_d1)
+{
+ const uint32_t *d0 = _d0;
+ const uint32_t *d1 = _d1;
+
+ /* BUG_ON((unsigned long)d0 & 3 || (unsigned long)d1 & 3)) */
+ return !((d0[0] ^ d1[0]) | /* FC + DU */
+ (d0[1] ^ d1[1]) | /* addr1 */
+ (d0[2] ^ d1[2]) | (d0[3] ^ d1[3]) | /* addr2 + addr3 */
+ (d0[4] ^ d1[4])); /* addr3 */
+}
+
+static inline bool same_aggr(struct ieee80211_hdr *a, struct ieee80211_hdr *b)
+{
+ return (get_tid(a) == get_tid(b)) || same_hdr(a, b);
+}
+
+static void wlan_tx_ampdu_end(unsigned int qidx)
+{
+ struct carl9170_tx_superframe *ht_prev = fw.wlan.ampdu_prev[qidx];
+
+ fw.wlan.ampdu_prev[qidx] = NULL;
+ if (ht_prev)
+ ht_prev->f.hdr.mac.ba_end = 1;
+}
+
static void wlan_tx_ampdu(struct carl9170_tx_superframe *super)
{
unsigned int qidx = super->s.queue;
struct carl9170_tx_superframe *ht_prev = fw.wlan.ampdu_prev[qidx];
if (!super->f.hdr.mac.ampdu) {
- fw.wlan.ampdu_prev[qidx] = NULL;
-
- if (ht_prev)
- ht_prev->f.hdr.mac.ba_end = 1;
+ wlan_tx_ampdu_end(qidx);
} else {
fw.wlan.ampdu_prev[qidx] = super;
- if (ht_prev && (get_tid(&super->f.data.i3e) != get_tid(&ht_prev->f.data.i3e) ||
- !compare_ether_address(super->f.data.i3e.addr1, ht_prev->f.data.i3e.addr1)))
+ if (ht_prev &&
+ !same_aggr(&super->f.data.i3e, &ht_prev->f.data.i3e))
ht_prev->f.hdr.mac.ba_end = 1;
else
super->f.hdr.mac.ba_end = 0;
}
}
-#endif /* CONFIG_CARL9170FW_TX_AMPDU */
/* for all tries */
static void __wlan_tx(struct dma_desc *desc)
read_tsf(tsf);
}
-#ifdef CONFIG_CARL9170FW_TX_AMPDU
wlan_tx_ampdu(super);
-#endif /* CONFIG_CARL9170FW_TX_AMPDU */
#if (defined CONFIG_CARL9170FW_LOOPBACK) || (defined CONFIG_CARL9170FW_DISCARD)
wlan_tx_complete(super, true);
# endif
#else /* CONFIG_CARL9170FW_LOOPBACK */
-# if ((defined CONFIG_CARL9170FW_DEBUG) && (defined CONFIG_CARL9170FW_PSM))
+# ifdef CONFIG_CARL9170FW_DEBUG
BUG_ON(fw.phy.psm.state != CARL9170_PSM_WAKE);
-# endif /* CONFIG_CARL9170FW_DEBUG && CONFIG_CARL9170FW_PSM */
+# endif /* CONFIG_CARL9170FW_DEBUG */
/* insert desc into the right queue */
dma_put(&fw.wlan.tx_queue[super->s.queue], desc);
*/
dma_unlink_head(queue);
-#ifdef CONFIG_CARL9170FW_DELAYED_TX
dma_put(&fw.wlan.tx_retry, desc);
-#else
- __wlan_tx(desc);
-#endif /* CONFIG_CARL9170FW_DELAYED_TX */
return true;
}
} else {
static void handle_tx_completion(void)
{
struct dma_desc *desc;
- unsigned int i;
+ unsigned int map = 0;
+ int i;
- for (i = 0; i < __AR9170_NUM_TX_QUEUES; i++) {
+ for (i = AR9170_TXQ_SPECIAL; i >= AR9170_TXQ0; i--) {
__while_desc_bits(desc, &fw.wlan.tx_queue[i], AR9170_OWN_BITS_SW) {
if (!wlan_tx_status(&fw.wlan.tx_queue[i], desc)) {
/* termination requested. */
}
}
-#ifdef CONFIG_CARL9170FW_DELAYED_TX
for_each_desc(desc, &fw.wlan.tx_retry)
__wlan_tx(desc);
- for_each_desc(desc, &fw.wlan.tx_delay[i])
- _wlan_tx(desc);
-#endif /* CONFIG_CARL9170FW_DELAYED_TX */
- wlan_trigger(BIT(i));
+ wlan_tx_ampdu_end(i);
+ if (!queue_empty(&fw.wlan.tx_queue[i]))
+ map |= BIT(i);
+
}
+ wlan_trigger(map);
}
void __hot wlan_tx(struct dma_desc *desc)
}
#endif /* CONFIG_CARL9170FW_CAB_QUEUE */
-#ifdef CONFIG_CARL9170FW_DELAYED_TX
- if (!queue_empty(&fw.wlan.tx_queue[super->s.queue])) {
- dma_put(&fw.wlan.tx_delay[super->s.queue], desc);
- return;
- }
-#endif /* CONFIG_CARL9170FW_DELAYED_TX */
-
_wlan_tx(desc);
wlan_trigger(BIT(super->s.queue));
}
up_trigger();
} else {
dma_reclaim(&fw.wlan.rx_queue, desc);
- _wlan_trigger(AR9170_DMA_TRIGGER_RXQ);
+ wlan_trigger(AR9170_DMA_TRIGGER_RXQ);
}
}
}
fw.wlan.cab_flush_time = get_clock_counter();
#endif /* CONFIG_CARL9170FW_CAB_QUEUE */
-#ifdef CONFIG_CARL9170FW_PSM
rf_psm();
send_cmd_to_host(4, CARL9170_RSP_PRETBTT, 0x00,
(uint8_t *) &fw.phy.psm.state);
-#else
- send_cmd_to_host(0, CARL9170_RSP_PRETBTT, 0x00, NULL);
-#endif /* CONFIG_CARL9170FW_PSM */
}
static void handle_atim(void)
}
#endif /* CONFIG_CARL9170FW_CAB_QUEUE */
-#ifdef CONFIG_CARL9170FW_DELAYED_TX
- if (fw.wlan.tx_trigger) {
- _wlan_trigger(fw.wlan.tx_trigger);
- fw.wlan.tx_trigger = 0;
- }
-#endif /* CONFIG_CARL9170FW_DELAYED_TX */
-
wlan_send_buffered_tx_status();
#ifdef CONFIG_CARL9170FW_HANDLE_BACK_REQ
static void wlan_check_hang(void)
{
struct dma_desc *desc;
- unsigned int i;
+ int i;
- for (i = 0; i < __AR9170_NUM_TX_QUEUES; i++) {
+ for (i = AR9170_TXQ_SPECIAL; i >= AR9170_TXQ0; i--) {
if (queue_empty(&fw.wlan.tx_queue[i])) {
/* Nothing to do here... move along */
continue;
uint32_t ack_power;
uint32_t rts_cts_tpc;
uint32_t rts_cts_rate;
- unsigned int i;
+ int i;
#ifdef CONFIG_CARL9170FW_RADIO_FUNCTIONS
uint32_t rx_BB;
* set(AR9170_PHY_REG_CCA_THRESHOLD, 0x0);
*/
+ val = AR9170_DMA_TRIGGER_RXQ;
/* Reinitialize all WLAN TX DMA queues. */
- for (i = 0; i < __AR9170_NUM_TX_QUEUES; i++) {
+ for (i = AR9170_TXQ_SPECIAL; i >= AR9170_TXQ0; i--) {
struct dma_desc *iter;
__for_each_desc_bits(iter, &fw.wlan.tx_queue[i], AR9170_OWN_BITS_SW);
set_wlan_txq_dma_addr(i, (uint32_t) iter);
if (!is_terminator(&fw.wlan.tx_queue[i], iter))
- wlan_trigger(BIT(i));
+ val |= BIT(i);
DBG("Q:%d l:%d h:%p t:%p cu:%p it:%p ct:%x st:%x\n", i, queue_len(&fw.wlan.tx_queue[i]),
fw.wlan.tx_queue[i].head, fw.wlan.tx_queue[i].terminator,
AR9170_MAC_INT_RETRY_FAIL;
set(AR9170_MAC_REG_DMA_RXQ_ADDR, (uint32_t) fw.wlan.rx_queue.head);
- wlan_trigger(AR9170_DMA_TRIGGER_RXQ);
+ wlan_trigger(val);
}
#else
static void wlan_mac_reset(void)