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