Search Linux Wireless

[PATCH] zd1211rw-mac80211: limit URB buffering in tx path

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Ulrich Kunitz <kune@xxxxxxxxxxxxxx>

The old code allowed unlimited buffing of tx frames in URBs
submitted for transfer to the device. This patch stops the
ieee80211_hw queue(s) if to many URBs are ready for submit to the
device. Actually the ZD1211 device supports currently only one
queue.

Signed-off-by: Ulrich Kunitz <kune@xxxxxxxxxxxxxx>
Signed-off-by: Daniel Drake <dsd@xxxxxxxxxx>
---
 drivers/net/wireless/mac80211/zd1211rw/zd_chip.c |    6 +-
 drivers/net/wireless/mac80211/zd1211rw/zd_chip.h |    4 +-
 drivers/net/wireless/mac80211/zd1211rw/zd_mac.c  |  327 +++++++++++++++++-----
 drivers/net/wireless/mac80211/zd1211rw/zd_mac.h  |   23 ++
 drivers/net/wireless/mac80211/zd1211rw/zd_usb.c  |  173 +++++++++---
 drivers/net/wireless/mac80211/zd1211rw/zd_usb.h  |   30 ++-
 6 files changed, 450 insertions(+), 113 deletions(-)

diff --git a/drivers/net/wireless/mac80211/zd1211rw/zd_chip.c b/drivers/net/wireless/mac80211/zd1211rw/zd_chip.c
index d8bc0f1..fcf78ab 100644
--- a/drivers/net/wireless/mac80211/zd1211rw/zd_chip.c
+++ b/drivers/net/wireless/mac80211/zd1211rw/zd_chip.c
@@ -1606,20 +1606,22 @@ void zd_chip_disable_int(struct zd_chip *chip)
 	mutex_unlock(&chip->mutex);
 }
 
-int zd_chip_enable_rx(struct zd_chip *chip)
+int zd_chip_enable_rxtx(struct zd_chip *chip)
 {
 	int r;
 
 	mutex_lock(&chip->mutex);
+	zd_usb_enable_tx(&chip->usb);
 	r = zd_usb_enable_rx(&chip->usb);
 	mutex_unlock(&chip->mutex);
 	return r;
 }
 
-void zd_chip_disable_rx(struct zd_chip *chip)
+void zd_chip_disable_rxtx(struct zd_chip *chip)
 {
 	mutex_lock(&chip->mutex);
 	zd_usb_disable_rx(&chip->usb);
+	zd_usb_disable_tx(&chip->usb);
 	mutex_unlock(&chip->mutex);
 }
 
diff --git a/drivers/net/wireless/mac80211/zd1211rw/zd_chip.h b/drivers/net/wireless/mac80211/zd1211rw/zd_chip.h
index 584b1c8..24f5913 100644
--- a/drivers/net/wireless/mac80211/zd1211rw/zd_chip.h
+++ b/drivers/net/wireless/mac80211/zd1211rw/zd_chip.h
@@ -824,8 +824,8 @@ int zd_chip_switch_radio_on(struct zd_chip *chip);
 int zd_chip_switch_radio_off(struct zd_chip *chip);
 int zd_chip_enable_int(struct zd_chip *chip);
 void zd_chip_disable_int(struct zd_chip *chip);
-int zd_chip_enable_rx(struct zd_chip *chip);
-void zd_chip_disable_rx(struct zd_chip *chip);
+int zd_chip_enable_rxtx(struct zd_chip *chip);
+void zd_chip_disable_rxtx(struct zd_chip *chip);
 int zd_chip_enable_hwint(struct zd_chip *chip);
 int zd_chip_disable_hwint(struct zd_chip *chip);
 int zd_chip_generic_patch_6m_band(struct zd_chip *chip, int channel);
diff --git a/drivers/net/wireless/mac80211/zd1211rw/zd_mac.c b/drivers/net/wireless/mac80211/zd1211rw/zd_mac.c
index 91b908a..c3f8d30 100644
--- a/drivers/net/wireless/mac80211/zd1211rw/zd_mac.c
+++ b/drivers/net/wireless/mac80211/zd1211rw/zd_mac.c
@@ -118,17 +118,17 @@ static int zd_mac_open(struct ieee80211_hw *dev)
 	r = zd_write_mac_addr(chip, mac->hwaddr);
 	if (r)
 		goto disable_radio;
-	r = zd_chip_enable_rx(chip);
+	r = zd_chip_enable_rxtx(chip);
 	if (r < 0)
 		goto disable_radio;
 	r = zd_chip_enable_hwint(chip);
 	if (r < 0)
-		goto disable_rx;
+		goto disable_rxtx;
 
 	housekeeping_enable(mac);
 	return 0;
-disable_rx:
-	zd_chip_disable_rx(chip);
+disable_rxtx:
+	zd_chip_disable_rxtx(chip);
 disable_radio:
 	zd_chip_switch_radio_off(chip);
 disable_int:
@@ -137,11 +137,42 @@ out:
 	return r;
 }
 
+/**
+ * clear_tx_skb_control_block - clears the control block of tx skbuffs
+ * @skb: a &struct sk_buff pointer
+ *
+ * This clears the control block of skbuff buffers, which were transmitted to
+ * the device. Notify that the function is not thread-safe, so prevent
+ * multiple calls.
+ */
+static void clear_tx_skb_control_block(struct sk_buff *skb)
+{
+	struct zd_tx_skb_control_block *cb =
+		(struct zd_tx_skb_control_block *)skb->cb;
+
+	kfree(cb->control);
+	cb->control = NULL;
+}
+
+/**
+ * kfree_tx_skb - frees a tx skbuff
+ * @skb: a &struct sk_buff pointer
+ *
+ * Frees the tx skbuff. Frees also the allocated control structure in the
+ * control block if necessary.
+ */
+static void kfree_tx_skb(struct sk_buff *skb)
+{
+	clear_tx_skb_control_block(skb);
+	dev_kfree_skb_any(skb);
+}
+
 static int zd_mac_stop(struct ieee80211_hw *dev)
 {
 	struct zd_mac *mac = zd_dev_mac(dev);
 	struct zd_chip *chip = &mac->chip;
 	struct sk_buff *skb;
+	struct sk_buff_head *tx_queue = &mac->tx_queue;
 
 	/*
 	 * The order here deliberately is a little different from the open()
@@ -149,23 +180,155 @@ static int zd_mac_stop(struct ieee80211_hw *dev)
 	 * frames to be processed by softmac after we have stopped it.
 	 */
 
-	zd_chip_disable_rx(chip);
+	zd_chip_disable_rxtx(chip);
 	housekeeping_disable(mac);
 
 	zd_chip_disable_hwint(chip);
 	zd_chip_switch_radio_off(chip);
 	zd_chip_disable_int(chip);
 
-	while ((skb = skb_dequeue(&mac->tx_queue))) {
-		struct ieee80211_tx_control *control =
-			*(struct ieee80211_tx_control **)skb->cb;
-		kfree(control);
-		dev_kfree_skb(skb);
+
+	while ((skb = skb_dequeue(tx_queue)))
+		kfree_tx_skb(skb);
+
+	return 0;
+}
+
+/**
+ * wake_queues - wakes all queues
+ * @hw: a &struct ieee80211_hw pointer
+ *
+ * Such a function is not provided by mac80211, so we have to provide them on
+ * our own.
+ */
+static void wake_queues(struct ieee80211_hw *hw)
+{
+	int i;
+
+	for (i = 0; i < hw->queues; i++)
+		ieee80211_wake_queue(hw, i);
+}
+
+/**
+ * tx_frames - returns the number of incompleted frames
+ * @mac: a &struct zd_mac pointer
+ *
+ * This is the number of frames, which have not been completed so far.
+ * Packets without ACKs are completed if the have been transmitted to the
+ * decice and all others if they have been removed from the tx_queue.
+ */
+static int tx_frames(struct zd_mac *mac)
+{
+	return skb_queue_len(&mac->tx_queue) + zd_usb_tx_frames(&mac->chip.usb);
+}
+
+/**
+ * try_stop - if necessary closes the incoming network queues
+ * @dev: a &struct ieee80211_hw pointer
+ *
+ * If the number of incompleted frames is higher than @tx_high, the function
+ * stops the incoming queues of the mac80211 stack. Nothing happens if the
+ * queues have already been stopped.
+ */
+static void try_stop(struct ieee80211_hw *dev)
+{
+	unsigned long flags;
+	struct zd_mac *mac = zd_dev_mac(dev);
+
+	spin_lock_irqsave(&mac->lock, flags);
+	if (!mac->tx_stopped && tx_frames(mac) > mac->tx_high) {
+		ieee80211_stop_queues(dev);
+		mac->tx_stopped = 1;
+	}
+	spin_unlock_irqrestore(&mac->lock, flags);
+}
+
+/**
+ * try_wakeup - wake queue
+ * @dev: a &struct ieee80211_hw pointer
+ *
+ * If the number of incompleted frames drops under the the low level and the
+ * upper-layer transfer queues have been stopped, the queues will be wakened
+ * again.
+ */
+static void try_wakeup(struct ieee80211_hw *dev)
+{
+	unsigned long flags;
+	struct zd_mac *mac = zd_dev_mac(dev);
+
+	spin_lock_irqsave(&mac->lock, flags);
+	if (mac->tx_stopped && tx_frames(mac) <= mac->tx_low) {
+		wake_queues(dev);
+		mac->tx_stopped = 0;
+	}
+	spin_unlock_irqrestore(&mac->lock, flags);
+}
+
+/**
+ * init_tx_skb_control_block - initializes skb control block
+ * @skb: a &sk_buff pointer
+ * @dev: pointer to the mac80221 device
+ * @control: mac80211 tx control applying for the frame in @skb
+ *
+ * Initializes the control block of the skbuff to be transmitted. Notify that
+ * the control parameter will be only copied into the control block, if ACKs
+ * are requieed.
+ */
+static int init_tx_skb_control_block(struct sk_buff *skb,
+				     struct ieee80211_hw *dev,
+	                             struct ieee80211_tx_control *control)
+{
+	struct zd_tx_skb_control_block *cb =
+		(struct zd_tx_skb_control_block *)skb->cb;
+
+	ZD_ASSERT(sizeof(*cb) <= sizeof(skb->cb));
+	memset(cb, 0, sizeof(*cb));
+	cb->dev = dev;
+	if (!(control->flags & IEEE80211_TXCTL_NO_ACK)) {
+		cb->control = kmalloc(sizeof(*control), GFP_ATOMIC);
+		if (cb->control == NULL)
+			return -ENOMEM;
+		memcpy(cb->control, control, sizeof(*control));
 	}
 
 	return 0;
 }
 
+/**
+ * zd_mac_tx_to_dev - callback for USB layer
+ * @skb: a &sk_buff pointer
+ * @error: error value, 0 if transmission successful
+ *
+ * Informs the MAC layer that the frame has successfully transferred to the
+ * device. If an ACK is required and the transfer to the device has been
+ * successful, the packets are put on the @tx_queue with
+ * the control set removed.
+ */
+void zd_mac_tx_to_dev(struct sk_buff *skb, int error)
+{
+	struct zd_tx_skb_control_block *cb =
+		(struct zd_tx_skb_control_block *)skb->cb;
+	struct ieee80211_hw *dev = cb->dev;
+
+	if (likely(cb->control)) {
+		skb_pull(skb, sizeof(struct zd_ctrlset));
+		if (unlikely(error)) {
+			struct ieee80211_tx_status status = {{0}};
+
+			memcpy(&status.control,
+				cb->control, sizeof(status.control));
+			clear_tx_skb_control_block(skb);
+			ieee80211_tx_status_irqsafe(dev, skb, &status);
+		} else {
+			skb_queue_tail(&zd_dev_mac(dev)->tx_queue, skb);
+			return;
+		}
+	} else {
+		kfree_tx_skb(skb);
+	}
+	try_wakeup(dev);
+}
+
 static int zd_calc_tx_length_us(u8 *service, u8 cs_rate, u16 tx_length)
 {
 	static const u8 rate_divisor[] = {
@@ -304,55 +467,64 @@ static int fill_ctrlset(struct zd_mac *mac,
 	return 0;
 }
 
+/**
+ * zd_mac_tx - transmits a network frame to the device
+ *
+ * @dev: mac80211 hardware device
+ * @skb: socket buffer
+ * @control: the control structure
+ *
+ * This function transmit an IEEE 802.11 network frame to the device. The
+ * control block of the skbuff will be initialized. If necessary the incoming
+ * mac80211 queues will be stopped.
+ */
 static int zd_mac_tx(struct ieee80211_hw *dev, struct sk_buff *skb,
 		     struct ieee80211_tx_control *control)
 {
 	struct zd_mac *mac = zd_dev_mac(dev);
-	struct ieee80211_tx_control *control_copy;
 	int r;
 
 	r = fill_ctrlset(mac, skb, control);
 	if (r)
 		return r;
-	r = zd_usb_tx(&mac->chip.usb, skb->data, skb->len);
+
+	r = init_tx_skb_control_block(skb, dev, control);
 	if (r)
 		return r;
-
-	if (control->flags & IEEE80211_TXCTL_NO_ACK) {
-		dev_kfree_skb(skb);
-		return 0;
+	r = zd_usb_tx(&mac->chip.usb, skb);
+	if (r) {
+		clear_tx_skb_control_block(skb);
+		return r;
 	}
-
-	control_copy = kmalloc(sizeof(*control_copy), GFP_ATOMIC);
-	if (control_copy)
-		memcpy(control_copy, control, sizeof(*control_copy));
-
-	*(struct ieee80211_tx_control **)skb->cb = control_copy;
-	skb_pull(skb, sizeof(struct zd_ctrlset));
-	skb_queue_tail(&mac->tx_queue, skb);
+	try_stop(dev);
 	return 0;
 }
 
+/**
+ * zd_mac_tx_failed - callback for failed frames
+ * @dev: the mac80211 wireless device
+ *
+ * This function is called if a frame couldn't be succesfully be
+ * transferred. The first frame from the tx queue, will be selected and
+ * reported as error to the upper layers.
+ */
 void zd_mac_tx_failed(struct ieee80211_hw *dev)
 {
-	struct zd_mac *mac = zd_dev_mac(dev);
-	struct ieee80211_tx_control *control;
+	struct sk_buff_head *tx_queue = &zd_dev_mac(dev)->tx_queue;
 	struct sk_buff *skb;
+	struct ieee80211_tx_status status;
+	struct zd_tx_skb_control_block *cb;
 
-	skb = skb_dequeue(&mac->tx_queue);
-	if (!skb)
+	skb = skb_dequeue(tx_queue);
+	if (skb == NULL)
 		return;
-
-	control = *(struct ieee80211_tx_control **)skb->cb;
-	if (control) {
-		struct ieee80211_tx_status status = {{0}};
-		memcpy(&status.control, control, sizeof(status.control));
-		ieee80211_tx_status_irqsafe(dev, skb, &status);
-		kfree(control);
-	} else
-		dev_kfree_skb_any(skb);
-
-	return;
+	cb = (struct zd_tx_skb_control_block *)skb->cb;
+	ZD_ASSERT(cb->control != NULL);
+	memset(&status, 0, sizeof(status));
+	memcpy(&status.control, cb->control, sizeof(status.control));
+	clear_tx_skb_control_block(skb);
+	ieee80211_tx_status_irqsafe(dev, skb, &status);
+	try_wakeup(dev);
 }
 
 struct zd_rt_hdr {
@@ -416,49 +588,57 @@ static int fill_rx_stats(struct ieee80211_rx_status *stats,
 	return 0;
 }
 
+/**
+ * filter_ack - filters incoming packets for acknowledgements
+ * @dev: the mac80211 device
+ * @rx_hdr: received header
+ * @stats: the status for the received packet
+ *
+ * This functions looks for ACK packets and tries to match them with the
+ * frames in the tx queue. If a match is found the frame will be dequeued and
+ * the upper layers is informed about the successful transmission. If
+ * mac80211 queues have been stopped and the number of frames still to be
+ * transmitted is low the queues will be opened again.
+ */
 static int filter_ack(struct ieee80211_hw *dev, struct ieee80211_hdr *rx_hdr,
 		      struct ieee80211_rx_status *stats)
 {
-	struct zd_mac *mac = zd_dev_mac(dev);
 	u16 fc = le16_to_cpu(rx_hdr->frame_control);
 	struct sk_buff *skb;
-	struct ieee80211_hdr *tx_hdr;
-	struct ieee80211_tx_control *control;
-	struct ieee80211_tx_status status = {{0}};
+	struct sk_buff_head *tx_queue;
+	unsigned long flags;
 
 	if ((fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) !=
 	    (IEEE80211_FTYPE_CTL | IEEE80211_STYPE_ACK))
 		return 0;
 
-	spin_lock(&mac->tx_queue.lock);
-
-	skb = skb_peek(&mac->tx_queue);
-	if (!skb) {
-		spin_unlock(&mac->tx_queue.lock);
-		return 1;
-	}
-
-	tx_hdr = (struct ieee80211_hdr *) skb->data;
-
-	if (!compare_ether_addr(tx_hdr->addr2, rx_hdr->addr1))
-		skb = __skb_dequeue(&mac->tx_queue);
-	else {
-		spin_unlock(&mac->tx_queue.lock);
-		return 1;
+	tx_queue = &zd_dev_mac(dev)->tx_queue;
+	spin_lock_irqsave(&tx_queue->lock, flags);
+	for (skb = tx_queue->next; skb != (struct sk_buff *)tx_queue;
+	     skb = skb->next)
+	{
+		struct ieee80211_hdr *tx_hdr;
+		struct zd_tx_skb_control_block *cb =
+			(struct zd_tx_skb_control_block *)skb->cb;
+
+		ZD_ASSERT(cb->control != NULL);
+		tx_hdr = (struct ieee80211_hdr *)skb->data;
+		if (likely(!compare_ether_addr(tx_hdr->addr2, rx_hdr->addr1)))
+		{
+			struct ieee80211_tx_status status = {{0}};
+			memcpy(&status.control,
+		               cb->control, sizeof(status.control));
+			status.flags = IEEE80211_TX_STATUS_ACK;
+			status.ack_signal = stats->ssi;
+			__skb_unlink(skb, tx_queue);
+			clear_tx_skb_control_block(skb);
+			ieee80211_tx_status_irqsafe(dev, skb, &status);
+			try_wakeup(dev);
+			goto out;
+		}
 	}
-
-	spin_unlock(&mac->tx_queue.lock);
-
-	control = *(struct ieee80211_tx_control **)skb->cb;
-	if (control) {
-		memcpy(&status.control, control, sizeof(status.control));
-		status.flags = IEEE80211_TX_STATUS_ACK;
-		status.ack_signal = stats->ssi;
-		ieee80211_tx_status_irqsafe(dev, skb, &status);
-		kfree(control);
-	} else
-		dev_kfree_skb_any(skb);
-
+out:
+	spin_unlock_irqrestore(&tx_queue->lock, flags);
 	return 1;
 }
 
@@ -639,6 +819,10 @@ struct ieee80211_hw *zd_mac_alloc(struct usb_interface *intf)
 	dev->queues = 1;
 	dev->extra_tx_headroom = sizeof(struct zd_ctrlset);
 
+	mac->tx_low = ZD_MAC_TX_LOW;
+	mac->tx_high = ZD_MAC_TX_HIGH;
+	skb_queue_head_init(&mac->tx_queue);
+
 	for (i = 0; i < 2; i++) {
 		if (ieee80211_register_hwmode(dev, &mac->modes[i])) {
 			dev_dbg_f(&intf->dev, "cannot register hwmode\n");
@@ -647,7 +831,6 @@ struct ieee80211_hw *zd_mac_alloc(struct usb_interface *intf)
 		}
 	}
 
-	skb_queue_head_init(&mac->tx_queue);
 	zd_chip_init(&mac->chip, dev, intf);
 	housekeeping_init(mac);
 	INIT_WORK(&mac->set_multicast_hash_work, set_multicast_hash_handler);
diff --git a/drivers/net/wireless/mac80211/zd1211rw/zd_mac.h b/drivers/net/wireless/mac80211/zd1211rw/zd_mac.h
index ec02765..615c5fb 100644
--- a/drivers/net/wireless/mac80211/zd1211rw/zd_mac.h
+++ b/drivers/net/wireless/mac80211/zd1211rw/zd_mac.h
@@ -122,8 +122,27 @@ struct housekeeping {
 	struct delayed_work link_led_work;
 };
 
+/**
+ * struct zd_tx_skb_control_block - control block for tx skbuffs
+ * @control: &struct ieee80211_tx_control pointer
+ * @context: context pointer
+ *
+ * This structure is used to fill the cb field in an &sk_buff to transmit.
+ * The control field is NULL, if there is no requirement from the mac80211
+ * stack to report about the packet ACK. This is the case if the flag
+ * IEEE80211_TXCTL_NO_ACK is not set in &struct ieee80211_tx_control.
+ */
+struct zd_tx_skb_control_block {
+	struct ieee80211_tx_control *control;
+	struct ieee80211_hw *dev;
+	void *context;
+};
+
 #define ZD_MAC_STATS_BUFFER_SIZE 16
 
+#define ZD_MAC_TX_HIGH 6
+#define ZD_MAC_TX_LOW 2
+
 struct zd_mac {
 	struct zd_chip chip;
 	spinlock_t lock;
@@ -137,6 +156,9 @@ struct zd_mac {
 	int associated;
 	u8 *hwaddr;
 	struct sk_buff_head tx_queue;
+	int tx_high;
+	int tx_low;
+	int tx_stopped;
 	struct ieee80211_channel channels[14];
 	struct ieee80211_rate rates[12];
 	struct ieee80211_hw_mode modes[2];
@@ -166,6 +188,7 @@ int zd_mac_init_hw(struct ieee80211_hw *dev, u8 device_type);
 
 int zd_mac_rx(struct ieee80211_hw *dev, const u8 *buffer, unsigned int length);
 void zd_mac_tx_failed(struct ieee80211_hw *dev);
+void zd_mac_tx_to_dev(struct sk_buff *skb, int error);
 
 #ifdef DEBUG
 void zd_dump_rx_status(const struct rx_status *status);
diff --git a/drivers/net/wireless/mac80211/zd1211rw/zd_usb.c b/drivers/net/wireless/mac80211/zd1211rw/zd_usb.c
index 533f189..4212310 100644
--- a/drivers/net/wireless/mac80211/zd1211rw/zd_usb.c
+++ b/drivers/net/wireless/mac80211/zd1211rw/zd_usb.c
@@ -574,7 +574,7 @@ resubmit:
 	usb_submit_urb(urb, GFP_ATOMIC);
 }
 
-static struct urb *alloc_urb(struct zd_usb *usb)
+static struct urb *alloc_rx_urb(struct zd_usb *usb)
 {
 	struct usb_device *udev = zd_usb_to_usbdev(usb);
 	struct urb *urb;
@@ -598,7 +598,7 @@ static struct urb *alloc_urb(struct zd_usb *usb)
 	return urb;
 }
 
-static void free_urb(struct urb *urb)
+static void free_rx_urb(struct urb *urb)
 {
 	if (!urb)
 		return;
@@ -616,11 +616,11 @@ int zd_usb_enable_rx(struct zd_usb *usb)
 	dev_dbg_f(zd_usb_dev(usb), "\n");
 
 	r = -ENOMEM;
-	urbs = kcalloc(URBS_COUNT, sizeof(struct urb *), GFP_KERNEL);
+	urbs = kcalloc(RX_URBS_COUNT, sizeof(struct urb *), GFP_KERNEL);
 	if (!urbs)
 		goto error;
-	for (i = 0; i < URBS_COUNT; i++) {
-		urbs[i] = alloc_urb(usb);
+	for (i = 0; i < RX_URBS_COUNT; i++) {
+		urbs[i] = alloc_rx_urb(usb);
 		if (!urbs[i])
 			goto error;
 	}
@@ -633,10 +633,10 @@ int zd_usb_enable_rx(struct zd_usb *usb)
 		goto error;
 	}
 	rx->urbs = urbs;
-	rx->urbs_count = URBS_COUNT;
+	rx->urbs_count = RX_URBS_COUNT;
 	spin_unlock_irq(&rx->lock);
 
-	for (i = 0; i < URBS_COUNT; i++) {
+	for (i = 0; i < RX_URBS_COUNT; i++) {
 		r = usb_submit_urb(urbs[i], GFP_KERNEL);
 		if (r)
 			goto error_submit;
@@ -644,7 +644,7 @@ int zd_usb_enable_rx(struct zd_usb *usb)
 
 	return 0;
 error_submit:
-	for (i = 0; i < URBS_COUNT; i++) {
+	for (i = 0; i < RX_URBS_COUNT; i++) {
 		usb_kill_urb(urbs[i]);
 	}
 	spin_lock_irq(&rx->lock);
@@ -653,8 +653,8 @@ error_submit:
 	spin_unlock_irq(&rx->lock);
 error:
 	if (urbs) {
-		for (i = 0; i < URBS_COUNT; i++)
-			free_urb(urbs[i]);
+		for (i = 0; i < RX_URBS_COUNT; i++)
+			free_rx_urb(urbs[i]);
 	}
 	return r;
 }
@@ -676,7 +676,7 @@ void zd_usb_disable_rx(struct zd_usb *usb)
 
 	for (i = 0; i < count; i++) {
 		usb_kill_urb(urbs[i]);
-		free_urb(urbs[i]);
+		free_rx_urb(urbs[i]);
 	}
 	kfree(urbs);
 
@@ -686,9 +686,109 @@ void zd_usb_disable_rx(struct zd_usb *usb)
 	spin_unlock_irqrestore(&rx->lock, flags);
 }
 
+/**
+ * zd_usb_disable_tx - disable transmission
+ * @usb: the zd1211rw-private USB structure
+ *
+ * Frees all URBs in the free list and marks the transmission as disabled.
+ */
+void zd_usb_disable_tx(struct zd_usb *usb)
+{
+	struct zd_usb_tx *tx = &usb->tx;
+	unsigned long flags;
+	struct list_head *pos, *n;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	list_for_each_safe(pos, n, &tx->free_urb_list) {
+		list_del(pos);
+		usb_free_urb(list_entry(pos, struct urb, urb_list));
+	}
+	tx->enabled = 0;
+	atomic_set(&tx->submitted_urbs, 0);
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+/**
+ * zd_usb_enable_tx - enables transmission
+ * @usb: a &struct zd_usb pointer
+ *
+ * This function enables transmission and prepares the &zd_usb_tx data
+ * structure.
+ */
+void zd_usb_enable_tx(struct zd_usb *usb)
+{
+	unsigned long flags;
+	struct zd_usb_tx *tx = &usb->tx;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	tx->enabled = 1;
+	atomic_set(&tx->submitted_urbs, 0);
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+/**
+ * alloc_tx_urb - provides an tx URB
+ * @usb: a &struct zd_usb pointer
+ *
+ * Allocates a new URB. If possible takes the urb from the free list in
+ * usb->tx.
+ */
+static struct urb *alloc_tx_urb(struct zd_usb *usb)
+{
+	struct zd_usb_tx *tx = &usb->tx;
+	unsigned long flags;
+	struct list_head *entry;
+	struct urb *urb;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	if (list_empty(&tx->free_urb_list)) {
+		urb = usb_alloc_urb(0, GFP_ATOMIC);
+		goto out;
+	}
+	entry = tx->free_urb_list.next;
+	list_del(entry);
+	urb = list_entry(entry, struct urb, urb_list);
+out:
+	spin_unlock_irqrestore(&tx->lock, flags);
+	return urb;
+}
+
+/**
+ * free_tx_urb - frees a used tx URB
+ * @usb: a &struct zd_usb pointer
+ * @urb: URB to be freed
+ *
+ * Frees the the transmission URB, which means to put it on the free URB
+ * list.
+ */
+static void free_tx_urb(struct zd_usb *usb, struct urb *urb)
+{
+	struct zd_usb_tx *tx = &usb->tx;
+	unsigned long flags;
+
+	spin_lock_irqsave(&tx->lock, flags);
+	if (!tx->enabled) {
+		usb_free_urb(urb);
+		goto out;
+	}
+	list_add(&urb->urb_list, &tx->free_urb_list);
+out:
+	spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+/**
+ * tx_urb_complete - completes the execution of an URB
+ * @urb: a URB
+ *
+ * This function is called if the URB has been transferred to a device or an
+ * error has happened.
+ */
 static void tx_urb_complete(struct urb *urb)
 {
 	int r;
+	struct sk_buff *skb;
+	struct zd_tx_skb_control_block *cb;
+	struct zd_usb *usb;
 
 	switch (urb->status) {
 	case 0:
@@ -706,9 +806,12 @@ static void tx_urb_complete(struct urb *urb)
 		goto resubmit;
 	}
 free_urb:
-	usb_buffer_free(urb->dev, urb->transfer_buffer_length,
-		        urb->transfer_buffer, urb->transfer_dma);
-	usb_free_urb(urb);
+	skb = (struct sk_buff *)urb->context;
+	zd_mac_tx_to_dev(skb, urb->status);
+	cb = (struct zd_tx_skb_control_block *)skb->cb;
+	usb = &zd_dev_mac(cb->dev)->chip.usb;
+	atomic_dec(&usb->tx.submitted_urbs);
+	free_tx_urb(usb, urb);
 	return;
 resubmit:
 	r = usb_submit_urb(urb, GFP_ATOMIC);
@@ -718,43 +821,40 @@ resubmit:
 	}
 }
 
-/* Puts the frame on the USB endpoint. It doesn't wait for
- * completion. The frame must contain the control set.
+/**
+ * zd_usb_tx: initiates transfer of a frame of the device
+ *
+ * @usb: the zd1211rw-private USB structure
+ * @skb: a &struct sk_buff pointer
+ *
+ * This function tranmits a frame to the device. It doesn't wait for
+ * completion. The frame must contain the control set and have all the
+ * control set information available.
+ *
+ * The function returns 0 if the transfer has been successfully initiated.
  */
-int zd_usb_tx(struct zd_usb *usb, const u8 *frame, unsigned int length)
+int zd_usb_tx(struct zd_usb *usb, struct sk_buff *skb)
 {
 	int r;
 	struct usb_device *udev = zd_usb_to_usbdev(usb);
 	struct urb *urb;
-	void *buffer;
 
-	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	urb = alloc_tx_urb(usb);
 	if (!urb) {
 		r = -ENOMEM;
 		goto out;
 	}
 
-	buffer = usb_buffer_alloc(zd_usb_to_usbdev(usb), length, GFP_ATOMIC,
-		                  &urb->transfer_dma);
-	if (!buffer) {
-		r = -ENOMEM;
-		goto error_free_urb;
-	}
-	memcpy(buffer, frame, length);
-
 	usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
-		          buffer, length, tx_urb_complete, NULL);
-	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+		          skb->data, skb->len, tx_urb_complete, skb);
 
 	r = usb_submit_urb(urb, GFP_ATOMIC);
 	if (r)
 		goto error;
+	atomic_inc(&usb->tx.submitted_urbs);
 	return 0;
 error:
-	usb_buffer_free(zd_usb_to_usbdev(usb), length, buffer,
-		        urb->transfer_dma);
-error_free_urb:
-	usb_free_urb(urb);
+	free_tx_urb(usb, urb);
 out:
 	return r;
 }
@@ -783,8 +883,11 @@ static inline void init_usb_rx(struct zd_usb *usb)
 
 static inline void init_usb_tx(struct zd_usb *usb)
 {
-	/* FIXME: at this point we will allocate a fixed number of urb's for
-	 * use in a cyclic scheme */
+	struct zd_usb_tx *tx = &usb->tx;
+	spin_lock_init(&tx->lock);
+	tx->enabled = 0;
+	INIT_LIST_HEAD(&tx->free_urb_list);
+	atomic_set(&tx->submitted_urbs, 0);
 }
 
 void zd_usb_init(struct zd_usb *usb, struct ieee80211_hw *dev,
diff --git a/drivers/net/wireless/mac80211/zd1211rw/zd_usb.h b/drivers/net/wireless/mac80211/zd1211rw/zd_usb.h
index bcc55e8..f01d0bb 100644
--- a/drivers/net/wireless/mac80211/zd1211rw/zd_usb.h
+++ b/drivers/net/wireless/mac80211/zd1211rw/zd_usb.h
@@ -165,7 +165,7 @@ static inline struct usb_int_regs *get_read_regs(struct zd_usb_interrupt *intr)
 	return (struct usb_int_regs *)intr->read_regs.buffer;
 }
 
-#define URBS_COUNT 5
+#define RX_URBS_COUNT 5
 
 struct zd_usb_rx {
 	spinlock_t lock;
@@ -176,8 +176,19 @@ struct zd_usb_rx {
 	int urbs_count;
 };
 
+/**
+ * struct zd_usb_tx - structure used for transmitting frames
+ * @lock: lock for transmission
+ * @free_urb_list: list of free URBs, contains all the URBs, which can be used
+ * @submitted_urbs: atomic integer that counts the URBs having sent to the
+ *	device, which haven't been completed
+ * @enabled: enabled flag, indicates whether tx is enabled
+ */
 struct zd_usb_tx {
 	spinlock_t lock;
+	struct list_head free_urb_list;
+	atomic_t submitted_urbs;
+	int enabled;
 };
 
 /* Contains the usb parts. The structure doesn't require a lock because intf
@@ -220,7 +231,22 @@ void zd_usb_disable_int(struct zd_usb *usb);
 int zd_usb_enable_rx(struct zd_usb *usb);
 void zd_usb_disable_rx(struct zd_usb *usb);
 
-int zd_usb_tx(struct zd_usb *usb, const u8 *frame, unsigned int length);
+void zd_usb_enable_tx(struct zd_usb *usb);
+void zd_usb_disable_tx(struct zd_usb *usb);
+
+int zd_usb_tx(struct zd_usb *usb, struct sk_buff *skb);
+
+/**
+ * zd_usb_tx_frames - frames in transfer to the device
+ * @usb: a &struct zd_usb pointer
+ *
+ * This function returns the number of frames, which are currently
+ * transmitted to the device.
+ */
+static inline int zd_usb_tx_frames(struct zd_usb *usb)
+{
+	return atomic_read(&usb->tx.submitted_urbs);
+}
 
 int zd_usb_ioread16v(struct zd_usb *usb, u16 *values,
 	         const zd_addr_t *addresses, unsigned int count);
-- 
1.5.1.2

-
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Host AP]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Linux Kernel]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Samba]     [Device Mapper]
  Powered by Linux