[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-Id: <20070501030100.BDE3D7B409F@zog.reactivated.net>
Date: Tue, 1 May 2007 04:01:00 +0100 (BST)
From: Daniel Drake <dsd@...too.org>
To: linville@...driver.com
Cc: kune@...ne-taler.de
Subject: [PATCH] zd1211rw-mac80211: limit URB buffering in tx path
From: Ulrich Kunitz <kune@...ne-taler.de>
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@...ne-taler.de>
Signed-off-by: Daniel Drake <dsd@...too.org>
---
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 majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Powered by blists - more mailing lists