| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 1 | /* | 
| Ivo van Doorn | 7e613e1 | 2010-08-06 20:45:38 +0200 | [diff] [blame] | 2 | Copyright (C) 2010 Willow Garage <http://www.willowgarage.com> | 
|  | 3 | Copyright (C) 2004 - 2010 Ivo van Doorn <IvDoorn@gmail.com> | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 4 | <http://rt2x00.serialmonkey.com> | 
|  | 5 |  | 
|  | 6 | This program is free software; you can redistribute it and/or modify | 
|  | 7 | it under the terms of the GNU General Public License as published by | 
|  | 8 | the Free Software Foundation; either version 2 of the License, or | 
|  | 9 | (at your option) any later version. | 
|  | 10 |  | 
|  | 11 | This program is distributed in the hope that it will be useful, | 
|  | 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 
|  | 14 | GNU General Public License for more details. | 
|  | 15 |  | 
|  | 16 | You should have received a copy of the GNU General Public License | 
|  | 17 | along with this program; if not, write to the | 
|  | 18 | Free Software Foundation, Inc., | 
|  | 19 | 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | 
|  | 20 | */ | 
|  | 21 |  | 
|  | 22 | /* | 
|  | 23 | Module: rt2x00usb | 
|  | 24 | Abstract: rt2x00 generic usb device routines. | 
|  | 25 | */ | 
|  | 26 |  | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 27 | #include <linux/kernel.h> | 
|  | 28 | #include <linux/module.h> | 
| Tejun Heo | 5a0e3ad | 2010-03-24 17:04:11 +0900 | [diff] [blame] | 29 | #include <linux/slab.h> | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 30 | #include <linux/usb.h> | 
| Adam Baker | 3d82346 | 2007-10-27 13:43:29 +0200 | [diff] [blame] | 31 | #include <linux/bug.h> | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 32 |  | 
|  | 33 | #include "rt2x00.h" | 
|  | 34 | #include "rt2x00usb.h" | 
|  | 35 |  | 
|  | 36 | /* | 
|  | 37 | * Interfacing with the HW. | 
|  | 38 | */ | 
| Adam Baker | 0e14f6d | 2007-10-27 13:41:25 +0200 | [diff] [blame] | 39 | int rt2x00usb_vendor_request(struct rt2x00_dev *rt2x00dev, | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 40 | const u8 request, const u8 requesttype, | 
|  | 41 | const u16 offset, const u16 value, | 
|  | 42 | void *buffer, const u16 buffer_length, | 
| Ivo van Doorn | e913655 | 2007-09-25 20:54:20 +0200 | [diff] [blame] | 43 | const int timeout) | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 44 | { | 
| Ivo van Doorn | c1d35df | 2008-06-16 19:57:11 +0200 | [diff] [blame] | 45 | struct usb_device *usb_dev = to_usb_device_intf(rt2x00dev->dev); | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 46 | int status; | 
|  | 47 | unsigned int i; | 
|  | 48 | unsigned int pipe = | 
|  | 49 | (requesttype == USB_VENDOR_REQUEST_IN) ? | 
|  | 50 | usb_rcvctrlpipe(usb_dev, 0) : usb_sndctrlpipe(usb_dev, 0); | 
|  | 51 |  | 
| Sean Cross | 66f84d6 | 2009-11-05 20:22:03 +0100 | [diff] [blame] | 52 | if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) | 
|  | 53 | return -ENODEV; | 
| Adam Baker | 3d82346 | 2007-10-27 13:43:29 +0200 | [diff] [blame] | 54 |  | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 55 | for (i = 0; i < REGISTER_BUSY_COUNT; i++) { | 
|  | 56 | status = usb_control_msg(usb_dev, pipe, request, requesttype, | 
|  | 57 | value, offset, buffer, buffer_length, | 
|  | 58 | timeout); | 
|  | 59 | if (status >= 0) | 
|  | 60 | return 0; | 
|  | 61 |  | 
|  | 62 | /* | 
| Ivo van Doorn | e913655 | 2007-09-25 20:54:20 +0200 | [diff] [blame] | 63 | * Check for errors | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 64 | * -ENODEV: Device has disappeared, no point continuing. | 
| Ivo van Doorn | e913655 | 2007-09-25 20:54:20 +0200 | [diff] [blame] | 65 | * All other errors: Try again. | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 66 | */ | 
| Sean Cross | 66f84d6 | 2009-11-05 20:22:03 +0100 | [diff] [blame] | 67 | else if (status == -ENODEV) { | 
|  | 68 | clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 69 | break; | 
| Sean Cross | 66f84d6 | 2009-11-05 20:22:03 +0100 | [diff] [blame] | 70 | } | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 71 | } | 
|  | 72 |  | 
|  | 73 | ERROR(rt2x00dev, | 
|  | 74 | "Vendor Request 0x%02x failed for offset 0x%04x with error %d.\n", | 
|  | 75 | request, offset, status); | 
|  | 76 |  | 
|  | 77 | return status; | 
|  | 78 | } | 
|  | 79 | EXPORT_SYMBOL_GPL(rt2x00usb_vendor_request); | 
|  | 80 |  | 
| Adam Baker | 3d82346 | 2007-10-27 13:43:29 +0200 | [diff] [blame] | 81 | int rt2x00usb_vendor_req_buff_lock(struct rt2x00_dev *rt2x00dev, | 
|  | 82 | const u8 request, const u8 requesttype, | 
|  | 83 | const u16 offset, void *buffer, | 
|  | 84 | const u16 buffer_length, const int timeout) | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 85 | { | 
|  | 86 | int status; | 
|  | 87 |  | 
| Ivo van Doorn | 8ff48a8 | 2008-11-09 23:40:46 +0100 | [diff] [blame] | 88 | BUG_ON(!mutex_is_locked(&rt2x00dev->csr_mutex)); | 
| Adam Baker | 3d82346 | 2007-10-27 13:43:29 +0200 | [diff] [blame] | 89 |  | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 90 | /* | 
|  | 91 | * Check for Cache availability. | 
|  | 92 | */ | 
| Ivo van Doorn | 2179509 | 2008-02-10 22:49:13 +0100 | [diff] [blame] | 93 | if (unlikely(!rt2x00dev->csr.cache || buffer_length > CSR_CACHE_SIZE)) { | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 94 | ERROR(rt2x00dev, "CSR cache not available.\n"); | 
|  | 95 | return -ENOMEM; | 
|  | 96 | } | 
|  | 97 |  | 
|  | 98 | if (requesttype == USB_VENDOR_REQUEST_OUT) | 
| Ivo van Doorn | 2179509 | 2008-02-10 22:49:13 +0100 | [diff] [blame] | 99 | memcpy(rt2x00dev->csr.cache, buffer, buffer_length); | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 100 |  | 
|  | 101 | status = rt2x00usb_vendor_request(rt2x00dev, request, requesttype, | 
| Ivo van Doorn | 2179509 | 2008-02-10 22:49:13 +0100 | [diff] [blame] | 102 | offset, 0, rt2x00dev->csr.cache, | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 103 | buffer_length, timeout); | 
|  | 104 |  | 
|  | 105 | if (!status && requesttype == USB_VENDOR_REQUEST_IN) | 
| Ivo van Doorn | 2179509 | 2008-02-10 22:49:13 +0100 | [diff] [blame] | 106 | memcpy(buffer, rt2x00dev->csr.cache, buffer_length); | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 107 |  | 
|  | 108 | return status; | 
|  | 109 | } | 
| Adam Baker | 3d82346 | 2007-10-27 13:43:29 +0200 | [diff] [blame] | 110 | EXPORT_SYMBOL_GPL(rt2x00usb_vendor_req_buff_lock); | 
|  | 111 |  | 
|  | 112 | int rt2x00usb_vendor_request_buff(struct rt2x00_dev *rt2x00dev, | 
|  | 113 | const u8 request, const u8 requesttype, | 
|  | 114 | const u16 offset, void *buffer, | 
|  | 115 | const u16 buffer_length, const int timeout) | 
|  | 116 | { | 
| Iwo Mergler | ed0dbee | 2008-07-19 16:16:54 +0200 | [diff] [blame] | 117 | int status = 0; | 
|  | 118 | unsigned char *tb; | 
|  | 119 | u16 off, len, bsize; | 
|  | 120 |  | 
| Ivo van Doorn | 8ff48a8 | 2008-11-09 23:40:46 +0100 | [diff] [blame] | 121 | mutex_lock(&rt2x00dev->csr_mutex); | 
| Iwo Mergler | ed0dbee | 2008-07-19 16:16:54 +0200 | [diff] [blame] | 122 |  | 
| Ivo van Doorn | 82f97b8 | 2008-08-02 01:31:09 -0700 | [diff] [blame] | 123 | tb  = (char *)buffer; | 
| Iwo Mergler | ed0dbee | 2008-07-19 16:16:54 +0200 | [diff] [blame] | 124 | off = offset; | 
|  | 125 | len = buffer_length; | 
|  | 126 | while (len && !status) { | 
|  | 127 | bsize = min_t(u16, CSR_CACHE_SIZE, len); | 
|  | 128 | status = rt2x00usb_vendor_req_buff_lock(rt2x00dev, request, | 
|  | 129 | requesttype, off, tb, | 
|  | 130 | bsize, timeout); | 
|  | 131 |  | 
|  | 132 | tb  += bsize; | 
|  | 133 | len -= bsize; | 
|  | 134 | off += bsize; | 
|  | 135 | } | 
|  | 136 |  | 
| Ivo van Doorn | 8ff48a8 | 2008-11-09 23:40:46 +0100 | [diff] [blame] | 137 | mutex_unlock(&rt2x00dev->csr_mutex); | 
| Iwo Mergler | ed0dbee | 2008-07-19 16:16:54 +0200 | [diff] [blame] | 138 |  | 
|  | 139 | return status; | 
|  | 140 | } | 
| Gertjan van Wingerde | 96b61ba | 2010-06-03 10:51:51 +0200 | [diff] [blame] | 141 | EXPORT_SYMBOL_GPL(rt2x00usb_vendor_request_buff); | 
| Iwo Mergler | ed0dbee | 2008-07-19 16:16:54 +0200 | [diff] [blame] | 142 |  | 
| Ivo van Doorn | 0f829b1 | 2008-11-10 19:42:18 +0100 | [diff] [blame] | 143 | int rt2x00usb_regbusy_read(struct rt2x00_dev *rt2x00dev, | 
|  | 144 | const unsigned int offset, | 
| Bartlomiej Zolnierkiewicz | f255b92 | 2009-11-04 18:35:18 +0100 | [diff] [blame] | 145 | const struct rt2x00_field32 field, | 
| Ivo van Doorn | 0f829b1 | 2008-11-10 19:42:18 +0100 | [diff] [blame] | 146 | u32 *reg) | 
|  | 147 | { | 
|  | 148 | unsigned int i; | 
|  | 149 |  | 
| Sean Cross | 66f84d6 | 2009-11-05 20:22:03 +0100 | [diff] [blame] | 150 | if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) | 
|  | 151 | return -ENODEV; | 
|  | 152 |  | 
| Ivo van Doorn | 0f829b1 | 2008-11-10 19:42:18 +0100 | [diff] [blame] | 153 | for (i = 0; i < REGISTER_BUSY_COUNT; i++) { | 
|  | 154 | rt2x00usb_register_read_lock(rt2x00dev, offset, reg); | 
|  | 155 | if (!rt2x00_get_field32(*reg, field)) | 
|  | 156 | return 1; | 
|  | 157 | udelay(REGISTER_BUSY_DELAY); | 
|  | 158 | } | 
|  | 159 |  | 
|  | 160 | ERROR(rt2x00dev, "Indirect register access failed: " | 
|  | 161 | "offset=0x%.08x, value=0x%.08x\n", offset, *reg); | 
|  | 162 | *reg = ~0; | 
|  | 163 |  | 
|  | 164 | return 0; | 
|  | 165 | } | 
|  | 166 | EXPORT_SYMBOL_GPL(rt2x00usb_regbusy_read); | 
|  | 167 |  | 
| Johannes Stezenbach | 0e0d39e | 2011-04-18 15:29:12 +0200 | [diff] [blame] | 168 |  | 
|  | 169 | struct rt2x00_async_read_data { | 
|  | 170 | __le32 reg; | 
|  | 171 | struct usb_ctrlrequest cr; | 
|  | 172 | struct rt2x00_dev *rt2x00dev; | 
| Ivo van Doorn | a073fde | 2011-04-30 17:14:23 +0200 | [diff] [blame] | 173 | bool (*callback)(struct rt2x00_dev *, int, u32); | 
| Johannes Stezenbach | 0e0d39e | 2011-04-18 15:29:12 +0200 | [diff] [blame] | 174 | }; | 
|  | 175 |  | 
|  | 176 | static void rt2x00usb_register_read_async_cb(struct urb *urb) | 
|  | 177 | { | 
|  | 178 | struct rt2x00_async_read_data *rd = urb->context; | 
| Ivo van Doorn | a073fde | 2011-04-30 17:14:23 +0200 | [diff] [blame] | 179 | if (rd->callback(rd->rt2x00dev, urb->status, le32_to_cpu(rd->reg))) { | 
|  | 180 | if (usb_submit_urb(urb, GFP_ATOMIC) < 0) | 
|  | 181 | kfree(rd); | 
|  | 182 | } else | 
|  | 183 | kfree(rd); | 
| Johannes Stezenbach | 0e0d39e | 2011-04-18 15:29:12 +0200 | [diff] [blame] | 184 | } | 
|  | 185 |  | 
|  | 186 | void rt2x00usb_register_read_async(struct rt2x00_dev *rt2x00dev, | 
|  | 187 | const unsigned int offset, | 
| Ivo van Doorn | a073fde | 2011-04-30 17:14:23 +0200 | [diff] [blame] | 188 | bool (*callback)(struct rt2x00_dev*, int, u32)) | 
| Johannes Stezenbach | 0e0d39e | 2011-04-18 15:29:12 +0200 | [diff] [blame] | 189 | { | 
|  | 190 | struct usb_device *usb_dev = to_usb_device_intf(rt2x00dev->dev); | 
|  | 191 | struct urb *urb; | 
|  | 192 | struct rt2x00_async_read_data *rd; | 
|  | 193 |  | 
|  | 194 | rd = kmalloc(sizeof(*rd), GFP_ATOMIC); | 
|  | 195 | if (!rd) | 
|  | 196 | return; | 
|  | 197 |  | 
|  | 198 | urb = usb_alloc_urb(0, GFP_ATOMIC); | 
|  | 199 | if (!urb) { | 
|  | 200 | kfree(rd); | 
|  | 201 | return; | 
|  | 202 | } | 
|  | 203 |  | 
|  | 204 | rd->rt2x00dev = rt2x00dev; | 
|  | 205 | rd->callback = callback; | 
|  | 206 | rd->cr.bRequestType = USB_VENDOR_REQUEST_IN; | 
|  | 207 | rd->cr.bRequest = USB_MULTI_READ; | 
|  | 208 | rd->cr.wValue = 0; | 
|  | 209 | rd->cr.wIndex = cpu_to_le16(offset); | 
|  | 210 | rd->cr.wLength = cpu_to_le16(sizeof(u32)); | 
|  | 211 |  | 
|  | 212 | usb_fill_control_urb(urb, usb_dev, usb_rcvctrlpipe(usb_dev, 0), | 
|  | 213 | (unsigned char *)(&rd->cr), &rd->reg, sizeof(rd->reg), | 
|  | 214 | rt2x00usb_register_read_async_cb, rd); | 
|  | 215 | if (usb_submit_urb(urb, GFP_ATOMIC) < 0) | 
|  | 216 | kfree(rd); | 
|  | 217 | usb_free_urb(urb); | 
|  | 218 | } | 
|  | 219 | EXPORT_SYMBOL_GPL(rt2x00usb_register_read_async); | 
|  | 220 |  | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 221 | /* | 
|  | 222 | * TX data handlers. | 
|  | 223 | */ | 
| Ivo van Doorn | 7e613e1 | 2010-08-06 20:45:38 +0200 | [diff] [blame] | 224 | static void rt2x00usb_work_txdone_entry(struct queue_entry *entry) | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 225 | { | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 226 | /* | 
| Ivo van Doorn | 7e613e1 | 2010-08-06 20:45:38 +0200 | [diff] [blame] | 227 | * If the transfer to hardware succeeded, it does not mean the | 
| Ivo van Doorn | fb55f4d | 2008-05-10 13:42:06 +0200 | [diff] [blame] | 228 | * frame was send out correctly. It only means the frame | 
| Lucas De Marchi | 25985ed | 2011-03-30 22:57:33 -0300 | [diff] [blame] | 229 | * was successfully pushed to the hardware, we have no | 
| Ivo van Doorn | fb55f4d | 2008-05-10 13:42:06 +0200 | [diff] [blame] | 230 | * way to determine the transmission status right now. | 
|  | 231 | * (Only indirectly by looking at the failed TX counters | 
|  | 232 | * in the register). | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 233 | */ | 
| Ivo van Doorn | 7e613e1 | 2010-08-06 20:45:38 +0200 | [diff] [blame] | 234 | if (test_bit(ENTRY_DATA_IO_FAILED, &entry->flags)) | 
| Ivo van Doorn | 3392bec | 2010-08-06 20:46:53 +0200 | [diff] [blame] | 235 | rt2x00lib_txdone_noinfo(entry, TXDONE_FAILURE); | 
| Ivo van Doorn | 7e613e1 | 2010-08-06 20:45:38 +0200 | [diff] [blame] | 236 | else | 
| Ivo van Doorn | 3392bec | 2010-08-06 20:46:53 +0200 | [diff] [blame] | 237 | rt2x00lib_txdone_noinfo(entry, TXDONE_UNKNOWN); | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 238 | } | 
|  | 239 |  | 
| Ivo van Doorn | 7e613e1 | 2010-08-06 20:45:38 +0200 | [diff] [blame] | 240 | static void rt2x00usb_work_txdone(struct work_struct *work) | 
|  | 241 | { | 
|  | 242 | struct rt2x00_dev *rt2x00dev = | 
|  | 243 | container_of(work, struct rt2x00_dev, txdone_work); | 
|  | 244 | struct data_queue *queue; | 
|  | 245 | struct queue_entry *entry; | 
|  | 246 |  | 
|  | 247 | tx_queue_for_each(rt2x00dev, queue) { | 
|  | 248 | while (!rt2x00queue_empty(queue)) { | 
|  | 249 | entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE); | 
|  | 250 |  | 
| Ivo van Doorn | dba5dc1 | 2010-12-13 12:36:18 +0100 | [diff] [blame] | 251 | if (test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags) || | 
|  | 252 | !test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags)) | 
| Ivo van Doorn | 7e613e1 | 2010-08-06 20:45:38 +0200 | [diff] [blame] | 253 | break; | 
|  | 254 |  | 
|  | 255 | rt2x00usb_work_txdone_entry(entry); | 
|  | 256 | } | 
|  | 257 | } | 
|  | 258 | } | 
|  | 259 |  | 
|  | 260 | static void rt2x00usb_interrupt_txdone(struct urb *urb) | 
|  | 261 | { | 
|  | 262 | struct queue_entry *entry = (struct queue_entry *)urb->context; | 
|  | 263 | struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; | 
|  | 264 |  | 
| Stanislaw Gruszka | df71c9c | 2011-08-10 15:32:23 +0200 | [diff] [blame] | 265 | if (!test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags)) | 
| Ivo van Doorn | 7e613e1 | 2010-08-06 20:45:38 +0200 | [diff] [blame] | 266 | return; | 
| Ivo van Doorn | 652a9dd | 2010-08-30 21:15:19 +0200 | [diff] [blame] | 267 | /* | 
| Ivo van Doorn | 7e613e1 | 2010-08-06 20:45:38 +0200 | [diff] [blame] | 268 | * Check if the frame was correctly uploaded | 
|  | 269 | */ | 
|  | 270 | if (urb->status) | 
| Ivo van Doorn | a1d1eab | 2010-10-11 15:38:45 +0200 | [diff] [blame] | 271 | set_bit(ENTRY_DATA_IO_FAILED, &entry->flags); | 
| Stanislaw Gruszka | df71c9c | 2011-08-10 15:32:23 +0200 | [diff] [blame] | 272 | /* | 
|  | 273 | * Report the frame as DMA done | 
|  | 274 | */ | 
|  | 275 | rt2x00lib_dmadone(entry); | 
| Ivo van Doorn | 7e613e1 | 2010-08-06 20:45:38 +0200 | [diff] [blame] | 276 |  | 
| Stanislaw Gruszka | df71c9c | 2011-08-10 15:32:23 +0200 | [diff] [blame] | 277 | if (rt2x00dev->ops->lib->tx_dma_done) | 
|  | 278 | rt2x00dev->ops->lib->tx_dma_done(entry); | 
| Ivo van Doorn | 7e613e1 | 2010-08-06 20:45:38 +0200 | [diff] [blame] | 279 | /* | 
|  | 280 | * Schedule the delayed work for reading the TX status | 
|  | 281 | * from the device. | 
|  | 282 | */ | 
| Johannes Stezenbach | f0187a1 | 2011-04-18 15:30:36 +0200 | [diff] [blame] | 283 | if (!test_bit(REQUIRE_TXSTATUS_FIFO, &rt2x00dev->cap_flags) || | 
|  | 284 | !kfifo_is_empty(&rt2x00dev->txstatus_fifo)) | 
|  | 285 | queue_work(rt2x00dev->workqueue, &rt2x00dev->txdone_work); | 
| Ivo van Doorn | 7e613e1 | 2010-08-06 20:45:38 +0200 | [diff] [blame] | 286 | } | 
|  | 287 |  | 
| Helmut Schaa | 10e1156 | 2011-04-18 15:27:43 +0200 | [diff] [blame] | 288 | static bool rt2x00usb_kick_tx_entry(struct queue_entry *entry, void* data) | 
| Ivo van Doorn | f019d51 | 2008-06-06 22:47:39 +0200 | [diff] [blame] | 289 | { | 
| Gertjan van Wingerde | fe72569 | 2010-06-29 21:40:34 +0200 | [diff] [blame] | 290 | struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; | 
|  | 291 | struct usb_device *usb_dev = to_usb_device_intf(rt2x00dev->dev); | 
| Ivo van Doorn | f019d51 | 2008-06-06 22:47:39 +0200 | [diff] [blame] | 292 | struct queue_entry_priv_usb *entry_priv = entry->priv_data; | 
| Gertjan van Wingerde | fe72569 | 2010-06-29 21:40:34 +0200 | [diff] [blame] | 293 | u32 length; | 
| Johannes Stezenbach | d7bb5f8 | 2010-12-13 12:32:49 +0100 | [diff] [blame] | 294 | int status; | 
| Ivo van Doorn | f019d51 | 2008-06-06 22:47:39 +0200 | [diff] [blame] | 295 |  | 
| Ivo van Doorn | dba5dc1 | 2010-12-13 12:36:18 +0100 | [diff] [blame] | 296 | if (!test_and_clear_bit(ENTRY_DATA_PENDING, &entry->flags) || | 
|  | 297 | test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags)) | 
| Ivo van Doorn | 4268d8e | 2011-05-04 21:42:05 +0200 | [diff] [blame] | 298 | return false; | 
| Gertjan van Wingerde | fe72569 | 2010-06-29 21:40:34 +0200 | [diff] [blame] | 299 |  | 
| Ivo van Doorn | ee1e755 | 2010-08-23 19:54:02 +0200 | [diff] [blame] | 300 | /* | 
| Jakub Kiciński | d823a50 | 2011-12-28 01:53:22 +0100 | [diff] [blame] | 301 | * USB devices require certain padding at the end of each frame | 
|  | 302 | * and urb. Those paddings are not included in skbs. Pass entry | 
|  | 303 | * to the driver to determine what the overall length should be. | 
| Ivo van Doorn | ee1e755 | 2010-08-23 19:54:02 +0200 | [diff] [blame] | 304 | */ | 
|  | 305 | length = rt2x00dev->ops->lib->get_tx_data_len(entry); | 
| Gertjan van Wingerde | fe72569 | 2010-06-29 21:40:34 +0200 | [diff] [blame] | 306 |  | 
| Jakub Kiciński | d823a50 | 2011-12-28 01:53:22 +0100 | [diff] [blame] | 307 | status = skb_padto(entry->skb, length); | 
|  | 308 | if (unlikely(status)) { | 
|  | 309 | /* TODO: report something more appropriate than IO_FAILED. */ | 
|  | 310 | WARNING(rt2x00dev, "TX SKB padding error, out of memory\n"); | 
|  | 311 | set_bit(ENTRY_DATA_IO_FAILED, &entry->flags); | 
|  | 312 | rt2x00lib_dmadone(entry); | 
|  | 313 |  | 
|  | 314 | return false; | 
|  | 315 | } | 
|  | 316 |  | 
| Ivo van Doorn | ee1e755 | 2010-08-23 19:54:02 +0200 | [diff] [blame] | 317 | usb_fill_bulk_urb(entry_priv->urb, usb_dev, | 
|  | 318 | usb_sndbulkpipe(usb_dev, entry->queue->usb_endpoint), | 
|  | 319 | entry->skb->data, length, | 
|  | 320 | rt2x00usb_interrupt_txdone, entry); | 
|  | 321 |  | 
| Johannes Stezenbach | d7bb5f8 | 2010-12-13 12:32:49 +0100 | [diff] [blame] | 322 | status = usb_submit_urb(entry_priv->urb, GFP_ATOMIC); | 
|  | 323 | if (status) { | 
|  | 324 | if (status == -ENODEV) | 
|  | 325 | clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); | 
| Ivo van Doorn | a13c8f3 | 2010-10-11 15:39:48 +0200 | [diff] [blame] | 326 | set_bit(ENTRY_DATA_IO_FAILED, &entry->flags); | 
|  | 327 | rt2x00lib_dmadone(entry); | 
|  | 328 | } | 
| Helmut Schaa | 10e1156 | 2011-04-18 15:27:43 +0200 | [diff] [blame] | 329 |  | 
|  | 330 | return false; | 
| Ivo van Doorn | f019d51 | 2008-06-06 22:47:39 +0200 | [diff] [blame] | 331 | } | 
|  | 332 |  | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 333 | /* | 
|  | 334 | * RX data handlers. | 
|  | 335 | */ | 
| Ivo van Doorn | 7e613e1 | 2010-08-06 20:45:38 +0200 | [diff] [blame] | 336 | static void rt2x00usb_work_rxdone(struct work_struct *work) | 
|  | 337 | { | 
|  | 338 | struct rt2x00_dev *rt2x00dev = | 
|  | 339 | container_of(work, struct rt2x00_dev, rxdone_work); | 
|  | 340 | struct queue_entry *entry; | 
|  | 341 | struct skb_frame_desc *skbdesc; | 
|  | 342 | u8 rxd[32]; | 
|  | 343 |  | 
|  | 344 | while (!rt2x00queue_empty(rt2x00dev->rx)) { | 
|  | 345 | entry = rt2x00queue_get_entry(rt2x00dev->rx, Q_INDEX_DONE); | 
|  | 346 |  | 
| Ivo van Doorn | dba5dc1 | 2010-12-13 12:36:18 +0100 | [diff] [blame] | 347 | if (test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags) || | 
|  | 348 | !test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags)) | 
| Ivo van Doorn | 7e613e1 | 2010-08-06 20:45:38 +0200 | [diff] [blame] | 349 | break; | 
|  | 350 |  | 
|  | 351 | /* | 
|  | 352 | * Fill in desc fields of the skb descriptor | 
|  | 353 | */ | 
|  | 354 | skbdesc = get_skb_frame_desc(entry->skb); | 
|  | 355 | skbdesc->desc = rxd; | 
|  | 356 | skbdesc->desc_len = entry->queue->desc_size; | 
|  | 357 |  | 
|  | 358 | /* | 
|  | 359 | * Send the frame to rt2x00lib for further processing. | 
|  | 360 | */ | 
| Ivo van Doorn | fa69560 | 2010-10-11 15:37:25 +0200 | [diff] [blame] | 361 | rt2x00lib_rxdone(entry); | 
| Ivo van Doorn | 7e613e1 | 2010-08-06 20:45:38 +0200 | [diff] [blame] | 362 | } | 
|  | 363 | } | 
|  | 364 |  | 
| Ivo van Doorn | 181d690 | 2008-02-05 16:42:23 -0500 | [diff] [blame] | 365 | static void rt2x00usb_interrupt_rxdone(struct urb *urb) | 
|  | 366 | { | 
|  | 367 | struct queue_entry *entry = (struct queue_entry *)urb->context; | 
|  | 368 | struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; | 
| Ivo van Doorn | 181d690 | 2008-02-05 16:42:23 -0500 | [diff] [blame] | 369 |  | 
| Ivo van Doorn | a1d1eab | 2010-10-11 15:38:45 +0200 | [diff] [blame] | 370 | if (!test_and_clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags)) | 
| Ivo van Doorn | 181d690 | 2008-02-05 16:42:23 -0500 | [diff] [blame] | 371 | return; | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 372 |  | 
|  | 373 | /* | 
| Ivo van Doorn | 652a9dd | 2010-08-30 21:15:19 +0200 | [diff] [blame] | 374 | * Report the frame as DMA done | 
|  | 375 | */ | 
|  | 376 | rt2x00lib_dmadone(entry); | 
|  | 377 |  | 
|  | 378 | /* | 
| Ivo van Doorn | 181d690 | 2008-02-05 16:42:23 -0500 | [diff] [blame] | 379 | * Check if the received data is simply too small | 
|  | 380 | * to be actually valid, or if the urb is signaling | 
|  | 381 | * a problem. | 
| Ivo van Doorn | 08992f7 | 2008-01-24 01:56:25 -0800 | [diff] [blame] | 382 | */ | 
| Ivo van Doorn | 7e613e1 | 2010-08-06 20:45:38 +0200 | [diff] [blame] | 383 | if (urb->actual_length < entry->queue->desc_size || urb->status) | 
| Ivo van Doorn | a1d1eab | 2010-10-11 15:38:45 +0200 | [diff] [blame] | 384 | set_bit(ENTRY_DATA_IO_FAILED, &entry->flags); | 
| Ivo van Doorn | 181d690 | 2008-02-05 16:42:23 -0500 | [diff] [blame] | 385 |  | 
|  | 386 | /* | 
| Ivo van Doorn | 7e613e1 | 2010-08-06 20:45:38 +0200 | [diff] [blame] | 387 | * Schedule the delayed work for reading the RX status | 
|  | 388 | * from the device. | 
| Ivo van Doorn | 181d690 | 2008-02-05 16:42:23 -0500 | [diff] [blame] | 389 | */ | 
| Ivo van Doorn | 0439f53 | 2011-01-30 13:24:05 +0100 | [diff] [blame] | 390 | queue_work(rt2x00dev->workqueue, &rt2x00dev->rxdone_work); | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 391 | } | 
|  | 392 |  | 
| Helmut Schaa | 10e1156 | 2011-04-18 15:27:43 +0200 | [diff] [blame] | 393 | static bool rt2x00usb_kick_rx_entry(struct queue_entry *entry, void* data) | 
| Ivo van Doorn | 0b7fde5 | 2010-12-13 12:35:17 +0100 | [diff] [blame] | 394 | { | 
|  | 395 | struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; | 
|  | 396 | struct usb_device *usb_dev = to_usb_device_intf(rt2x00dev->dev); | 
|  | 397 | struct queue_entry_priv_usb *entry_priv = entry->priv_data; | 
|  | 398 | int status; | 
|  | 399 |  | 
| Ivo van Doorn | dba5dc1 | 2010-12-13 12:36:18 +0100 | [diff] [blame] | 400 | if (test_and_set_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags) || | 
|  | 401 | test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags)) | 
| Ivo van Doorn | 4268d8e | 2011-05-04 21:42:05 +0200 | [diff] [blame] | 402 | return false; | 
| Ivo van Doorn | 0b7fde5 | 2010-12-13 12:35:17 +0100 | [diff] [blame] | 403 |  | 
| Ivo van Doorn | 64e7d72 | 2010-12-13 12:36:00 +0100 | [diff] [blame] | 404 | rt2x00lib_dmastart(entry); | 
|  | 405 |  | 
| Ivo van Doorn | 0b7fde5 | 2010-12-13 12:35:17 +0100 | [diff] [blame] | 406 | usb_fill_bulk_urb(entry_priv->urb, usb_dev, | 
|  | 407 | usb_rcvbulkpipe(usb_dev, entry->queue->usb_endpoint), | 
|  | 408 | entry->skb->data, entry->skb->len, | 
|  | 409 | rt2x00usb_interrupt_rxdone, entry); | 
|  | 410 |  | 
|  | 411 | status = usb_submit_urb(entry_priv->urb, GFP_ATOMIC); | 
|  | 412 | if (status) { | 
|  | 413 | if (status == -ENODEV) | 
|  | 414 | clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); | 
|  | 415 | set_bit(ENTRY_DATA_IO_FAILED, &entry->flags); | 
|  | 416 | rt2x00lib_dmadone(entry); | 
|  | 417 | } | 
| Helmut Schaa | 10e1156 | 2011-04-18 15:27:43 +0200 | [diff] [blame] | 418 |  | 
|  | 419 | return false; | 
| Ivo van Doorn | 0b7fde5 | 2010-12-13 12:35:17 +0100 | [diff] [blame] | 420 | } | 
|  | 421 |  | 
|  | 422 | void rt2x00usb_kick_queue(struct data_queue *queue) | 
|  | 423 | { | 
|  | 424 | switch (queue->qid) { | 
| Ivo van Doorn | f615e9a | 2010-12-13 12:36:38 +0100 | [diff] [blame] | 425 | case QID_AC_VO: | 
|  | 426 | case QID_AC_VI: | 
| Ivo van Doorn | 0b7fde5 | 2010-12-13 12:35:17 +0100 | [diff] [blame] | 427 | case QID_AC_BE: | 
|  | 428 | case QID_AC_BK: | 
| Ivo van Doorn | 0b7fde5 | 2010-12-13 12:35:17 +0100 | [diff] [blame] | 429 | if (!rt2x00queue_empty(queue)) | 
| Helmut Schaa | 10e1156 | 2011-04-18 15:27:43 +0200 | [diff] [blame] | 430 | rt2x00queue_for_each_entry(queue, | 
|  | 431 | Q_INDEX_DONE, | 
|  | 432 | Q_INDEX, | 
|  | 433 | NULL, | 
| Ivo van Doorn | 0b7fde5 | 2010-12-13 12:35:17 +0100 | [diff] [blame] | 434 | rt2x00usb_kick_tx_entry); | 
|  | 435 | break; | 
|  | 436 | case QID_RX: | 
|  | 437 | if (!rt2x00queue_full(queue)) | 
| Helmut Schaa | 10e1156 | 2011-04-18 15:27:43 +0200 | [diff] [blame] | 438 | rt2x00queue_for_each_entry(queue, | 
| Helmut Schaa | 10e1156 | 2011-04-18 15:27:43 +0200 | [diff] [blame] | 439 | Q_INDEX, | 
| Stanislaw Gruszka | a3c021d | 2012-07-04 13:10:02 +0200 | [diff] [blame] | 440 | Q_INDEX_DONE, | 
| Helmut Schaa | 10e1156 | 2011-04-18 15:27:43 +0200 | [diff] [blame] | 441 | NULL, | 
| Ivo van Doorn | 0b7fde5 | 2010-12-13 12:35:17 +0100 | [diff] [blame] | 442 | rt2x00usb_kick_rx_entry); | 
|  | 443 | break; | 
|  | 444 | default: | 
|  | 445 | break; | 
|  | 446 | } | 
|  | 447 | } | 
|  | 448 | EXPORT_SYMBOL_GPL(rt2x00usb_kick_queue); | 
|  | 449 |  | 
| Helmut Schaa | 10e1156 | 2011-04-18 15:27:43 +0200 | [diff] [blame] | 450 | static bool rt2x00usb_flush_entry(struct queue_entry *entry, void* data) | 
| Ivo van Doorn | 0b7fde5 | 2010-12-13 12:35:17 +0100 | [diff] [blame] | 451 | { | 
|  | 452 | struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; | 
|  | 453 | struct queue_entry_priv_usb *entry_priv = entry->priv_data; | 
|  | 454 | struct queue_entry_priv_usb_bcn *bcn_priv = entry->priv_data; | 
|  | 455 |  | 
|  | 456 | if (!test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags)) | 
| Ivo van Doorn | 4268d8e | 2011-05-04 21:42:05 +0200 | [diff] [blame] | 457 | return false; | 
| Ivo van Doorn | 0b7fde5 | 2010-12-13 12:35:17 +0100 | [diff] [blame] | 458 |  | 
|  | 459 | usb_kill_urb(entry_priv->urb); | 
|  | 460 |  | 
|  | 461 | /* | 
|  | 462 | * Kill guardian urb (if required by driver). | 
|  | 463 | */ | 
|  | 464 | if ((entry->queue->qid == QID_BEACON) && | 
| Ivo van Doorn | 7dab73b | 2011-04-18 15:27:06 +0200 | [diff] [blame] | 465 | (test_bit(REQUIRE_BEACON_GUARD, &rt2x00dev->cap_flags))) | 
| Ivo van Doorn | 0b7fde5 | 2010-12-13 12:35:17 +0100 | [diff] [blame] | 466 | usb_kill_urb(bcn_priv->guardian_urb); | 
| Helmut Schaa | 10e1156 | 2011-04-18 15:27:43 +0200 | [diff] [blame] | 467 |  | 
|  | 468 | return false; | 
| Ivo van Doorn | 0b7fde5 | 2010-12-13 12:35:17 +0100 | [diff] [blame] | 469 | } | 
|  | 470 |  | 
| Ivo van Doorn | 152a599 | 2011-04-18 15:31:02 +0200 | [diff] [blame] | 471 | void rt2x00usb_flush_queue(struct data_queue *queue, bool drop) | 
| Ivo van Doorn | 0b7fde5 | 2010-12-13 12:35:17 +0100 | [diff] [blame] | 472 | { | 
| Ivo van Doorn | 5be6560 | 2010-12-13 12:35:40 +0100 | [diff] [blame] | 473 | struct work_struct *completion; | 
|  | 474 | unsigned int i; | 
|  | 475 |  | 
| Ivo van Doorn | 152a599 | 2011-04-18 15:31:02 +0200 | [diff] [blame] | 476 | if (drop) | 
|  | 477 | rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, Q_INDEX, NULL, | 
|  | 478 | rt2x00usb_flush_entry); | 
| Ivo van Doorn | 5be6560 | 2010-12-13 12:35:40 +0100 | [diff] [blame] | 479 |  | 
|  | 480 | /* | 
|  | 481 | * Obtain the queue completion handler | 
|  | 482 | */ | 
|  | 483 | switch (queue->qid) { | 
| Ivo van Doorn | f615e9a | 2010-12-13 12:36:38 +0100 | [diff] [blame] | 484 | case QID_AC_VO: | 
|  | 485 | case QID_AC_VI: | 
| Ivo van Doorn | 5be6560 | 2010-12-13 12:35:40 +0100 | [diff] [blame] | 486 | case QID_AC_BE: | 
|  | 487 | case QID_AC_BK: | 
| Ivo van Doorn | 5be6560 | 2010-12-13 12:35:40 +0100 | [diff] [blame] | 488 | completion = &queue->rt2x00dev->txdone_work; | 
|  | 489 | break; | 
|  | 490 | case QID_RX: | 
|  | 491 | completion = &queue->rt2x00dev->rxdone_work; | 
|  | 492 | break; | 
|  | 493 | default: | 
|  | 494 | return; | 
|  | 495 | } | 
|  | 496 |  | 
| Ivo van Doorn | 152a599 | 2011-04-18 15:31:02 +0200 | [diff] [blame] | 497 | for (i = 0; i < 10; i++) { | 
| Ivo van Doorn | 5be6560 | 2010-12-13 12:35:40 +0100 | [diff] [blame] | 498 | /* | 
|  | 499 | * Check if the driver is already done, otherwise we | 
|  | 500 | * have to sleep a little while to give the driver/hw | 
|  | 501 | * the oppurtunity to complete interrupt process itself. | 
|  | 502 | */ | 
|  | 503 | if (rt2x00queue_empty(queue)) | 
|  | 504 | break; | 
|  | 505 |  | 
|  | 506 | /* | 
|  | 507 | * Schedule the completion handler manually, when this | 
|  | 508 | * worker function runs, it should cleanup the queue. | 
|  | 509 | */ | 
| Ivo van Doorn | 0439f53 | 2011-01-30 13:24:05 +0100 | [diff] [blame] | 510 | queue_work(queue->rt2x00dev->workqueue, completion); | 
| Ivo van Doorn | 5be6560 | 2010-12-13 12:35:40 +0100 | [diff] [blame] | 511 |  | 
|  | 512 | /* | 
|  | 513 | * Wait for a little while to give the driver | 
|  | 514 | * the oppurtunity to recover itself. | 
|  | 515 | */ | 
|  | 516 | msleep(10); | 
|  | 517 | } | 
| Ivo van Doorn | 0b7fde5 | 2010-12-13 12:35:17 +0100 | [diff] [blame] | 518 | } | 
| Ivo van Doorn | 5be6560 | 2010-12-13 12:35:40 +0100 | [diff] [blame] | 519 | EXPORT_SYMBOL_GPL(rt2x00usb_flush_queue); | 
| Ivo van Doorn | 0b7fde5 | 2010-12-13 12:35:17 +0100 | [diff] [blame] | 520 |  | 
|  | 521 | static void rt2x00usb_watchdog_tx_dma(struct data_queue *queue) | 
|  | 522 | { | 
| Ivo van Doorn | 0b7fde5 | 2010-12-13 12:35:17 +0100 | [diff] [blame] | 523 | WARNING(queue->rt2x00dev, "TX queue %d DMA timed out," | 
|  | 524 | " invoke forced forced reset\n", queue->qid); | 
|  | 525 |  | 
| Ivo van Doorn | 5be6560 | 2010-12-13 12:35:40 +0100 | [diff] [blame] | 526 | rt2x00queue_flush_queue(queue, true); | 
| Ivo van Doorn | 0b7fde5 | 2010-12-13 12:35:17 +0100 | [diff] [blame] | 527 | } | 
|  | 528 |  | 
| Johannes Stezenbach | 75256f0 | 2011-04-18 15:29:38 +0200 | [diff] [blame] | 529 | static int rt2x00usb_dma_timeout(struct data_queue *queue) | 
|  | 530 | { | 
|  | 531 | struct queue_entry *entry; | 
|  | 532 |  | 
|  | 533 | entry = rt2x00queue_get_entry(queue, Q_INDEX_DMA_DONE); | 
|  | 534 | return rt2x00queue_dma_timeout(entry); | 
|  | 535 | } | 
|  | 536 |  | 
| Ivo van Doorn | 0b7fde5 | 2010-12-13 12:35:17 +0100 | [diff] [blame] | 537 | void rt2x00usb_watchdog(struct rt2x00_dev *rt2x00dev) | 
|  | 538 | { | 
|  | 539 | struct data_queue *queue; | 
|  | 540 |  | 
|  | 541 | tx_queue_for_each(rt2x00dev, queue) { | 
|  | 542 | if (!rt2x00queue_empty(queue)) { | 
| Johannes Stezenbach | 75256f0 | 2011-04-18 15:29:38 +0200 | [diff] [blame] | 543 | if (rt2x00usb_dma_timeout(queue)) | 
| Ivo van Doorn | 0b7fde5 | 2010-12-13 12:35:17 +0100 | [diff] [blame] | 544 | rt2x00usb_watchdog_tx_dma(queue); | 
| Ivo van Doorn | 0b7fde5 | 2010-12-13 12:35:17 +0100 | [diff] [blame] | 545 | } | 
|  | 546 | } | 
|  | 547 | } | 
|  | 548 | EXPORT_SYMBOL_GPL(rt2x00usb_watchdog); | 
|  | 549 |  | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 550 | /* | 
|  | 551 | * Radio handlers | 
|  | 552 | */ | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 553 | void rt2x00usb_disable_radio(struct rt2x00_dev *rt2x00dev) | 
|  | 554 | { | 
| Ivo van Doorn | bd394a7 | 2008-04-21 19:01:58 +0200 | [diff] [blame] | 555 | rt2x00usb_vendor_request_sw(rt2x00dev, USB_RX_CONTROL, 0, 0, | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 556 | REGISTER_TIMEOUT); | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 557 | } | 
|  | 558 | EXPORT_SYMBOL_GPL(rt2x00usb_disable_radio); | 
|  | 559 |  | 
|  | 560 | /* | 
|  | 561 | * Device initialization handlers. | 
|  | 562 | */ | 
| Ivo van Doorn | 798b7ad | 2008-11-08 15:25:33 +0100 | [diff] [blame] | 563 | void rt2x00usb_clear_entry(struct queue_entry *entry) | 
| Ivo van Doorn | 837e7f2 | 2008-01-06 23:41:45 +0100 | [diff] [blame] | 564 | { | 
| Ivo van Doorn | 7e613e1 | 2010-08-06 20:45:38 +0200 | [diff] [blame] | 565 | entry->flags = 0; | 
|  | 566 |  | 
| Ivo van Doorn | 0b7fde5 | 2010-12-13 12:35:17 +0100 | [diff] [blame] | 567 | if (entry->queue->qid == QID_RX) | 
| Helmut Schaa | 10e1156 | 2011-04-18 15:27:43 +0200 | [diff] [blame] | 568 | rt2x00usb_kick_rx_entry(entry, NULL); | 
| Ivo van Doorn | 837e7f2 | 2008-01-06 23:41:45 +0100 | [diff] [blame] | 569 | } | 
| Ivo van Doorn | 798b7ad | 2008-11-08 15:25:33 +0100 | [diff] [blame] | 570 | EXPORT_SYMBOL_GPL(rt2x00usb_clear_entry); | 
| Ivo van Doorn | 837e7f2 | 2008-01-06 23:41:45 +0100 | [diff] [blame] | 571 |  | 
| Ivo van Doorn | f1ca216 | 2008-11-13 23:07:33 +0100 | [diff] [blame] | 572 | static void rt2x00usb_assign_endpoint(struct data_queue *queue, | 
|  | 573 | struct usb_endpoint_descriptor *ep_desc) | 
|  | 574 | { | 
|  | 575 | struct usb_device *usb_dev = to_usb_device_intf(queue->rt2x00dev->dev); | 
|  | 576 | int pipe; | 
|  | 577 |  | 
|  | 578 | queue->usb_endpoint = usb_endpoint_num(ep_desc); | 
|  | 579 |  | 
|  | 580 | if (queue->qid == QID_RX) { | 
|  | 581 | pipe = usb_rcvbulkpipe(usb_dev, queue->usb_endpoint); | 
|  | 582 | queue->usb_maxpacket = usb_maxpacket(usb_dev, pipe, 0); | 
|  | 583 | } else { | 
|  | 584 | pipe = usb_sndbulkpipe(usb_dev, queue->usb_endpoint); | 
|  | 585 | queue->usb_maxpacket = usb_maxpacket(usb_dev, pipe, 1); | 
|  | 586 | } | 
|  | 587 |  | 
|  | 588 | if (!queue->usb_maxpacket) | 
|  | 589 | queue->usb_maxpacket = 1; | 
|  | 590 | } | 
|  | 591 |  | 
|  | 592 | static int rt2x00usb_find_endpoints(struct rt2x00_dev *rt2x00dev) | 
|  | 593 | { | 
|  | 594 | struct usb_interface *intf = to_usb_interface(rt2x00dev->dev); | 
|  | 595 | struct usb_host_interface *intf_desc = intf->cur_altsetting; | 
|  | 596 | struct usb_endpoint_descriptor *ep_desc; | 
|  | 597 | struct data_queue *queue = rt2x00dev->tx; | 
|  | 598 | struct usb_endpoint_descriptor *tx_ep_desc = NULL; | 
|  | 599 | unsigned int i; | 
|  | 600 |  | 
|  | 601 | /* | 
|  | 602 | * Walk through all available endpoints to search for "bulk in" | 
|  | 603 | * and "bulk out" endpoints. When we find such endpoints collect | 
|  | 604 | * the information we need from the descriptor and assign it | 
|  | 605 | * to the queue. | 
|  | 606 | */ | 
|  | 607 | for (i = 0; i < intf_desc->desc.bNumEndpoints; i++) { | 
|  | 608 | ep_desc = &intf_desc->endpoint[i].desc; | 
|  | 609 |  | 
|  | 610 | if (usb_endpoint_is_bulk_in(ep_desc)) { | 
|  | 611 | rt2x00usb_assign_endpoint(rt2x00dev->rx, ep_desc); | 
| Ivo van Doorn | d15cfc3 | 2008-12-20 11:00:23 +0100 | [diff] [blame] | 612 | } else if (usb_endpoint_is_bulk_out(ep_desc) && | 
|  | 613 | (queue != queue_end(rt2x00dev))) { | 
| Ivo van Doorn | f1ca216 | 2008-11-13 23:07:33 +0100 | [diff] [blame] | 614 | rt2x00usb_assign_endpoint(queue, ep_desc); | 
| Ivo van Doorn | d15cfc3 | 2008-12-20 11:00:23 +0100 | [diff] [blame] | 615 | queue = queue_next(queue); | 
| Ivo van Doorn | f1ca216 | 2008-11-13 23:07:33 +0100 | [diff] [blame] | 616 |  | 
| Ivo van Doorn | f1ca216 | 2008-11-13 23:07:33 +0100 | [diff] [blame] | 617 | tx_ep_desc = ep_desc; | 
|  | 618 | } | 
|  | 619 | } | 
|  | 620 |  | 
|  | 621 | /* | 
|  | 622 | * At least 1 endpoint for RX and 1 endpoint for TX must be available. | 
|  | 623 | */ | 
|  | 624 | if (!rt2x00dev->rx->usb_endpoint || !rt2x00dev->tx->usb_endpoint) { | 
|  | 625 | ERROR(rt2x00dev, "Bulk-in/Bulk-out endpoints not found\n"); | 
|  | 626 | return -EPIPE; | 
|  | 627 | } | 
|  | 628 |  | 
|  | 629 | /* | 
|  | 630 | * It might be possible not all queues have a dedicated endpoint. | 
|  | 631 | * Loop through all TX queues and copy the endpoint information | 
|  | 632 | * which we have gathered from already assigned endpoints. | 
|  | 633 | */ | 
|  | 634 | txall_queue_for_each(rt2x00dev, queue) { | 
|  | 635 | if (!queue->usb_endpoint) | 
|  | 636 | rt2x00usb_assign_endpoint(queue, tx_ep_desc); | 
|  | 637 | } | 
|  | 638 |  | 
|  | 639 | return 0; | 
|  | 640 | } | 
|  | 641 |  | 
| Ivo van Doorn | fa69560 | 2010-10-11 15:37:25 +0200 | [diff] [blame] | 642 | static int rt2x00usb_alloc_entries(struct data_queue *queue) | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 643 | { | 
| Ivo van Doorn | fa69560 | 2010-10-11 15:37:25 +0200 | [diff] [blame] | 644 | struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; | 
| Ivo van Doorn | b8be63f | 2008-05-10 13:46:03 +0200 | [diff] [blame] | 645 | struct queue_entry_priv_usb *entry_priv; | 
|  | 646 | struct queue_entry_priv_usb_bcn *bcn_priv; | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 647 | unsigned int i; | 
|  | 648 |  | 
| Ivo van Doorn | 181d690 | 2008-02-05 16:42:23 -0500 | [diff] [blame] | 649 | for (i = 0; i < queue->limit; i++) { | 
| Ivo van Doorn | b8be63f | 2008-05-10 13:46:03 +0200 | [diff] [blame] | 650 | entry_priv = queue->entries[i].priv_data; | 
|  | 651 | entry_priv->urb = usb_alloc_urb(0, GFP_KERNEL); | 
|  | 652 | if (!entry_priv->urb) | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 653 | return -ENOMEM; | 
| Ivo van Doorn | b8be63f | 2008-05-10 13:46:03 +0200 | [diff] [blame] | 654 | } | 
| Ivo van Doorn | 181d690 | 2008-02-05 16:42:23 -0500 | [diff] [blame] | 655 |  | 
| Ivo van Doorn | b8be63f | 2008-05-10 13:46:03 +0200 | [diff] [blame] | 656 | /* | 
|  | 657 | * If this is not the beacon queue or | 
|  | 658 | * no guardian byte was required for the beacon, | 
|  | 659 | * then we are done. | 
|  | 660 | */ | 
| Ivo van Doorn | fa69560 | 2010-10-11 15:37:25 +0200 | [diff] [blame] | 661 | if (queue->qid != QID_BEACON || | 
| Ivo van Doorn | 7dab73b | 2011-04-18 15:27:06 +0200 | [diff] [blame] | 662 | !test_bit(REQUIRE_BEACON_GUARD, &rt2x00dev->cap_flags)) | 
| Ivo van Doorn | b8be63f | 2008-05-10 13:46:03 +0200 | [diff] [blame] | 663 | return 0; | 
| Ivo van Doorn | 181d690 | 2008-02-05 16:42:23 -0500 | [diff] [blame] | 664 |  | 
| Ivo van Doorn | b8be63f | 2008-05-10 13:46:03 +0200 | [diff] [blame] | 665 | for (i = 0; i < queue->limit; i++) { | 
|  | 666 | bcn_priv = queue->entries[i].priv_data; | 
|  | 667 | bcn_priv->guardian_urb = usb_alloc_urb(0, GFP_KERNEL); | 
|  | 668 | if (!bcn_priv->guardian_urb) | 
|  | 669 | return -ENOMEM; | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 670 | } | 
|  | 671 |  | 
|  | 672 | return 0; | 
|  | 673 | } | 
|  | 674 |  | 
| Ivo van Doorn | fa69560 | 2010-10-11 15:37:25 +0200 | [diff] [blame] | 675 | static void rt2x00usb_free_entries(struct data_queue *queue) | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 676 | { | 
| Ivo van Doorn | fa69560 | 2010-10-11 15:37:25 +0200 | [diff] [blame] | 677 | struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; | 
| Ivo van Doorn | b8be63f | 2008-05-10 13:46:03 +0200 | [diff] [blame] | 678 | struct queue_entry_priv_usb *entry_priv; | 
|  | 679 | struct queue_entry_priv_usb_bcn *bcn_priv; | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 680 | unsigned int i; | 
|  | 681 |  | 
| Ivo van Doorn | 181d690 | 2008-02-05 16:42:23 -0500 | [diff] [blame] | 682 | if (!queue->entries) | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 683 | return; | 
|  | 684 |  | 
| Ivo van Doorn | 181d690 | 2008-02-05 16:42:23 -0500 | [diff] [blame] | 685 | for (i = 0; i < queue->limit; i++) { | 
| Ivo van Doorn | b8be63f | 2008-05-10 13:46:03 +0200 | [diff] [blame] | 686 | entry_priv = queue->entries[i].priv_data; | 
|  | 687 | usb_kill_urb(entry_priv->urb); | 
|  | 688 | usb_free_urb(entry_priv->urb); | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 689 | } | 
| Ivo van Doorn | b8be63f | 2008-05-10 13:46:03 +0200 | [diff] [blame] | 690 |  | 
|  | 691 | /* | 
|  | 692 | * If this is not the beacon queue or | 
|  | 693 | * no guardian byte was required for the beacon, | 
|  | 694 | * then we are done. | 
|  | 695 | */ | 
| Ivo van Doorn | fa69560 | 2010-10-11 15:37:25 +0200 | [diff] [blame] | 696 | if (queue->qid != QID_BEACON || | 
| Ivo van Doorn | 7dab73b | 2011-04-18 15:27:06 +0200 | [diff] [blame] | 697 | !test_bit(REQUIRE_BEACON_GUARD, &rt2x00dev->cap_flags)) | 
| Ivo van Doorn | b8be63f | 2008-05-10 13:46:03 +0200 | [diff] [blame] | 698 | return; | 
|  | 699 |  | 
|  | 700 | for (i = 0; i < queue->limit; i++) { | 
|  | 701 | bcn_priv = queue->entries[i].priv_data; | 
|  | 702 | usb_kill_urb(bcn_priv->guardian_urb); | 
|  | 703 | usb_free_urb(bcn_priv->guardian_urb); | 
|  | 704 | } | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 705 | } | 
|  | 706 |  | 
|  | 707 | int rt2x00usb_initialize(struct rt2x00_dev *rt2x00dev) | 
|  | 708 | { | 
| Ivo van Doorn | 181d690 | 2008-02-05 16:42:23 -0500 | [diff] [blame] | 709 | struct data_queue *queue; | 
| Gertjan van Wingerde | 30caa6e | 2008-06-16 19:56:08 +0200 | [diff] [blame] | 710 | int status; | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 711 |  | 
|  | 712 | /* | 
| Ivo van Doorn | f1ca216 | 2008-11-13 23:07:33 +0100 | [diff] [blame] | 713 | * Find endpoints for each queue | 
|  | 714 | */ | 
|  | 715 | status = rt2x00usb_find_endpoints(rt2x00dev); | 
|  | 716 | if (status) | 
|  | 717 | goto exit; | 
|  | 718 |  | 
|  | 719 | /* | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 720 | * Allocate DMA | 
|  | 721 | */ | 
| Ivo van Doorn | 181d690 | 2008-02-05 16:42:23 -0500 | [diff] [blame] | 722 | queue_for_each(rt2x00dev, queue) { | 
| Ivo van Doorn | fa69560 | 2010-10-11 15:37:25 +0200 | [diff] [blame] | 723 | status = rt2x00usb_alloc_entries(queue); | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 724 | if (status) | 
|  | 725 | goto exit; | 
|  | 726 | } | 
|  | 727 |  | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 728 | return 0; | 
|  | 729 |  | 
|  | 730 | exit: | 
|  | 731 | rt2x00usb_uninitialize(rt2x00dev); | 
|  | 732 |  | 
|  | 733 | return status; | 
|  | 734 | } | 
|  | 735 | EXPORT_SYMBOL_GPL(rt2x00usb_initialize); | 
|  | 736 |  | 
|  | 737 | void rt2x00usb_uninitialize(struct rt2x00_dev *rt2x00dev) | 
|  | 738 | { | 
| Ivo van Doorn | 181d690 | 2008-02-05 16:42:23 -0500 | [diff] [blame] | 739 | struct data_queue *queue; | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 740 |  | 
| Ivo van Doorn | 181d690 | 2008-02-05 16:42:23 -0500 | [diff] [blame] | 741 | queue_for_each(rt2x00dev, queue) | 
| Ivo van Doorn | fa69560 | 2010-10-11 15:37:25 +0200 | [diff] [blame] | 742 | rt2x00usb_free_entries(queue); | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 743 | } | 
|  | 744 | EXPORT_SYMBOL_GPL(rt2x00usb_uninitialize); | 
|  | 745 |  | 
|  | 746 | /* | 
|  | 747 | * USB driver handlers. | 
|  | 748 | */ | 
|  | 749 | static void rt2x00usb_free_reg(struct rt2x00_dev *rt2x00dev) | 
|  | 750 | { | 
|  | 751 | kfree(rt2x00dev->rf); | 
|  | 752 | rt2x00dev->rf = NULL; | 
|  | 753 |  | 
|  | 754 | kfree(rt2x00dev->eeprom); | 
|  | 755 | rt2x00dev->eeprom = NULL; | 
|  | 756 |  | 
| Ivo van Doorn | 2179509 | 2008-02-10 22:49:13 +0100 | [diff] [blame] | 757 | kfree(rt2x00dev->csr.cache); | 
|  | 758 | rt2x00dev->csr.cache = NULL; | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 759 | } | 
|  | 760 |  | 
|  | 761 | static int rt2x00usb_alloc_reg(struct rt2x00_dev *rt2x00dev) | 
|  | 762 | { | 
| Ivo van Doorn | 2179509 | 2008-02-10 22:49:13 +0100 | [diff] [blame] | 763 | rt2x00dev->csr.cache = kzalloc(CSR_CACHE_SIZE, GFP_KERNEL); | 
|  | 764 | if (!rt2x00dev->csr.cache) | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 765 | goto exit; | 
|  | 766 |  | 
|  | 767 | rt2x00dev->eeprom = kzalloc(rt2x00dev->ops->eeprom_size, GFP_KERNEL); | 
|  | 768 | if (!rt2x00dev->eeprom) | 
|  | 769 | goto exit; | 
|  | 770 |  | 
|  | 771 | rt2x00dev->rf = kzalloc(rt2x00dev->ops->rf_size, GFP_KERNEL); | 
|  | 772 | if (!rt2x00dev->rf) | 
|  | 773 | goto exit; | 
|  | 774 |  | 
|  | 775 | return 0; | 
|  | 776 |  | 
|  | 777 | exit: | 
|  | 778 | ERROR_PROBE("Failed to allocate registers.\n"); | 
|  | 779 |  | 
|  | 780 | rt2x00usb_free_reg(rt2x00dev); | 
|  | 781 |  | 
|  | 782 | return -ENOMEM; | 
|  | 783 | } | 
|  | 784 |  | 
|  | 785 | int rt2x00usb_probe(struct usb_interface *usb_intf, | 
| Gertjan van Wingerde | e01ae27 | 2011-04-18 15:32:13 +0200 | [diff] [blame] | 786 | const struct rt2x00_ops *ops) | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 787 | { | 
|  | 788 | struct usb_device *usb_dev = interface_to_usbdev(usb_intf); | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 789 | struct ieee80211_hw *hw; | 
|  | 790 | struct rt2x00_dev *rt2x00dev; | 
|  | 791 | int retval; | 
|  | 792 |  | 
|  | 793 | usb_dev = usb_get_dev(usb_dev); | 
| Stanislaw Gruszka | bf4c02d | 2011-06-19 19:47:39 +0200 | [diff] [blame] | 794 | usb_reset_device(usb_dev); | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 795 |  | 
|  | 796 | hw = ieee80211_alloc_hw(sizeof(struct rt2x00_dev), ops->hw); | 
|  | 797 | if (!hw) { | 
|  | 798 | ERROR_PROBE("Failed to allocate hardware.\n"); | 
|  | 799 | retval = -ENOMEM; | 
|  | 800 | goto exit_put_device; | 
|  | 801 | } | 
|  | 802 |  | 
|  | 803 | usb_set_intfdata(usb_intf, hw); | 
|  | 804 |  | 
|  | 805 | rt2x00dev = hw->priv; | 
| Gertjan van Wingerde | 14a3bf8 | 2008-06-16 19:55:43 +0200 | [diff] [blame] | 806 | rt2x00dev->dev = &usb_intf->dev; | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 807 | rt2x00dev->ops = ops; | 
|  | 808 | rt2x00dev->hw = hw; | 
|  | 809 |  | 
| Gertjan van Wingerde | 2015d19 | 2009-11-08 12:30:14 +0100 | [diff] [blame] | 810 | rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_USB); | 
|  | 811 |  | 
| Ivo van Doorn | 7e613e1 | 2010-08-06 20:45:38 +0200 | [diff] [blame] | 812 | INIT_WORK(&rt2x00dev->rxdone_work, rt2x00usb_work_rxdone); | 
|  | 813 | INIT_WORK(&rt2x00dev->txdone_work, rt2x00usb_work_txdone); | 
| Stanislaw Gruszka | f421111 | 2012-03-14 11:16:19 +0100 | [diff] [blame] | 814 | hrtimer_init(&rt2x00dev->txstatus_timer, CLOCK_MONOTONIC, | 
|  | 815 | HRTIMER_MODE_REL); | 
| Ivo van Doorn | 7e613e1 | 2010-08-06 20:45:38 +0200 | [diff] [blame] | 816 |  | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 817 | retval = rt2x00usb_alloc_reg(rt2x00dev); | 
|  | 818 | if (retval) | 
|  | 819 | goto exit_free_device; | 
|  | 820 |  | 
|  | 821 | retval = rt2x00lib_probe_dev(rt2x00dev); | 
|  | 822 | if (retval) | 
|  | 823 | goto exit_free_reg; | 
|  | 824 |  | 
|  | 825 | return 0; | 
|  | 826 |  | 
|  | 827 | exit_free_reg: | 
|  | 828 | rt2x00usb_free_reg(rt2x00dev); | 
|  | 829 |  | 
|  | 830 | exit_free_device: | 
|  | 831 | ieee80211_free_hw(hw); | 
|  | 832 |  | 
|  | 833 | exit_put_device: | 
|  | 834 | usb_put_dev(usb_dev); | 
|  | 835 |  | 
|  | 836 | usb_set_intfdata(usb_intf, NULL); | 
|  | 837 |  | 
|  | 838 | return retval; | 
|  | 839 | } | 
|  | 840 | EXPORT_SYMBOL_GPL(rt2x00usb_probe); | 
|  | 841 |  | 
|  | 842 | void rt2x00usb_disconnect(struct usb_interface *usb_intf) | 
|  | 843 | { | 
|  | 844 | struct ieee80211_hw *hw = usb_get_intfdata(usb_intf); | 
|  | 845 | struct rt2x00_dev *rt2x00dev = hw->priv; | 
|  | 846 |  | 
|  | 847 | /* | 
|  | 848 | * Free all allocated data. | 
|  | 849 | */ | 
|  | 850 | rt2x00lib_remove_dev(rt2x00dev); | 
|  | 851 | rt2x00usb_free_reg(rt2x00dev); | 
|  | 852 | ieee80211_free_hw(hw); | 
|  | 853 |  | 
|  | 854 | /* | 
|  | 855 | * Free the USB device data. | 
|  | 856 | */ | 
|  | 857 | usb_set_intfdata(usb_intf, NULL); | 
|  | 858 | usb_put_dev(interface_to_usbdev(usb_intf)); | 
|  | 859 | } | 
|  | 860 | EXPORT_SYMBOL_GPL(rt2x00usb_disconnect); | 
|  | 861 |  | 
|  | 862 | #ifdef CONFIG_PM | 
|  | 863 | int rt2x00usb_suspend(struct usb_interface *usb_intf, pm_message_t state) | 
|  | 864 | { | 
|  | 865 | struct ieee80211_hw *hw = usb_get_intfdata(usb_intf); | 
|  | 866 | struct rt2x00_dev *rt2x00dev = hw->priv; | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 867 |  | 
| Stanislaw Gruszka | 543cc38 | 2011-08-12 14:02:04 +0200 | [diff] [blame] | 868 | return rt2x00lib_suspend(rt2x00dev, state); | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 869 | } | 
|  | 870 | EXPORT_SYMBOL_GPL(rt2x00usb_suspend); | 
|  | 871 |  | 
|  | 872 | int rt2x00usb_resume(struct usb_interface *usb_intf) | 
|  | 873 | { | 
|  | 874 | struct ieee80211_hw *hw = usb_get_intfdata(usb_intf); | 
|  | 875 | struct rt2x00_dev *rt2x00dev = hw->priv; | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 876 |  | 
| Ivo van Doorn | 499a214 | 2009-03-28 20:51:58 +0100 | [diff] [blame] | 877 | return rt2x00lib_resume(rt2x00dev); | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 878 | } | 
|  | 879 | EXPORT_SYMBOL_GPL(rt2x00usb_resume); | 
|  | 880 | #endif /* CONFIG_PM */ | 
|  | 881 |  | 
|  | 882 | /* | 
| Ivo van Doorn | 181d690 | 2008-02-05 16:42:23 -0500 | [diff] [blame] | 883 | * rt2x00usb module information. | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 884 | */ | 
|  | 885 | MODULE_AUTHOR(DRV_PROJECT); | 
|  | 886 | MODULE_VERSION(DRV_VERSION); | 
| Ivo van Doorn | 181d690 | 2008-02-05 16:42:23 -0500 | [diff] [blame] | 887 | MODULE_DESCRIPTION("rt2x00 usb library"); | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 888 | MODULE_LICENSE("GPL"); |