Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
[safe/jmp/linux-2.6] / drivers / net / wireless / rt2x00 / rt2x00usb.c
index c507b0d..c9cbdaa 100644 (file)
@@ -1,5 +1,5 @@
 /*
-       Copyright (C) 2004 - 2008 rt2x00 SourceForge Project
+       Copyright (C) 2004 - 2009 rt2x00 SourceForge Project
        <http://rt2x00.serialmonkey.com>
 
        This program is free software; you can redistribute it and/or modify
@@ -47,6 +47,8 @@ int rt2x00usb_vendor_request(struct rt2x00_dev *rt2x00dev,
            (requesttype == USB_VENDOR_REQUEST_IN) ?
            usb_rcvctrlpipe(usb_dev, 0) : usb_sndctrlpipe(usb_dev, 0);
 
+       if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags))
+               return -ENODEV;
 
        for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
                status = usb_control_msg(usb_dev, pipe, request, requesttype,
@@ -60,8 +62,10 @@ int rt2x00usb_vendor_request(struct rt2x00_dev *rt2x00dev,
                 * -ENODEV: Device has disappeared, no point continuing.
                 * All other errors: Try again.
                 */
-               else if (status == -ENODEV)
+               else if (status == -ENODEV) {
+                       clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags);
                        break;
+               }
        }
 
        ERROR(rt2x00dev,
@@ -154,6 +158,31 @@ int rt2x00usb_vendor_request_large_buff(struct rt2x00_dev *rt2x00dev,
 }
 EXPORT_SYMBOL_GPL(rt2x00usb_vendor_request_large_buff);
 
+int rt2x00usb_regbusy_read(struct rt2x00_dev *rt2x00dev,
+                          const unsigned int offset,
+                          const struct rt2x00_field32 field,
+                          u32 *reg)
+{
+       unsigned int i;
+
+       if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags))
+               return -ENODEV;
+
+       for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
+               rt2x00usb_register_read_lock(rt2x00dev, offset, reg);
+               if (!rt2x00_get_field32(*reg, field))
+                       return 1;
+               udelay(REGISTER_BUSY_DELAY);
+       }
+
+       ERROR(rt2x00dev, "Indirect register access failed: "
+             "offset=0x%.08x, value=0x%.08x\n", offset, *reg);
+       *reg = ~0;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rt2x00usb_regbusy_read);
+
 /*
  * TX data handlers.
  */
@@ -212,10 +241,10 @@ int rt2x00usb_write_tx_data(struct queue_entry *entry)
         * length of the data to usb_fill_bulk_urb. Pass the skb
         * to the driver to determine what the length should be.
         */
-       length = rt2x00dev->ops->lib->get_tx_data_len(rt2x00dev, entry->skb);
+       length = rt2x00dev->ops->lib->get_tx_data_len(entry);
 
        usb_fill_bulk_urb(entry_priv->urb, usb_dev,
-                         usb_sndbulkpipe(usb_dev, 1),
+                         usb_sndbulkpipe(usb_dev, entry->queue->usb_endpoint),
                          entry->skb->data, length,
                          rt2x00usb_interrupt_txdone, entry);
 
@@ -274,6 +303,41 @@ void rt2x00usb_kick_tx_queue(struct rt2x00_dev *rt2x00dev,
 }
 EXPORT_SYMBOL_GPL(rt2x00usb_kick_tx_queue);
 
+void rt2x00usb_kill_tx_queue(struct rt2x00_dev *rt2x00dev,
+                            const enum data_queue_qid qid)
+{
+       struct data_queue *queue = rt2x00queue_get_queue(rt2x00dev, qid);
+       struct queue_entry_priv_usb *entry_priv;
+       struct queue_entry_priv_usb_bcn *bcn_priv;
+       unsigned int i;
+       bool kill_guard;
+
+       /*
+        * When killing the beacon queue, we must also kill
+        * the beacon guard byte.
+        */
+       kill_guard =
+           (qid == QID_BEACON) &&
+           (test_bit(DRIVER_REQUIRE_BEACON_GUARD, &rt2x00dev->flags));
+
+       /*
+        * Cancel all entries.
+        */
+       for (i = 0; i < queue->limit; i++) {
+               entry_priv = queue->entries[i].priv_data;
+               usb_kill_urb(entry_priv->urb);
+
+               /*
+                * Kill guardian urb (if required by driver).
+                */
+               if (kill_guard) {
+                       bcn_priv = queue->entries[i].priv_data;
+                       usb_kill_urb(bcn_priv->guardian_urb);
+               }
+       }
+}
+EXPORT_SYMBOL_GPL(rt2x00usb_kill_tx_queue);
+
 /*
  * RX data handlers.
  */
@@ -316,35 +380,14 @@ static void rt2x00usb_interrupt_rxdone(struct urb *urb)
  */
 void rt2x00usb_disable_radio(struct rt2x00_dev *rt2x00dev)
 {
-       struct queue_entry_priv_usb *entry_priv;
-       struct queue_entry_priv_usb_bcn *bcn_priv;
-       struct data_queue *queue;
-       unsigned int i;
-
        rt2x00usb_vendor_request_sw(rt2x00dev, USB_RX_CONTROL, 0, 0,
                                    REGISTER_TIMEOUT);
 
        /*
-        * Cancel all queues.
-        */
-       queue_for_each(rt2x00dev, queue) {
-               for (i = 0; i < queue->limit; i++) {
-                       entry_priv = queue->entries[i].priv_data;
-                       usb_kill_urb(entry_priv->urb);
-               }
-       }
-
-       /*
-        * Kill guardian urb (if required by driver).
+        * The USB version of kill_tx_queue also works
+        * on the RX queue.
         */
-       if (!test_bit(DRIVER_REQUIRE_BEACON_GUARD, &rt2x00dev->flags))
-               return;
-
-       for (i = 0; i < rt2x00dev->bcn->limit; i++) {
-               bcn_priv = rt2x00dev->bcn->entries[i].priv_data;
-               if (bcn_priv->guardian_urb)
-                       usb_kill_urb(bcn_priv->guardian_urb);
-       }
+       rt2x00dev->ops->lib->kill_tx_queue(rt2x00dev, QID_RX);
 }
 EXPORT_SYMBOL_GPL(rt2x00usb_disable_radio);
 
@@ -356,10 +399,11 @@ void rt2x00usb_clear_entry(struct queue_entry *entry)
        struct usb_device *usb_dev =
            to_usb_device_intf(entry->queue->rt2x00dev->dev);
        struct queue_entry_priv_usb *entry_priv = entry->priv_data;
+       int pipe;
 
        if (entry->queue->qid == QID_RX) {
-               usb_fill_bulk_urb(entry_priv->urb, usb_dev,
-                               usb_rcvbulkpipe(usb_dev, 1),
+               pipe = usb_rcvbulkpipe(usb_dev, entry->queue->usb_endpoint);
+               usb_fill_bulk_urb(entry_priv->urb, usb_dev, pipe,
                                entry->skb->data, entry->skb->len,
                                rt2x00usb_interrupt_rxdone, entry);
 
@@ -371,6 +415,76 @@ void rt2x00usb_clear_entry(struct queue_entry *entry)
 }
 EXPORT_SYMBOL_GPL(rt2x00usb_clear_entry);
 
+static void rt2x00usb_assign_endpoint(struct data_queue *queue,
+                                     struct usb_endpoint_descriptor *ep_desc)
+{
+       struct usb_device *usb_dev = to_usb_device_intf(queue->rt2x00dev->dev);
+       int pipe;
+
+       queue->usb_endpoint = usb_endpoint_num(ep_desc);
+
+       if (queue->qid == QID_RX) {
+               pipe = usb_rcvbulkpipe(usb_dev, queue->usb_endpoint);
+               queue->usb_maxpacket = usb_maxpacket(usb_dev, pipe, 0);
+       } else {
+               pipe = usb_sndbulkpipe(usb_dev, queue->usb_endpoint);
+               queue->usb_maxpacket = usb_maxpacket(usb_dev, pipe, 1);
+       }
+
+       if (!queue->usb_maxpacket)
+               queue->usb_maxpacket = 1;
+}
+
+static int rt2x00usb_find_endpoints(struct rt2x00_dev *rt2x00dev)
+{
+       struct usb_interface *intf = to_usb_interface(rt2x00dev->dev);
+       struct usb_host_interface *intf_desc = intf->cur_altsetting;
+       struct usb_endpoint_descriptor *ep_desc;
+       struct data_queue *queue = rt2x00dev->tx;
+       struct usb_endpoint_descriptor *tx_ep_desc = NULL;
+       unsigned int i;
+
+       /*
+        * Walk through all available endpoints to search for "bulk in"
+        * and "bulk out" endpoints. When we find such endpoints collect
+        * the information we need from the descriptor and assign it
+        * to the queue.
+        */
+       for (i = 0; i < intf_desc->desc.bNumEndpoints; i++) {
+               ep_desc = &intf_desc->endpoint[i].desc;
+
+               if (usb_endpoint_is_bulk_in(ep_desc)) {
+                       rt2x00usb_assign_endpoint(rt2x00dev->rx, ep_desc);
+               } else if (usb_endpoint_is_bulk_out(ep_desc) &&
+                          (queue != queue_end(rt2x00dev))) {
+                       rt2x00usb_assign_endpoint(queue, ep_desc);
+                       queue = queue_next(queue);
+
+                       tx_ep_desc = ep_desc;
+               }
+       }
+
+       /*
+        * At least 1 endpoint for RX and 1 endpoint for TX must be available.
+        */
+       if (!rt2x00dev->rx->usb_endpoint || !rt2x00dev->tx->usb_endpoint) {
+               ERROR(rt2x00dev, "Bulk-in/Bulk-out endpoints not found\n");
+               return -EPIPE;
+       }
+
+       /*
+        * It might be possible not all queues have a dedicated endpoint.
+        * Loop through all TX queues and copy the endpoint information
+        * which we have gathered from already assigned endpoints.
+        */
+       txall_queue_for_each(rt2x00dev, queue) {
+               if (!queue->usb_endpoint)
+                       rt2x00usb_assign_endpoint(queue, tx_ep_desc);
+       }
+
+       return 0;
+}
+
 static int rt2x00usb_alloc_urb(struct rt2x00_dev *rt2x00dev,
                               struct data_queue *queue)
 {
@@ -442,6 +556,13 @@ int rt2x00usb_initialize(struct rt2x00_dev *rt2x00dev)
        int status;
 
        /*
+        * Find endpoints for each queue
+        */
+       status = rt2x00usb_find_endpoints(rt2x00dev);
+       if (status)
+               goto exit;
+
+       /*
         * Allocate DMA
         */
        queue_for_each(rt2x00dev, queue) {
@@ -532,11 +653,6 @@ int rt2x00usb_probe(struct usb_interface *usb_intf,
        rt2x00dev->ops = ops;
        rt2x00dev->hw = hw;
 
-       rt2x00dev->usb_maxpacket =
-           usb_maxpacket(usb_dev, usb_sndbulkpipe(usb_dev, 1), 1);
-       if (!rt2x00dev->usb_maxpacket)
-               rt2x00dev->usb_maxpacket = 1;
-
        retval = rt2x00usb_alloc_reg(rt2x00dev);
        if (retval)
                goto exit_free_device;
@@ -593,8 +709,6 @@ int rt2x00usb_suspend(struct usb_interface *usb_intf, pm_message_t state)
        if (retval)
                return retval;
 
-       rt2x00usb_free_reg(rt2x00dev);
-
        /*
         * Decrease usbdev refcount.
         */
@@ -608,24 +722,10 @@ int rt2x00usb_resume(struct usb_interface *usb_intf)
 {
        struct ieee80211_hw *hw = usb_get_intfdata(usb_intf);
        struct rt2x00_dev *rt2x00dev = hw->priv;
-       int retval;
 
        usb_get_dev(interface_to_usbdev(usb_intf));
 
-       retval = rt2x00usb_alloc_reg(rt2x00dev);
-       if (retval)
-               return retval;
-
-       retval = rt2x00lib_resume(rt2x00dev);
-       if (retval)
-               goto exit_free_reg;
-
-       return 0;
-
-exit_free_reg:
-       rt2x00usb_free_reg(rt2x00dev);
-
-       return retval;
+       return rt2x00lib_resume(rt2x00dev);
 }
 EXPORT_SYMBOL_GPL(rt2x00usb_resume);
 #endif /* CONFIG_PM */