Subject: [PATCH 16/32] rt2x00: Make sure TX rings are empty when scanning

From: Ivo van Doorn <[email-masked]>
Date: Fri, 28 Apr 2006 00:03:08 +0200

From: Ivo van Doorn <[email-masked]>

Improve the waiting when a skb buffer needs to be send
before the channel switch. This also makes sure no
pending packets are still on the TX ring while making the
channel switch.

Signed-off-by: Ivo van Doorn <[email-masked]>

diff -uprN wireless-dev-rt2x00/drivers/net/wireless/d80211/rt2x00/rt2400pci.c wireless-dev-rt2x00-patch/drivers/net/wireless/d80211/rt2x00/rt2400pci.c
--- wireless-dev-rt2x00/drivers/net/wireless/d80211/rt2x00/rt2400pci.c	2006-04-27 21:47:52.000000000 +0200
+++ wireless-dev-rt2x00-patch/drivers/net/wireless/d80211/rt2x00/rt2400pci.c	2006-04-27 21:48:21.000000000 +0200
@@ -838,7 +838,6 @@ rt2400pci_txdone(void *data)
 	struct rt2x00_pci	*rt2x00pci = (struct rt2x00_pci*)ring->dev;
 	struct net_device	*net_dev = pci_get_drvdata(rt2x00pci->pci_dev);
 	struct data_entry	*entry;
-	struct skb_cb		*cb;
 	struct txd		*txd;
 	int			tx_status;
 	int			ack;
@@ -851,13 +850,6 @@ rt2400pci_txdone(void *data)
 		|| !rt2x00_get_field32(txd->word0, TXD_W0_VALID))
 			break;
 
-		/*
-		 * Check if this frame was send for the scan routine.
-		 */
-		cb = (struct skb_cb*)&entry->skb->cb;
-		if (rt2x00pci->scan && cb->scan_complete)
-			complete(&rt2x00pci->scan->completion);
-
 		ack = rt2x00_get_field32(txd->word0, TXD_W0_ACK);
 
 		/*
@@ -896,6 +888,18 @@ rt2400pci_txdone(void *data)
 
 		rt2x00_ring_index_done_inc(ring);
 	} while (!rt2x00_ring_empty(ring));
+
+	/*
+	 * Check if we are waiting on an empty queue
+	 * to start scanning.
+	 */
+	if (rt2x00pci->scan
+	&& rt2x00_ring_empty(&rt2x00pci->tx)
+	&& rt2x00_ring_empty(&rt2x00pci->atim)
+	&& rt2x00_ring_empty(&rt2x00pci->prio)) {
+		rt2x00pci->scan->status = SCANNING_READY;
+		complete(&rt2x00pci->scan->completion);
+	}
 }
 
 static irqreturn_t
@@ -1517,6 +1521,14 @@ rt2400pci_stop(struct net_device *net_de
 	rt2x00_register_write(rt2x00pci, CSR8, reg);
 
 	/*
+	 * Cancel scanning.
+	 */
+	if (rt2x00pci->scan) {
+		rt2x00pci->scan->status = SCANNING_CANCELLED;
+		complete_all(&rt2x00pci->scan->completion);
+	}
+
+	/*
 	 * Free DMA rings.
 	 */
 	rt2400pci_free_rings(rt2x00pci);
@@ -1564,35 +1576,23 @@ static void
 rt2400pci_scan(void *data)
 {
 	struct rt2x00_pci	*rt2x00pci = data;
-	struct net_device	*net_dev = pci_get_drvdata(rt2x00pci->pci_dev);
-	struct skb_cb		*cb;
 
 	if (unlikely(!rt2x00pci->scan))
 		return;
 
 	/*
-	 * Check if we have to send a packet before the
-	 * channel switch.
+	 * Before we can start switch the channel for scanning
+	 * we need to wait untill all TX rings are empty to
+	 * guarentee that all frames are send on the correct channel.
 	 */
-	if (rt2x00pci->scan->conf.skb) {
-		cb = (struct skb_cb*)&rt2x00pci->scan->conf.skb->cb;
-		cb->scan_complete = 1;
-
-		if (rt2400pci_tx(net_dev, rt2x00pci->scan->conf.skb,
-			rt2x00pci->scan->conf.tx_control))
-			goto exit;
-
-		/*
-		 * Wait for the frame to be correctly transmitted.
-		 */
+	if (rt2x00pci->scan->status != SCANNING_READY)
 		wait_for_completion(&rt2x00pci->scan->completion);
-	}
 
 	/*
 	 * Check if this scan has been cancelled while
 	 * work was still scheduled.
 	 */
-	if (rt2x00pci->scan->cancelled)
+	if (rt2x00pci->scan->status == SCANNING_CANCELLED)
 		goto exit;
 
 	/*
@@ -1633,9 +1633,24 @@ rt2400pci_passive_scan(struct net_device
 {
 	struct rt2x00_pci	*rt2x00pci = ieee80211_dev_hw_data(net_dev);
 
+	if (rt2x00pci->scan)
+		return -EBUSY;
+
+	/*
+	 * Allocate scanning structure to store scanning info.
+	 */
 	rt2x00pci->scan = kmalloc(sizeof(struct scanning), GFP_ATOMIC);
 	if (!rt2x00pci->scan)
-		return 0;
+		return -ENOMEM;
+
+	/*
+	 * Check if we have to send a packet before the
+	 * channel switch.
+	 */
+	if (conf->skb) {
+		if (rt2400pci_tx(net_dev, conf->skb, conf->tx_control))
+			goto exit;
+	}
 
 	/*
 	 * Initialize Scanning structure.
@@ -1646,14 +1661,21 @@ rt2400pci_passive_scan(struct net_device
 
 	rt2x00pci->scan->state = state;
 
-	rt2x00pci->scan->cancelled = 0;
+	rt2x00pci->scan->status = 0;
 
 	/*
 	 * Queue work.
 	 */
-	queue_work(rt2x00pci->workqueue, &rt2x00pci->scan_work);
+	if (!queue_work(rt2x00pci->workqueue, &rt2x00pci->scan_work))
+		goto exit;
 
 	return 0;
+
+exit:
+	kfree(rt2x00pci->scan);
+	rt2x00pci->scan = NULL;
+
+	return -EIO;
 }
 
 static int
@@ -2207,11 +2229,6 @@ rt2400pci_uninitialize(struct net_device
 		rt2x00pci->csr_addr = NULL;
 	}
 
-	if (rt2x00pci->scan) {
-		rt2x00pci->scan->cancelled = 1;
-		complete_all(&rt2x00pci->scan->completion);
-	}
-
 	if (likely(rt2x00pci->workqueue)) {
 		flush_workqueue(rt2x00pci->workqueue);
 		destroy_workqueue(rt2x00pci->workqueue);
diff -uprN wireless-dev-rt2x00/drivers/net/wireless/d80211/rt2x00/rt2500pci.c wireless-dev-rt2x00-patch/drivers/net/wireless/d80211/rt2x00/rt2500pci.c
--- wireless-dev-rt2x00/drivers/net/wireless/d80211/rt2x00/rt2500pci.c	2006-04-27 21:47:52.000000000 +0200
+++ wireless-dev-rt2x00-patch/drivers/net/wireless/d80211/rt2x00/rt2500pci.c	2006-04-27 21:48:21.000000000 +0200
@@ -923,7 +923,6 @@ rt2500pci_txdone(void *data)
 	struct rt2x00_pci	*rt2x00pci = (struct rt2x00_pci*)ring->dev;
 	struct net_device	*net_dev = pci_get_drvdata(rt2x00pci->pci_dev);
 	struct data_entry	*entry;
-	struct skb_cb		*cb;
 	struct txd		*txd;
 	int			tx_status;
 	int			ack;
@@ -936,13 +935,6 @@ rt2500pci_txdone(void *data)
 		|| !rt2x00_get_field32(txd->word0, TXD_W0_VALID))
 			break;
 
-		/*
-		 * Check if this frame was send for the scan routine.
-		 */
-		cb = (struct skb_cb*)&entry->skb->cb;
-		if (rt2x00pci->scan && cb->scan_complete)
-			complete(&rt2x00pci->scan->completion);
-
 		ack = rt2x00_get_field32(txd->word0, TXD_W0_ACK);
 
 		/*
@@ -981,6 +973,18 @@ rt2500pci_txdone(void *data)
 
 		rt2x00_ring_index_done_inc(ring);
 	} while (!rt2x00_ring_empty(ring));
+
+	/*
+	 * Check if we are waiting on an empty queue
+	 * to start scanning.
+	 */
+	if (rt2x00pci->scan
+	&& rt2x00_ring_empty(&rt2x00pci->tx)
+	&& rt2x00_ring_empty(&rt2x00pci->atim)
+	&& rt2x00_ring_empty(&rt2x00pci->prio)) {
+		rt2x00pci->scan->status = SCANNING_READY;
+		complete(&rt2x00pci->scan->completion);
+	}
 }
 
 static irqreturn_t
@@ -1630,6 +1634,14 @@ rt2500pci_stop(struct net_device *net_de
 	rt2x00_register_write(rt2x00pci, CSR8, reg);
 
 	/*
+	 * Cancel scanning.
+	 */
+	if (rt2x00pci->scan) {
+		rt2x00pci->scan->status = SCANNING_CANCELLED;
+		complete_all(&rt2x00pci->scan->completion);
+	}
+
+	/*
 	 * Free DMA rings.
 	 */
 	rt2500pci_free_rings(rt2x00pci);
@@ -1677,35 +1689,23 @@ static void
 rt2500pci_scan(void *data)
 {
 	struct rt2x00_pci	*rt2x00pci = data;
-	struct net_device	*net_dev = pci_get_drvdata(rt2x00pci->pci_dev);
-	struct skb_cb		*cb;
 
 	if (unlikely(!rt2x00pci->scan))
 		return;
 
 	/*
-	 * Check if we have to send a packet before the
-	 * channel switch.
+	 * Before we can start switch the channel for scanning
+	 * we need to wait untill all TX rings are empty to
+	 * guarentee that all frames are send on the correct channel.
 	 */
-	if (rt2x00pci->scan->conf.skb) {
-		cb = (struct skb_cb*)&rt2x00pci->scan->conf.skb->cb;
-		cb->scan_complete = 1;
-
-		if (rt2500pci_tx(net_dev, rt2x00pci->scan->conf.skb,
-			rt2x00pci->scan->conf.tx_control))
-			goto exit;
-
-		/*
-		 * Wait for the frame to be correctly transmitted.
-		 */
+	if (rt2x00pci->scan->status != SCANNING_READY)
 		wait_for_completion(&rt2x00pci->scan->completion);
-	}
 
 	/*
 	 * Check if this scan has been cancelled while
 	 * work was still scheduled.
 	 */
-	if (rt2x00pci->scan->cancelled)
+	if (rt2x00pci->scan->status == SCANNING_CANCELLED)
 		goto exit;
 
 	/*
@@ -1742,9 +1742,24 @@ rt2500pci_passive_scan(struct net_device
 {
 	struct rt2x00_pci	*rt2x00pci = ieee80211_dev_hw_data(net_dev);
 
+	if (rt2x00pci->scan)
+		return -EBUSY;
+
+	/*
+	 * Allocate scanning structure to store scanning info.
+	 */
 	rt2x00pci->scan = kmalloc(sizeof(struct scanning), GFP_ATOMIC);
 	if (!rt2x00pci->scan)
-		return 0;
+		return -ENOMEM;
+
+	/*
+	 * Check if we have to send a packet before the
+	 * channel switch.
+	 */
+	if (conf->skb) {
+		if (rt2500pci_tx(net_dev, conf->skb, conf->tx_control))
+			goto exit;
+	}
 
 	/*
 	 * Initialize Scanning structure.
@@ -1755,14 +1770,21 @@ rt2500pci_passive_scan(struct net_device
 
 	rt2x00pci->scan->state = state;
 
-	rt2x00pci->scan->cancelled = 0;
+	rt2x00pci->scan->status = 0;
 
 	/*
 	 * Queue work.
 	 */
-	queue_work(rt2x00pci->workqueue, &rt2x00pci->scan_work);
+	if (!queue_work(rt2x00pci->workqueue, &rt2x00pci->scan_work))
+		goto exit;
 
 	return 0;
+
+exit:
+	kfree(rt2x00pci->scan);
+	rt2x00pci->scan = NULL;
+
+	return -EIO;
 }
 
 static int
@@ -2505,20 +2527,15 @@ rt2500pci_uninitialize(struct net_device
 		rt2x00pci->csr_addr = NULL;
 	}
 
-	if (rt2x00pci->scan) {
-		rt2x00pci->scan->cancelled = 1;
-		complete_all(&rt2x00pci->scan->completion);
-	}
-
 	if (likely(rt2x00pci->workqueue)) {
 		flush_workqueue(rt2x00pci->workqueue);
 		destroy_workqueue(rt2x00pci->workqueue);
 		rt2x00pci->workqueue = NULL;
 	}
 
-		kfree(rt2x00pci->hw.modes);
-		rt2x00pci->hw.modes = NULL;
-	}
+	kfree(rt2x00pci->hw.modes);
+	rt2x00pci->hw.modes = NULL;
+}
 
 /*
  * PCI driver handlers.
diff -uprN wireless-dev-rt2x00/drivers/net/wireless/d80211/rt2x00/rt2500usb.c wireless-dev-rt2x00-patch/drivers/net/wireless/d80211/rt2x00/rt2500usb.c
--- wireless-dev-rt2x00/drivers/net/wireless/d80211/rt2x00/rt2500usb.c	2006-04-27 21:47:52.000000000 +0200
+++ wireless-dev-rt2x00-patch/drivers/net/wireless/d80211/rt2x00/rt2500usb.c	2006-04-27 21:48:21.000000000 +0200
@@ -769,7 +769,6 @@ rt2500usb_txdone(void *data)
 	struct net_device	*net_dev = usb_get_intfdata(
 						rt2x00usb->usb_intf);
 	struct data_entry	*entry;
-	struct skb_cb		*cb;
 	struct txd		*txd;
 	int			ack;
 
@@ -777,13 +776,6 @@ rt2500usb_txdone(void *data)
 		entry = rt2x00_get_data_entry_done(ring);
 		txd = entry->desc_addr;
 
-		/*
-		 * Check if this frame was send for the scan routine.
-		 */
-		cb = (struct skb_cb*)&entry->skb->cb;
-		if (rt2x00usb->scan && cb->scan_complete)
-			complete(&rt2x00usb->scan->completion);
-
 		ack = rt2x00_get_field32(txd->word0, TXD_W0_ACK);
 	
 		/*
@@ -815,6 +807,18 @@ rt2500usb_txdone(void *data)
 	
 		rt2x00_ring_index_done_inc(entry->ring);
 	} while (!rt2x00_ring_empty(ring));
+
+	/*
+	 * Check if we are waiting on an empty queue
+	 * to start scanning.
+	 */
+	if (rt2x00usb->scan
+	&& rt2x00_ring_empty(&rt2x00usb->tx)
+	&& rt2x00_ring_empty(&rt2x00usb->atim)
+	&& rt2x00_ring_empty(&rt2x00usb->prio)) {
+		rt2x00usb->scan->status = SCANNING_READY;
+		complete(&rt2x00usb->scan->completion);
+	}
 }
 
 static void
@@ -1368,6 +1372,14 @@ rt2500usb_stop(struct net_device *net_de
 	rt2x00_register_write(rt2x00usb, MAC_CSR14, 0x2121);
 
 	/*
+	 * Cancel scanning.
+	 */
+	if (rt2x00usb->scan) {
+		rt2x00usb->scan->status = SCANNING_CANCELLED;
+		complete_all(&rt2x00usb->scan->completion);
+	}
+
+	/*
 	 * Free DMA rings.
 	 */
 	rt2500usb_free_rings(rt2x00usb);
@@ -1414,36 +1426,23 @@ static void
 rt2500usb_scan(void *data)
 {
 	struct rt2x00_usb	*rt2x00usb = data;
-	struct net_device	*net_dev =
-		usb_get_intfdata(rt2x00usb->usb_intf);
-	struct skb_cb		*cb;
 
 	if (unlikely(!rt2x00usb->scan))
 		return;
 
 	/*
-	 * Check if we have to send a packet before the
-	 * channel switch.
+	 * Before we can start switch the channel for scanning
+	 * we need to wait untill all TX rings are empty to
+	 * guarentee that all frames are send on the correct channel.
 	 */
-	if (rt2x00usb->scan->conf.skb) {
-		cb = (struct skb_cb*)&rt2x00usb->scan->conf.skb->cb;
-		cb->scan_complete = 1;
-
-		if (rt2500usb_tx(net_dev, rt2x00usb->scan->conf.skb,
-			rt2x00usb->scan->conf.tx_control))
-			goto exit;
-
-		/*
-		 * Wait for the frame to be correctly transmitted.
-		 */
+	if (rt2x00usb->scan->status != SCANNING_READY)
 		wait_for_completion(&rt2x00usb->scan->completion);
-	}
 
 	/*
 	 * Check if this scan has been cancelled while
 	 * work was still scheduled.
 	 */
-	if (rt2x00usb->scan->cancelled)
+	if (rt2x00usb->scan->status == SCANNING_CANCELLED)
 		goto exit;
 
 	/*
@@ -1480,9 +1479,24 @@ rt2500usb_passive_scan(struct net_device
 {
 	struct rt2x00_usb	*rt2x00usb = ieee80211_dev_hw_data(net_dev);
 
+	if (rt2x00usb->scan)
+		return -EBUSY;
+
+	/*
+	 * Allocate scanning structure to store scanning info.
+	 */
 	rt2x00usb->scan = kmalloc(sizeof(struct scanning), GFP_ATOMIC);
 	if (!rt2x00usb->scan)
-		return 0;
+		return -ENOMEM;
+
+	/*
+	 * Check if we have to send a packet before the
+	 * channel switch.
+	 */
+	if (conf->skb) {
+		if (rt2500usb_tx(net_dev, conf->skb, conf->tx_control))
+			goto exit;
+	}
 
 	/*
 	 * Initialize Scanning structure.
@@ -1493,14 +1507,21 @@ rt2500usb_passive_scan(struct net_device
 
 	rt2x00usb->scan->state = state;
 
-	rt2x00usb->scan->cancelled = 0;
+	rt2x00usb->scan->status = 0;
 
 	/*
 	 * Queue work.
 	 */
-	queue_work(rt2x00usb->workqueue, &rt2x00usb->scan_work);
+	if (!queue_work(rt2x00usb->workqueue, &rt2x00usb->scan_work))
+		goto exit;
 
 	return 0;
+
+exit:
+	kfree(rt2x00usb->scan);
+	rt2x00usb->scan = NULL;
+
+	return -EIO;
 }
 
 static int
@@ -2121,19 +2142,14 @@ rt2500usb_uninitialize(struct net_device
 
 	kfree(rt2x00usb->eeprom);
 
-	if (rt2x00usb->scan) {
-		rt2x00usb->scan->cancelled = 1;
-		complete_all(&rt2x00usb->scan->completion);
-	}
-
 	if (likely(rt2x00usb->workqueue)) {
 		flush_workqueue(rt2x00usb->workqueue);
 		destroy_workqueue(rt2x00usb->workqueue);
 		rt2x00usb->workqueue = NULL;
 	}
 
-		kfree(rt2x00usb->hw.modes);
-		rt2x00usb->hw.modes = NULL;
+	kfree(rt2x00usb->hw.modes);
+	rt2x00usb->hw.modes = NULL;
 }
 
 /*
diff -uprN wireless-dev-rt2x00/drivers/net/wireless/d80211/rt2x00/rt2x00.h wireless-dev-rt2x00-patch/drivers/net/wireless/d80211/rt2x00/rt2x00.h
--- wireless-dev-rt2x00/drivers/net/wireless/d80211/rt2x00/rt2x00.h	2006-04-27 21:47:52.000000000 +0200
+++ wireless-dev-rt2x00-patch/drivers/net/wireless/d80211/rt2x00/rt2x00.h	2006-04-27 21:48:21.000000000 +0200
@@ -428,15 +428,6 @@ rt2x00_rf(const struct _rt2x00_chip *chi
 }
 
 /*
- * Data to be placed in the skb->cb array.
- * therefor this structure may not exceed the 40 bytes.
- */
-struct skb_cb{
-	int					scan_complete;
-} __attribute__ ((packed));
-
-
-/*
  * data_ring
  * Data rings are used by the device to send and receive packets.
  * The data_addr is the base address of the data memory.
@@ -589,7 +580,9 @@ struct scanning{
 	/*
 	 * Flag to see if this scan has been cancelled.
 	 */
-	short					cancelled;
+	short					status;
+#define SCANNING_READY				0x0001
+#define SCANNING_CANCELLED			0x0002
 } __attribute__ ((packed));