Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 96481b20 authored by Ivo van Doorn's avatar Ivo van Doorn Committed by John W. Linville
Browse files

rt2x00: Implement TX status reporting for rt2800usb



The TX_STA_FIFO register which is used for per-frame TX frame
status reporting is also valid on rt2800usb. We can move the
rt2800pci_txdone function into rt2800lib where it can also
be used by rt2800usb.

rt2800usb needs to overwrite the txdone work handler to
a different function.

Both rt2800usb as rt2800_txdone need to take into account
that IO failures can occur while uploading the URB, which
means that when obtaining the new entry the IO status must
be checked.

Signed-off-by: default avatarIvo van Doorn <IvDoorn@gmail.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 0c5879bc
Loading
Loading
Loading
Loading
+117 −0
Original line number Diff line number Diff line
/*
	Copyright (C) 2010 Willow Garage <http://www.willowgarage.com>
	Copyright (C) 2010 Ivo van Doorn <IvDoorn@gmail.com>
	Copyright (C) 2009 Bartlomiej Zolnierkiewicz <bzolnier@gmail.com>
	Copyright (C) 2009 Gertjan van Wingerde <gwingerde@gmail.com>
@@ -572,6 +573,122 @@ void rt2800_process_rxwi(struct queue_entry *entry,
}
EXPORT_SYMBOL_GPL(rt2800_process_rxwi);

void rt2800_txdone(struct rt2x00_dev *rt2x00dev)
{
	struct data_queue *queue;
	struct queue_entry *entry;
	__le32 *txwi;
	struct txdone_entry_desc txdesc;
	u32 word;
	u32 reg;
	int wcid, ack, pid, tx_wcid, tx_ack, tx_pid;
	u16 mcs, real_mcs;
	int i;

	/*
	 * TX_STA_FIFO is a stack of X entries, hence read TX_STA_FIFO
	 * at most X times and also stop processing once the TX_STA_FIFO_VALID
	 * flag is not set anymore.
	 *
	 * The legacy drivers use X=TX_RING_SIZE but state in a comment
	 * that the TX_STA_FIFO stack has a size of 16. We stick to our
	 * tx ring size for now.
	 */
	for (i = 0; i < TX_ENTRIES; i++) {
		rt2800_register_read(rt2x00dev, TX_STA_FIFO, &reg);
		if (!rt2x00_get_field32(reg, TX_STA_FIFO_VALID))
			break;

		wcid	= rt2x00_get_field32(reg, TX_STA_FIFO_WCID);
		ack	= rt2x00_get_field32(reg, TX_STA_FIFO_TX_ACK_REQUIRED);
		pid	= rt2x00_get_field32(reg, TX_STA_FIFO_PID_TYPE);

		/*
		 * Skip this entry when it contains an invalid
		 * queue identication number.
		 */
		if (pid <= 0 || pid > QID_RX)
			continue;

		queue = rt2x00queue_get_queue(rt2x00dev, pid - 1);
		if (unlikely(!queue))
			continue;

		/*
		 * Inside each queue, we process each entry in a chronological
		 * order. We first check that the queue is not empty.
		 */
		entry = NULL;
		while (!rt2x00queue_empty(queue)) {
			entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE);
			if (!test_bit(ENTRY_DATA_IO_FAILED, &entry->flags))
				break;

			rt2x00lib_txdone_noinfo(entry, TXDONE_FAILURE);
		}

		if (!entry || rt2x00queue_empty(queue))
			break;

		/*
		 * Check if we got a match by looking at WCID/ACK/PID
		 * fields
		 */
		txwi = rt2800_drv_get_txwi(entry);

		rt2x00_desc_read(txwi, 1, &word);
		tx_wcid	= rt2x00_get_field32(word, TXWI_W1_WIRELESS_CLI_ID);
		tx_ack	= rt2x00_get_field32(word, TXWI_W1_ACK);
		tx_pid	= rt2x00_get_field32(word, TXWI_W1_PACKETID);

		if ((wcid != tx_wcid) || (ack != tx_ack) || (pid != tx_pid))
			WARNING(rt2x00dev, "invalid TX_STA_FIFO content");

		/*
		 * Obtain the status about this packet.
		 */
		txdesc.flags = 0;
		rt2x00_desc_read(txwi, 0, &word);
		mcs = rt2x00_get_field32(word, TXWI_W0_MCS);
		mcs = rt2x00_get_field32(reg, TX_STA_FIFO_MCS);
		real_mcs = rt2x00_get_field32(reg, TX_STA_FIFO_MCS);

		/*
		 * Ralink has a retry mechanism using a global fallback
		 * table. We setup this fallback table to try the immediate
		 * lower rate for all rates. In the TX_STA_FIFO, the MCS field
		 * always contains the MCS used for the last transmission, be
		 * it successful or not.
		 */
		if (rt2x00_get_field32(reg, TX_STA_FIFO_TX_SUCCESS)) {
			/*
			 * Transmission succeeded. The number of retries is
			 * mcs - real_mcs
			 */
			__set_bit(TXDONE_SUCCESS, &txdesc.flags);
			txdesc.retry = ((mcs > real_mcs) ? mcs - real_mcs : 0);
		} else {
			/*
			 * Transmission failed. The number of retries is
			 * always 7 in this case (for a total number of 8
			 * frames sent).
			 */
			__set_bit(TXDONE_FAILURE, &txdesc.flags);
			txdesc.retry = rt2x00dev->long_retry;
		}

		/*
		 * the frame was retried at least once
		 * -> hw used fallback rates
		 */
		if (txdesc.retry)
			__set_bit(TXDONE_FALLBACK, &txdesc.flags);

		rt2x00lib_txdone(entry, &txdesc);
	}
}
EXPORT_SYMBOL_GPL(rt2800_txdone);

void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc)
{
	struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
+4 −0
Original line number Diff line number Diff line
/*
	Copyright (C) 2010 Willow Garage <http://www.willowgarage.com>
	Copyright (C) 2010 Ivo van Doorn <IvDoorn@gmail.com>
	Copyright (C) 2009 Bartlomiej Zolnierkiewicz

	This program is free software; you can redistribute it and/or modify
@@ -147,6 +149,8 @@ void rt2800_write_tx_data(struct queue_entry *entry,
			  struct txentry_desc *txdesc);
void rt2800_process_rxwi(struct queue_entry *entry, struct rxdone_entry_desc *txdesc);

void rt2800_txdone(struct rt2x00_dev *rt2x00dev);

void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc);

extern const struct rt2x00debug rt2800_rt2x00debug;
+2 −106
Original line number Diff line number Diff line
/*
	Copyright (C) 2009 Ivo van Doorn <IvDoorn@gmail.com>
	Copyright (C) 2009 - 2010 Ivo van Doorn <IvDoorn@gmail.com>
	Copyright (C) 2009 Alban Browaeys <prahal@yahoo.com>
	Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org>
	Copyright (C) 2009 Luis Correia <luis.f.correia@gmail.com>
@@ -724,110 +724,6 @@ static void rt2800pci_fill_rxdone(struct queue_entry *entry,
/*
 * Interrupt functions.
 */
static void rt2800pci_txdone(struct rt2x00_dev *rt2x00dev)
{
	struct data_queue *queue;
	struct queue_entry *entry;
	__le32 *txwi;
	struct txdone_entry_desc txdesc;
	u32 word;
	u32 reg;
	int wcid, ack, pid, tx_wcid, tx_ack, tx_pid;
	u16 mcs, real_mcs;
	int i;

	/*
	 * TX_STA_FIFO is a stack of X entries, hence read TX_STA_FIFO
	 * at most X times and also stop processing once the TX_STA_FIFO_VALID
	 * flag is not set anymore.
	 *
	 * The legacy drivers use X=TX_RING_SIZE but state in a comment
	 * that the TX_STA_FIFO stack has a size of 16. We stick to our
	 * tx ring size for now.
	 */
	for (i = 0; i < TX_ENTRIES; i++) {
		rt2800_register_read(rt2x00dev, TX_STA_FIFO, &reg);
		if (!rt2x00_get_field32(reg, TX_STA_FIFO_VALID))
			break;

		wcid    = rt2x00_get_field32(reg, TX_STA_FIFO_WCID);
		ack     = rt2x00_get_field32(reg, TX_STA_FIFO_TX_ACK_REQUIRED);
		pid     = rt2x00_get_field32(reg, TX_STA_FIFO_PID_TYPE);

		/*
		 * Skip this entry when it contains an invalid
		 * queue identication number.
		 */
		if (pid <= 0 || pid > QID_RX)
			continue;

		queue = rt2x00queue_get_queue(rt2x00dev, pid - 1);
		if (unlikely(!queue))
			continue;

		/*
		 * Inside each queue, we process each entry in a chronological
		 * order. We first check that the queue is not empty.
		 */
		if (rt2x00queue_empty(queue))
			continue;
		entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE);

		/* Check if we got a match by looking at WCID/ACK/PID
		 * fields */
		txwi = (__le32 *) entry->skb->data;

		rt2x00_desc_read(txwi, 1, &word);
		tx_wcid = rt2x00_get_field32(word, TXWI_W1_WIRELESS_CLI_ID);
		tx_ack  = rt2x00_get_field32(word, TXWI_W1_ACK);
		tx_pid  = rt2x00_get_field32(word, TXWI_W1_PACKETID);

		if ((wcid != tx_wcid) || (ack != tx_ack) || (pid != tx_pid))
			WARNING(rt2x00dev, "invalid TX_STA_FIFO content\n");

		/*
		 * Obtain the status about this packet.
		 */
		txdesc.flags = 0;
		rt2x00_desc_read(txwi, 0, &word);
		mcs = rt2x00_get_field32(word, TXWI_W0_MCS);
		real_mcs = rt2x00_get_field32(reg, TX_STA_FIFO_MCS);

		/*
		 * Ralink has a retry mechanism using a global fallback
		 * table. We setup this fallback table to try the immediate
		 * lower rate for all rates. In the TX_STA_FIFO, the MCS field
		 * always contains the MCS used for the last transmission, be
		 * it successful or not.
		 */
		if (rt2x00_get_field32(reg, TX_STA_FIFO_TX_SUCCESS)) {
			/*
			 * Transmission succeeded. The number of retries is
			 * mcs - real_mcs
			 */
			__set_bit(TXDONE_SUCCESS, &txdesc.flags);
			txdesc.retry = ((mcs > real_mcs) ? mcs - real_mcs : 0);
		} else {
			/*
			 * Transmission failed. The number of retries is
			 * always 7 in this case (for a total number of 8
			 * frames sent).
			 */
			__set_bit(TXDONE_FAILURE, &txdesc.flags);
			txdesc.retry = 7;
		}

		/*
		 * the frame was retried at least once
		 * -> hw used fallback rates
		 */
		if (txdesc.retry)
			__set_bit(TXDONE_FALLBACK, &txdesc.flags);

		rt2x00lib_txdone(entry, &txdesc);
	}
}

static void rt2800pci_wakeup(struct rt2x00_dev *rt2x00dev)
{
	struct ieee80211_conf conf = { .flags = 0 };
@@ -863,7 +759,7 @@ static irqreturn_t rt2800pci_interrupt_thread(int irq, void *dev_instance)
	 * 4 - Tx done interrupt.
	 */
	if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TX_FIFO_STATUS))
		rt2800pci_txdone(rt2x00dev);
		rt2800_txdone(rt2x00dev);

	/*
	 * 5 - Auto wakeup interrupt.
+39 −1
Original line number Diff line number Diff line
/*
	Copyright (C) 2009 Ivo van Doorn <IvDoorn@gmail.com>
	Copyright (C) 2010 Willow Garage <http://www.willowgarage.com>
	Copyright (C) 2009 - 2010 Ivo van Doorn <IvDoorn@gmail.com>
	Copyright (C) 2009 Mattias Nissler <mattias.nissler@gmx.de>
	Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org>
	Copyright (C) 2009 Xose Vazquez Perez <xose.vazquez@gmail.com>
@@ -377,6 +378,38 @@ static int rt2800usb_get_tx_data_len(struct queue_entry *entry)
	return length;
}

/*
 * TX control handlers
 */
static void rt2800usb_work_txdone(struct work_struct *work)
{
	struct rt2x00_dev *rt2x00dev =
	    container_of(work, struct rt2x00_dev, txdone_work);
	struct data_queue *queue;
	struct queue_entry *entry;

	rt2800_txdone(rt2x00dev);

	/*
	 * Process any trailing TX status reports for IO failures,
	 * we loop until we find the first non-IO error entry. This
	 * can either be a frame which is free, is being uploaded,
	 * or has completed the upload but didn't have an entry
	 * in the TX_STAT_FIFO register yet.
	 */
	tx_queue_for_each(rt2x00dev, queue) {
		while (!rt2x00queue_empty(queue)) {
			entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE);

			if (test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags) ||
			    !test_bit(ENTRY_DATA_IO_FAILED, &entry->flags))
				break;

			rt2x00lib_txdone_noinfo(entry, TXDONE_FAILURE);
		}
	}
}

/*
 * RX control handlers
 */
@@ -513,6 +546,11 @@ static int rt2800usb_probe_hw(struct rt2x00_dev *rt2x00dev)
	 */
	rt2x00dev->rssi_offset = DEFAULT_RSSI_OFFSET;

	/*
	 * Overwrite TX done handler
	 */
	PREPARE_WORK(&rt2x00dev->txdone_work, rt2800usb_work_txdone);

	return 0;
}