]>
Commit | Line | Data |
---|---|---|
92b96797 FB |
1 | /* |
2 | * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. | |
3 | * All rights reserved. | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation; either version 2 of the License, or | |
8 | * (at your option) any later version. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
92b96797 FB |
15 | * |
16 | * File: usbpipe.c | |
17 | * | |
18 | * Purpose: Handle USB control endpoint | |
19 | * | |
20 | * Author: Warren Hsu | |
21 | * | |
22 | * Date: Mar. 29, 2005 | |
23 | * | |
24 | * Functions: | |
1390b02a | 25 | * vnt_control_out - Write variable length bytes to MEM/BB/MAC/EEPROM |
441c21c3 | 26 | * vnt_control_in - Read variable length bytes from MEM/BB/MAC/EEPROM |
285d58c4 | 27 | * vnt_control_out_u8 - Write one byte to MEM/BB/MAC/EEPROM |
53742906 | 28 | * vnt_control_in_u8 - Read one byte from MEM/BB/MAC/EEPROM |
92b96797 FB |
29 | * |
30 | * Revision History: | |
3b1d7533 AJ |
31 | * 04-05-2004 Jerry Chen: Initial release |
32 | * 11-24-2004 Warren Hsu: Add ControlvWriteByte,ControlvReadByte, | |
33 | * ControlvMaskByte | |
92b96797 FB |
34 | * |
35 | */ | |
36 | ||
92b96797 | 37 | #include "int.h" |
92b96797 | 38 | #include "rxtx.h" |
92b96797 | 39 | #include "dpc.h" |
92b96797 | 40 | #include "desc.h" |
92b96797 | 41 | #include "device.h" |
62c8526d | 42 | #include "usbpipe.h" |
92b96797 | 43 | |
b2435179 | 44 | #define USB_CTL_WAIT 500 /* ms */ |
92b96797 | 45 | |
1390b02a | 46 | int vnt_control_out(struct vnt_private *priv, u8 request, u16 value, |
81293066 | 47 | u16 index, u16 length, u8 *buffer) |
92b96797 | 48 | { |
0f06a739 | 49 | int status = 0; |
12ecd24e | 50 | u8 *usb_buffer; |
92b96797 | 51 | |
cbcc9a36 | 52 | if (test_bit(DEVICE_FLAGS_DISCONNECTED, &priv->flags)) |
e1feda13 MP |
53 | return STATUS_FAILURE; |
54 | ||
0f06a739 | 55 | mutex_lock(&priv->usb_lock); |
c91b1869 | 56 | |
12ecd24e MP |
57 | usb_buffer = kmemdup(buffer, length, GFP_KERNEL); |
58 | if (!usb_buffer) { | |
59 | mutex_unlock(&priv->usb_lock); | |
60 | return -ENOMEM; | |
61 | } | |
62 | ||
0f06a739 | 63 | status = usb_control_msg(priv->usb, |
12ecd24e MP |
64 | usb_sndctrlpipe(priv->usb, 0), |
65 | request, 0x40, value, | |
66 | index, usb_buffer, length, USB_CTL_WAIT); | |
67 | ||
68 | kfree(usb_buffer); | |
ae5943de | 69 | |
0f06a739 | 70 | mutex_unlock(&priv->usb_lock); |
92b96797 | 71 | |
0f06a739 | 72 | if (status < (int)length) |
92b96797 | 73 | return STATUS_FAILURE; |
ae5943de | 74 | |
7021b684 | 75 | return STATUS_SUCCESS; |
92b96797 FB |
76 | } |
77 | ||
285d58c4 MP |
78 | void vnt_control_out_u8(struct vnt_private *priv, u8 reg, u8 reg_off, u8 data) |
79 | { | |
80 | vnt_control_out(priv, MESSAGE_TYPE_WRITE, | |
81293066 | 81 | reg_off, reg, sizeof(u8), &data); |
285d58c4 MP |
82 | } |
83 | ||
441c21c3 | 84 | int vnt_control_in(struct vnt_private *priv, u8 request, u16 value, |
81293066 | 85 | u16 index, u16 length, u8 *buffer) |
92b96797 | 86 | { |
9af49fdb | 87 | int status; |
05c0cf88 | 88 | u8 *usb_buffer; |
731047f9 | 89 | |
cbcc9a36 | 90 | if (test_bit(DEVICE_FLAGS_DISCONNECTED, &priv->flags)) |
e1feda13 | 91 | return STATUS_FAILURE; |
ae5943de | 92 | |
9af49fdb | 93 | mutex_lock(&priv->usb_lock); |
c91b1869 | 94 | |
05c0cf88 MP |
95 | usb_buffer = kmalloc(length, GFP_KERNEL); |
96 | if (!usb_buffer) { | |
97 | mutex_unlock(&priv->usb_lock); | |
98 | return -ENOMEM; | |
99 | } | |
100 | ||
9af49fdb | 101 | status = usb_control_msg(priv->usb, |
05c0cf88 MP |
102 | usb_rcvctrlpipe(priv->usb, 0), |
103 | request, 0xc0, value, | |
104 | index, usb_buffer, length, USB_CTL_WAIT); | |
105 | ||
106 | if (status == length) | |
107 | memcpy(buffer, usb_buffer, length); | |
108 | ||
109 | kfree(usb_buffer); | |
92b96797 | 110 | |
9af49fdb | 111 | mutex_unlock(&priv->usb_lock); |
c91b1869 | 112 | |
9af49fdb | 113 | if (status < (int)length) |
0fb2af35 | 114 | return STATUS_FAILURE; |
fe5d00eb | 115 | |
0fb2af35 | 116 | return STATUS_SUCCESS; |
92b96797 FB |
117 | } |
118 | ||
53742906 MP |
119 | void vnt_control_in_u8(struct vnt_private *priv, u8 reg, u8 reg_off, u8 *data) |
120 | { | |
121 | vnt_control_in(priv, MESSAGE_TYPE_READ, | |
81293066 | 122 | reg_off, reg, sizeof(u8), data); |
53742906 MP |
123 | } |
124 | ||
34f98e3f | 125 | static void vnt_start_interrupt_urb_complete(struct urb *urb) |
92b96797 | 126 | { |
599e4b5c | 127 | struct vnt_private *priv = urb->context; |
b9183fdd | 128 | int status = urb->status; |
92b96797 | 129 | |
b9183fdd | 130 | switch (status) { |
c98fbf90 MP |
131 | case 0: |
132 | case -ETIMEDOUT: | |
133 | break; | |
134 | case -ECONNRESET: | |
135 | case -ENOENT: | |
136 | case -ESHUTDOWN: | |
f764e00d | 137 | priv->int_buf.in_use = false; |
c98fbf90 MP |
138 | return; |
139 | default: | |
140 | break; | |
141 | } | |
142 | ||
7aa47db9 | 143 | if (status) { |
f764e00d | 144 | priv->int_buf.in_use = false; |
92b96797 | 145 | |
8a660261 | 146 | dev_dbg(&priv->usb->dev, "%s status = %d\n", __func__, status); |
d9ad7a98 | 147 | } else { |
e360d2b8 | 148 | vnt_int_process_data(priv); |
d9ad7a98 | 149 | } |
92b96797 | 150 | |
3d582487 | 151 | status = usb_submit_urb(priv->interrupt_urb, GFP_ATOMIC); |
86140346 | 152 | if (status) |
8a660261 | 153 | dev_dbg(&priv->usb->dev, "Submit int URB failed %d\n", status); |
86140346 | 154 | else |
f764e00d | 155 | priv->int_buf.in_use = true; |
92b96797 FB |
156 | } |
157 | ||
eaa56792 MP |
158 | int vnt_start_interrupt_urb(struct vnt_private *priv) |
159 | { | |
160 | int status = STATUS_FAILURE; | |
161 | ||
5699c0f4 | 162 | if (priv->int_buf.in_use) |
eaa56792 MP |
163 | return STATUS_FAILURE; |
164 | ||
165 | priv->int_buf.in_use = true; | |
166 | ||
167 | usb_fill_int_urb(priv->interrupt_urb, | |
168 | priv->usb, | |
169 | usb_rcvintpipe(priv->usb, 1), | |
170 | priv->int_buf.data_buf, | |
171 | MAX_INTERRUPT_SIZE, | |
172 | vnt_start_interrupt_urb_complete, | |
173 | priv, | |
174 | priv->int_interval); | |
175 | ||
176 | status = usb_submit_urb(priv->interrupt_urb, GFP_ATOMIC); | |
177 | if (status) { | |
178 | dev_dbg(&priv->usb->dev, "Submit int URB failed %d\n", status); | |
179 | priv->int_buf.in_use = false; | |
180 | } | |
181 | ||
182 | return status; | |
183 | } | |
184 | ||
08823af4 | 185 | static void vnt_submit_rx_urb_complete(struct urb *urb) |
92b96797 | 186 | { |
599e4b5c | 187 | struct vnt_rcb *rcb = urb->context; |
325de984 | 188 | struct vnt_private *priv = rcb->priv; |
92b96797 | 189 | |
67638980 MP |
190 | switch (urb->status) { |
191 | case 0: | |
67638980 MP |
192 | break; |
193 | case -ECONNRESET: | |
194 | case -ENOENT: | |
195 | case -ESHUTDOWN: | |
196 | return; | |
197 | case -ETIMEDOUT: | |
198 | default: | |
8a660261 | 199 | dev_dbg(&priv->usb->dev, "BULK In failed %d\n", urb->status); |
67638980 MP |
200 | break; |
201 | } | |
92b96797 | 202 | |
d4fa2ab0 | 203 | if (urb->actual_length) { |
f5283274 MP |
204 | if (vnt_rx_data(priv, rcb, urb->actual_length)) { |
205 | rcb->skb = dev_alloc_skb(priv->rx_buf_sz); | |
206 | if (!rcb->skb) { | |
207 | dev_dbg(&priv->usb->dev, | |
208 | "Failed to re-alloc rx skb\n"); | |
d4fa2ab0 | 209 | |
325de984 | 210 | rcb->in_use = false; |
f5283274 MP |
211 | return; |
212 | } | |
618ff9ce MP |
213 | } else { |
214 | skb_push(rcb->skb, skb_headroom(rcb->skb)); | |
215 | skb_trim(rcb->skb, 0); | |
8cffb3cf | 216 | } |
d4fa2ab0 | 217 | |
8cffb3cf MP |
218 | urb->transfer_buffer = skb_put(rcb->skb, |
219 | skb_tailroom(rcb->skb)); | |
220 | } | |
221 | ||
222 | if (usb_submit_urb(urb, GFP_ATOMIC)) { | |
223 | dev_dbg(&priv->usb->dev, "Failed to re submit rx skb\n"); | |
224 | ||
325de984 | 225 | rcb->in_use = false; |
d4fa2ab0 | 226 | } |
92b96797 FB |
227 | } |
228 | ||
93eac3b1 MP |
229 | int vnt_submit_rx_urb(struct vnt_private *priv, struct vnt_rcb *rcb) |
230 | { | |
231 | int status = 0; | |
b9183fdd | 232 | struct urb *urb = rcb->urb; |
93eac3b1 | 233 | |
27f31cf9 | 234 | if (!rcb->skb) { |
93eac3b1 MP |
235 | dev_dbg(&priv->usb->dev, "rcb->skb is null\n"); |
236 | return status; | |
237 | } | |
238 | ||
239 | usb_fill_bulk_urb(urb, | |
240 | priv->usb, | |
241 | usb_rcvbulkpipe(priv->usb, 2), | |
242 | skb_put(rcb->skb, skb_tailroom(rcb->skb)), | |
243 | MAX_TOTAL_SIZE_WITH_ALL_HEADERS, | |
244 | vnt_submit_rx_urb_complete, | |
245 | rcb); | |
246 | ||
247 | status = usb_submit_urb(urb, GFP_ATOMIC); | |
7aa47db9 | 248 | if (status) { |
93eac3b1 MP |
249 | dev_dbg(&priv->usb->dev, "Submit Rx URB failed %d\n", status); |
250 | return STATUS_FAILURE; | |
251 | } | |
252 | ||
253 | rcb->in_use = true; | |
254 | ||
255 | return status; | |
256 | } | |
257 | ||
ceebd903 | 258 | static void vnt_tx_context_complete(struct urb *urb) |
92b96797 | 259 | { |
599e4b5c | 260 | struct vnt_usb_send_context *context = urb->context; |
30a05b39 | 261 | struct vnt_private *priv = context->priv; |
92b96797 | 262 | |
e8152bfb MP |
263 | switch (urb->status) { |
264 | case 0: | |
8a660261 | 265 | dev_dbg(&priv->usb->dev, "Write %d bytes\n", context->buf_len); |
e8152bfb MP |
266 | break; |
267 | case -ECONNRESET: | |
268 | case -ENOENT: | |
269 | case -ESHUTDOWN: | |
30a05b39 | 270 | context->in_use = false; |
e8152bfb | 271 | return; |
d1b2a11d | 272 | case -ETIMEDOUT: |
e8152bfb | 273 | default: |
8a660261 | 274 | dev_dbg(&priv->usb->dev, "BULK Out failed %d\n", urb->status); |
e8152bfb MP |
275 | break; |
276 | } | |
277 | ||
d38b13aa MP |
278 | if (context->type == CONTEXT_DATA_PACKET) |
279 | ieee80211_wake_queues(priv->hw); | |
21aa212c | 280 | |
71d764ae MP |
281 | if (urb->status || context->type == CONTEXT_BEACON_PACKET) { |
282 | if (context->skb) | |
283 | ieee80211_free_txskb(priv->hw, context->skb); | |
284 | ||
285 | context->in_use = false; | |
286 | } | |
92b96797 | 287 | } |
664b044b MP |
288 | |
289 | int vnt_tx_context(struct vnt_private *priv, | |
290 | struct vnt_usb_send_context *context) | |
291 | { | |
292 | int status; | |
b9183fdd | 293 | struct urb *urb = context->urb; |
664b044b | 294 | |
cbcc9a36 | 295 | if (test_bit(DEVICE_FLAGS_DISCONNECTED, &priv->flags)) { |
664b044b MP |
296 | context->in_use = false; |
297 | return STATUS_RESOURCES; | |
298 | } | |
299 | ||
664b044b MP |
300 | usb_fill_bulk_urb(urb, |
301 | priv->usb, | |
302 | usb_sndbulkpipe(priv->usb, 3), | |
303 | context->data, | |
304 | context->buf_len, | |
305 | vnt_tx_context_complete, | |
306 | context); | |
307 | ||
308 | status = usb_submit_urb(urb, GFP_ATOMIC); | |
7aa47db9 | 309 | if (status) { |
664b044b MP |
310 | dev_dbg(&priv->usb->dev, "Submit Tx URB failed %d\n", status); |
311 | ||
312 | context->in_use = false; | |
313 | return STATUS_FAILURE; | |
314 | } | |
315 | ||
316 | return STATUS_PENDING; | |
317 | } |