diff options
author | Daniel Drake <dsd@gentoo.org> | 2007-11-19 15:00:29 +0000 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-01-28 15:04:47 -0800 |
commit | 459c51ad6e1fc19e91a53798358433d3c08cd09d (patch) | |
tree | fb86feacf1b229cb4ab6b36b4d1deaf4983b1e45 /drivers/net/wireless/zd1211rw/zd_usb.c | |
parent | 0765af4493193149505f118d04d9300f0a15c8f5 (diff) | |
download | kernel_samsung_smdk4412-459c51ad6e1fc19e91a53798358433d3c08cd09d.zip kernel_samsung_smdk4412-459c51ad6e1fc19e91a53798358433d3c08cd09d.tar.gz kernel_samsung_smdk4412-459c51ad6e1fc19e91a53798358433d3c08cd09d.tar.bz2 |
zd1211rw: port to mac80211
This seems to be working smoothly now. Let's not hold back the mac80211
transition any further. This patch ports the existing driver from softmac
to mac80211.
Many thanks to everyone who helped out with the porting efforts.
Signed-off-by: Daniel Drake <dsd@gentoo.org>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/zd1211rw/zd_usb.c')
-rw-r--r-- | drivers/net/wireless/zd1211rw/zd_usb.c | 282 |
1 files changed, 205 insertions, 77 deletions
diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c index c755b69..3429576 100644 --- a/drivers/net/wireless/zd1211rw/zd_usb.c +++ b/drivers/net/wireless/zd1211rw/zd_usb.c @@ -17,18 +17,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" @@ -353,18 +351,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 +386,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 +516,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 +543,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 +611,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 +635,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 +653,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 +670,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 +681,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 +690,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 +713,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 +723,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 +876,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 +891,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 +953,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 +1092,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 +1106,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 +1126,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 +1197,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, |