aboutsummaryrefslogtreecommitdiff
path: root/drivers/net/wireless/zd1211rw/zd_usb.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/zd1211rw/zd_usb.c')
-rw-r--r--drivers/net/wireless/zd1211rw/zd_usb.c289
1 files changed, 211 insertions, 78 deletions
diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c
index c755b692381..7942b15acfe 100644
--- a/drivers/net/wireless/zd1211rw/zd_usb.c
+++ b/drivers/net/wireless/zd1211rw/zd_usb.c
@@ -1,4 +1,8 @@
-/* zd_usb.c
+/* ZD1211 USB-WLAN driver for Linux
+ *
+ * Copyright (C) 2005-2007 Ulrich Kunitz <kune@deine-taler.de>
+ * Copyright (C) 2006-2007 Daniel Drake <dsd@gentoo.org>
+ * Copyright (C) 2006-2007 Michael Wu <flamingice@sourmilk.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -17,18 +21,16 @@
#include <linux/kernel.h>
#include <linux/init.h>
-#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/skbuff.h>
#include <linux/usb.h>
#include <linux/workqueue.h>
-#include <net/ieee80211.h>
+#include <net/mac80211.h>
#include <asm/unaligned.h>
#include "zd_def.h"
-#include "zd_netdev.h"
#include "zd_mac.h"
#include "zd_usb.h"
@@ -55,6 +57,7 @@ static struct usb_device_id usb_ids[] = {
{ USB_DEVICE(0x13b1, 0x001e), .driver_info = DEVICE_ZD1211 },
{ USB_DEVICE(0x0586, 0x3407), .driver_info = DEVICE_ZD1211 },
{ USB_DEVICE(0x129b, 0x1666), .driver_info = DEVICE_ZD1211 },
+ { USB_DEVICE(0x157e, 0x300a), .driver_info = DEVICE_ZD1211 },
/* ZD1211B */
{ USB_DEVICE(0x0ace, 0x1215), .driver_info = DEVICE_ZD1211B },
{ USB_DEVICE(0x157e, 0x300d), .driver_info = DEVICE_ZD1211B },
@@ -353,18 +356,6 @@ out:
spin_unlock(&intr->lock);
}
-static inline void handle_retry_failed_int(struct urb *urb)
-{
- struct zd_usb *usb = urb->context;
- struct zd_mac *mac = zd_usb_to_mac(usb);
- struct ieee80211_device *ieee = zd_mac_to_ieee80211(mac);
-
- ieee->stats.tx_errors++;
- ieee->ieee_stats.tx_retry_limit_exceeded++;
- dev_dbg_f(urb_dev(urb), "retry failed interrupt\n");
-}
-
-
static void int_urb_complete(struct urb *urb)
{
int r;
@@ -400,7 +391,7 @@ static void int_urb_complete(struct urb *urb)
handle_regs_int(urb);
break;
case USB_INT_ID_RETRY_FAILED:
- handle_retry_failed_int(urb);
+ zd_mac_tx_failed(zd_usb_to_hw(urb->context));
break;
default:
dev_dbg_f(urb_dev(urb), "error: urb %p unknown id %x\n", urb,
@@ -530,14 +521,10 @@ static void handle_rx_packet(struct zd_usb *usb, const u8 *buffer,
unsigned int length)
{
int i;
- struct zd_mac *mac = zd_usb_to_mac(usb);
const struct rx_length_info *length_info;
if (length < sizeof(struct rx_length_info)) {
/* It's not a complete packet anyhow. */
- struct ieee80211_device *ieee = zd_mac_to_ieee80211(mac);
- ieee->stats.rx_errors++;
- ieee->stats.rx_length_errors++;
return;
}
length_info = (struct rx_length_info *)
@@ -561,13 +548,13 @@ static void handle_rx_packet(struct zd_usb *usb, const u8 *buffer,
n = l+k;
if (n > length)
return;
- zd_mac_rx_irq(mac, buffer+l, k);
+ zd_mac_rx(zd_usb_to_hw(usb), buffer+l, k);
if (i >= 2)
return;
l = (n+3) & ~3;
}
} else {
- zd_mac_rx_irq(mac, buffer, length);
+ zd_mac_rx(zd_usb_to_hw(usb), buffer, length);
}
}
@@ -629,7 +616,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;
@@ -653,7 +640,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;
@@ -671,11 +658,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;
}
@@ -688,10 +675,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;
@@ -699,7 +686,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);
@@ -708,8 +695,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;
}
@@ -731,7 +718,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);
@@ -741,9 +728,142 @@ 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;
+ tx->submitted_urbs = 0;
+ /* The stopped state is ignored, relying on ieee80211_wake_queues()
+ * in a potentionally following zd_usb_enable_tx().
+ */
+ 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;
+ tx->submitted_urbs = 0;
+ ieee80211_wake_queues(zd_usb_to_hw(usb));
+ tx->stopped = 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);
+}
+
+static void tx_dec_submitted_urbs(struct zd_usb *usb)
+{
+ struct zd_usb_tx *tx = &usb->tx;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tx->lock, flags);
+ --tx->submitted_urbs;
+ if (tx->stopped && tx->submitted_urbs <= ZD_USB_TX_LOW) {
+ ieee80211_wake_queues(zd_usb_to_hw(usb));
+ tx->stopped = 0;
+ }
+ spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+static void tx_inc_submitted_urbs(struct zd_usb *usb)
+{
+ struct zd_usb_tx *tx = &usb->tx;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tx->lock, flags);
+ ++tx->submitted_urbs;
+ if (!tx->stopped && tx->submitted_urbs > ZD_USB_TX_HIGH) {
+ ieee80211_stop_queues(zd_usb_to_hw(usb));
+ tx->stopped = 1;
+ }
+ 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:
@@ -761,9 +881,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_hw_mac(cb->hw)->chip.usb;
+ free_tx_urb(usb, urb);
+ tx_dec_submitted_urbs(usb);
return;
resubmit:
r = usb_submit_urb(urb, GFP_ATOMIC);
@@ -773,43 +896,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;
+ tx_inc_submitted_urbs(usb);
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;
}
@@ -838,16 +958,20 @@ 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;
+ tx->stopped = 0;
+ INIT_LIST_HEAD(&tx->free_urb_list);
+ tx->submitted_urbs = 0;
}
-void zd_usb_init(struct zd_usb *usb, struct net_device *netdev,
+void zd_usb_init(struct zd_usb *usb, struct ieee80211_hw *hw,
struct usb_interface *intf)
{
memset(usb, 0, sizeof(*usb));
usb->intf = usb_get_intf(intf);
- usb_set_intfdata(usb->intf, netdev);
+ usb_set_intfdata(usb->intf, hw);
init_usb_interrupt(usb);
init_usb_tx(usb);
init_usb_rx(usb);
@@ -973,7 +1097,7 @@ int zd_usb_init_hw(struct zd_usb *usb)
return r;
}
- r = zd_mac_init_hw(mac);
+ r = zd_mac_init_hw(mac->hw);
if (r) {
dev_dbg_f(zd_usb_dev(usb),
"couldn't initialize mac. Error number %d\n", r);
@@ -987,9 +1111,9 @@ int zd_usb_init_hw(struct zd_usb *usb)
static int probe(struct usb_interface *intf, const struct usb_device_id *id)
{
int r;
- struct zd_usb *usb;
struct usb_device *udev = interface_to_usbdev(intf);
- struct net_device *netdev = NULL;
+ struct zd_usb *usb;
+ struct ieee80211_hw *hw = NULL;
print_id(udev);
@@ -1007,57 +1131,65 @@ static int probe(struct usb_interface *intf, const struct usb_device_id *id)
goto error;
}
- usb_reset_device(interface_to_usbdev(intf));
+ r = usb_reset_device(udev);
+ if (r) {
+ dev_err(&intf->dev,
+ "couldn't reset usb device. Error number %d\n", r);
+ goto error;
+ }
- netdev = zd_netdev_alloc(intf);
- if (netdev == NULL) {
+ hw = zd_mac_alloc_hw(intf);
+ if (hw == NULL) {
r = -ENOMEM;
goto error;
}
- usb = &zd_netdev_mac(netdev)->chip.usb;
+ usb = &zd_hw_mac(hw)->chip.usb;
usb->is_zd1211b = (id->driver_info == DEVICE_ZD1211B) != 0;
- r = zd_mac_preinit_hw(zd_netdev_mac(netdev));
+ r = zd_mac_preinit_hw(hw);
if (r) {
dev_dbg_f(&intf->dev,
"couldn't initialize mac. Error number %d\n", r);
goto error;
}
- r = register_netdev(netdev);
+ r = ieee80211_register_hw(hw);
if (r) {
dev_dbg_f(&intf->dev,
- "couldn't register netdev. Error number %d\n", r);
+ "couldn't register device. Error number %d\n", r);
goto error;
}
dev_dbg_f(&intf->dev, "successful\n");
- dev_info(&intf->dev,"%s\n", netdev->name);
+ dev_info(&intf->dev, "%s\n", wiphy_name(hw->wiphy));
return 0;
error:
usb_reset_device(interface_to_usbdev(intf));
- zd_netdev_free(netdev);
+ if (hw) {
+ zd_mac_clear(zd_hw_mac(hw));
+ ieee80211_free_hw(hw);
+ }
return r;
}
static void disconnect(struct usb_interface *intf)
{
- struct net_device *netdev = zd_intf_to_netdev(intf);
+ struct ieee80211_hw *hw = zd_intf_to_hw(intf);
struct zd_mac *mac;
struct zd_usb *usb;
/* Either something really bad happened, or we're just dealing with
* a DEVICE_INSTALLER. */
- if (netdev == NULL)
+ if (hw == NULL)
return;
- mac = zd_netdev_mac(netdev);
+ mac = zd_hw_mac(hw);
usb = &mac->chip.usb;
dev_dbg_f(zd_usb_dev(usb), "\n");
- zd_netdev_disconnect(netdev);
+ ieee80211_unregister_hw(hw);
/* Just in case something has gone wrong! */
zd_usb_disable_rx(usb);
@@ -1070,12 +1202,13 @@ static void disconnect(struct usb_interface *intf)
*/
usb_reset_device(interface_to_usbdev(intf));
- zd_netdev_free(netdev);
+ zd_mac_clear(mac);
+ ieee80211_free_hw(hw);
dev_dbg(&intf->dev, "disconnected\n");
}
static struct usb_driver driver = {
- .name = "zd1211rw",
+ .name = KBUILD_MODNAME,
.id_table = usb_ids,
.probe = probe,
.disconnect = disconnect,