From: Ulrich Kunitz <[EMAIL PROTECTED]>

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 <[EMAIL PROTECTED]>
Signed-off-by: Daniel Drake <[EMAIL PROTECTED]>
---
 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 netdev" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to