blob: 88bf518f23eb6c5360ffb07ab8359d08b4fcf33d [file] [log] [blame]
Forest Bond92b96792009-06-13 07:38:31 -04001/*
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:
Malcolm Priestley1390b022014-05-26 13:59:01 +010029 * vnt_control_out - Write variable length bytes to MEM/BB/MAC/EEPROM
Malcolm Priestley441c21c2014-05-26 13:59:02 +010030 * vnt_control_in - Read variable length bytes from MEM/BB/MAC/EEPROM
Malcolm Priestley285d58c2014-05-26 13:59:03 +010031 * vnt_control_out_u8 - Write one byte to MEM/BB/MAC/EEPROM
Malcolm Priestley53742902014-05-26 13:59:06 +010032 * vnt_control_in_u8 - Read one byte from MEM/BB/MAC/EEPROM
Forest Bond92b96792009-06-13 07:38:31 -040033 *
34 * Revision History:
35 * 04-05-2004 Jerry Chen: Initial release
36 * 11-24-2004 Warren Hsu: Add ControlvWriteByte,ControlvReadByte,ControlvMaskByte
37 *
38 */
39
Forest Bond92b96792009-06-13 07:38:31 -040040#include "int.h"
Forest Bond92b96792009-06-13 07:38:31 -040041#include "rxtx.h"
Forest Bond92b96792009-06-13 07:38:31 -040042#include "dpc.h"
Forest Bond92b96792009-06-13 07:38:31 -040043#include "desc.h"
Forest Bond92b96792009-06-13 07:38:31 -040044#include "device.h"
Malcolm Priestley62c85262014-05-26 13:59:07 +010045#include "usbpipe.h"
Forest Bond92b96792009-06-13 07:38:31 -040046
Malcolm Priestleyb2435172014-07-21 22:40:47 +010047#define USB_CTL_WAIT 500 /* ms */
Forest Bond92b96792009-06-13 07:38:31 -040048
Malcolm Priestley1390b022014-05-26 13:59:01 +010049int vnt_control_out(struct vnt_private *priv, u8 request, u16 value,
Malcolm Priestley0f06a732014-05-17 09:50:21 +010050 u16 index, u16 length, u8 *buffer)
Forest Bond92b96792009-06-13 07:38:31 -040051{
Malcolm Priestley0f06a732014-05-17 09:50:21 +010052 int status = 0;
Forest Bond92b96792009-06-13 07:38:31 -040053
Malcolm Priestleycbcc9a32014-07-24 21:13:20 +010054 if (test_bit(DEVICE_FLAGS_DISCONNECTED, &priv->flags))
Malcolm Priestleye1feda12013-10-14 19:44:13 +010055 return STATUS_FAILURE;
56
Malcolm Priestley0f06a732014-05-17 09:50:21 +010057 mutex_lock(&priv->usb_lock);
Malcolm Priestleyc91b1862014-05-15 22:49:19 +010058
Malcolm Priestley0f06a732014-05-17 09:50:21 +010059 status = usb_control_msg(priv->usb,
60 usb_sndctrlpipe(priv->usb, 0), request, 0x40, value,
61 index, buffer, length, USB_CTL_WAIT);
Forest Bond92b96792009-06-13 07:38:31 -040062
Malcolm Priestley0f06a732014-05-17 09:50:21 +010063 mutex_unlock(&priv->usb_lock);
Malcolm Priestleyc91b1862014-05-15 22:49:19 +010064
Malcolm Priestley0f06a732014-05-17 09:50:21 +010065 if (status < (int)length)
Malcolm Priestley7021b682014-05-15 22:49:22 +010066 return STATUS_FAILURE;
67
68 return STATUS_SUCCESS;
Forest Bond92b96792009-06-13 07:38:31 -040069}
70
Malcolm Priestley285d58c2014-05-26 13:59:03 +010071void 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
Malcolm Priestley441c21c2014-05-26 13:59:02 +010077int vnt_control_in(struct vnt_private *priv, u8 request, u16 value,
Malcolm Priestley9af49fd2014-05-17 09:50:22 +010078 u16 index, u16 length, u8 *buffer)
Forest Bond92b96792009-06-13 07:38:31 -040079{
Malcolm Priestley9af49fd2014-05-17 09:50:22 +010080 int status;
Forest Bond92b96792009-06-13 07:38:31 -040081
Malcolm Priestleycbcc9a32014-07-24 21:13:20 +010082 if (test_bit(DEVICE_FLAGS_DISCONNECTED, &priv->flags))
Malcolm Priestleye1feda12013-10-14 19:44:13 +010083 return STATUS_FAILURE;
84
Malcolm Priestley9af49fd2014-05-17 09:50:22 +010085 mutex_lock(&priv->usb_lock);
Malcolm Priestleyc91b1862014-05-15 22:49:19 +010086
Malcolm Priestley9af49fd2014-05-17 09:50:22 +010087 status = usb_control_msg(priv->usb,
88 usb_rcvctrlpipe(priv->usb, 0), request, 0xc0, value,
89 index, buffer, length, USB_CTL_WAIT);
Forest Bond92b96792009-06-13 07:38:31 -040090
Malcolm Priestley9af49fd2014-05-17 09:50:22 +010091 mutex_unlock(&priv->usb_lock);
Malcolm Priestleyc91b1862014-05-15 22:49:19 +010092
Malcolm Priestley9af49fd2014-05-17 09:50:22 +010093 if (status < (int)length)
Malcolm Priestley0fb2af32014-05-15 22:49:23 +010094 return STATUS_FAILURE;
Forest Bond92b96792009-06-13 07:38:31 -040095
Malcolm Priestley0fb2af32014-05-15 22:49:23 +010096 return STATUS_SUCCESS;
Forest Bond92b96792009-06-13 07:38:31 -040097}
98
Malcolm Priestley53742902014-05-26 13:59:06 +010099void 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
Malcolm Priestley34f98e32014-07-15 19:54:38 +0100105static void vnt_start_interrupt_urb_complete(struct urb *urb)
Forest Bond92b96792009-06-13 07:38:31 -0400106{
Malcolm Priestley599e4b52014-02-25 20:51:51 +0000107 struct vnt_private *priv = urb->context;
Malcolm Priestleyd9ad7a92014-02-17 21:27:30 +0000108 int status;
Forest Bond92b96792009-06-13 07:38:31 -0400109
Malcolm Priestleyc98fbf92014-02-17 21:16:20 +0000110 switch (urb->status) {
111 case 0:
112 case -ETIMEDOUT:
113 break;
114 case -ECONNRESET:
115 case -ENOENT:
116 case -ESHUTDOWN:
Malcolm Priestleyf764e002014-02-19 18:39:09 +0000117 priv->int_buf.in_use = false;
Malcolm Priestleyc98fbf92014-02-17 21:16:20 +0000118 return;
119 default:
120 break;
121 }
122
Malcolm Priestleyd9ad7a92014-02-17 21:27:30 +0000123 status = urb->status;
Forest Bond92b96792009-06-13 07:38:31 -0400124
Malcolm Priestleyd9ad7a92014-02-17 21:27:30 +0000125 if (status != STATUS_SUCCESS) {
Malcolm Priestleyf764e002014-02-19 18:39:09 +0000126 priv->int_buf.in_use = false;
Forest Bond92b96792009-06-13 07:38:31 -0400127
Malcolm Priestley8a660262014-05-17 09:50:20 +0100128 dev_dbg(&priv->usb->dev, "%s status = %d\n", __func__, status);
Malcolm Priestley247b4b62014-02-17 21:12:51 +0000129 } else {
Malcolm Priestleye360d2b2014-07-15 19:54:34 +0100130 vnt_int_process_data(priv);
Malcolm Priestley247b4b62014-02-17 21:12:51 +0000131 }
Malcolm Priestley749627f2014-02-17 21:24:33 +0000132
Malcolm Priestley3d582482014-07-18 06:36:14 +0100133 status = usb_submit_urb(priv->interrupt_urb, GFP_ATOMIC);
Ragnar B. Johannsson86140342014-08-10 22:23:48 +0000134 if (status)
Malcolm Priestley8a660262014-05-17 09:50:20 +0100135 dev_dbg(&priv->usb->dev, "Submit int URB failed %d\n", status);
Ragnar B. Johannsson86140342014-08-10 22:23:48 +0000136 else
Malcolm Priestleyf764e002014-02-19 18:39:09 +0000137 priv->int_buf.in_use = true;
Forest Bond92b96792009-06-13 07:38:31 -0400138}
139
Malcolm Priestleyeaa56792014-07-21 22:40:42 +0100140int 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
Malcolm Priestley08823af2014-07-15 19:54:42 +0100167static void vnt_submit_rx_urb_complete(struct urb *urb)
Forest Bond92b96792009-06-13 07:38:31 -0400168{
Malcolm Priestley599e4b52014-02-25 20:51:51 +0000169 struct vnt_rcb *rcb = urb->context;
Malcolm Priestley325de982014-07-18 06:36:11 +0100170 struct vnt_private *priv = rcb->priv;
Malcolm Priestley29b02372014-05-15 22:49:20 +0100171 unsigned long flags;
Forest Bond92b96792009-06-13 07:38:31 -0400172
Malcolm Priestley67638982014-02-25 20:51:48 +0000173 switch (urb->status) {
174 case 0:
Malcolm Priestley67638982014-02-25 20:51:48 +0000175 break;
176 case -ECONNRESET:
177 case -ENOENT:
178 case -ESHUTDOWN:
179 return;
180 case -ETIMEDOUT:
181 default:
Malcolm Priestley8a660262014-05-17 09:50:20 +0100182 dev_dbg(&priv->usb->dev, "BULK In failed %d\n", urb->status);
Malcolm Priestley67638982014-02-25 20:51:48 +0000183 break;
184 }
Forest Bond92b96792009-06-13 07:38:31 -0400185
Malcolm Priestleyd4fa2ab2014-02-25 20:51:49 +0000186 if (urb->actual_length) {
Malcolm Priestley29b02372014-05-15 22:49:20 +0100187 spin_lock_irqsave(&priv->lock, flags);
Forest Bond92b96792009-06-13 07:38:31 -0400188
Malcolm Priestleyf5283272014-07-05 19:24:26 +0100189 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");
Malcolm Priestleyd4fa2ab2014-02-25 20:51:49 +0000194
Malcolm Priestley325de982014-07-18 06:36:11 +0100195 rcb->in_use = false;
Malcolm Priestleyf5283272014-07-05 19:24:26 +0100196 spin_unlock_irqrestore(&priv->lock, flags);
197 return;
198 }
Malcolm Priestley618ff9c2014-07-06 23:19:46 +0100199 } else {
200 skb_push(rcb->skb, skb_headroom(rcb->skb));
201 skb_trim(rcb->skb, 0);
Malcolm Priestley8cffb3c2014-06-28 23:55:42 +0100202 }
Malcolm Priestleyd4fa2ab2014-02-25 20:51:49 +0000203
Malcolm Priestley8cffb3c2014-06-28 23:55:42 +0100204 urb->transfer_buffer = skb_put(rcb->skb,
205 skb_tailroom(rcb->skb));
Malcolm Priestleyf5283272014-07-05 19:24:26 +0100206
207 spin_unlock_irqrestore(&priv->lock, flags);
Malcolm Priestley8cffb3c2014-06-28 23:55:42 +0100208 }
209
210 if (usb_submit_urb(urb, GFP_ATOMIC)) {
211 dev_dbg(&priv->usb->dev, "Failed to re submit rx skb\n");
212
Malcolm Priestley325de982014-07-18 06:36:11 +0100213 rcb->in_use = false;
Malcolm Priestleyd4fa2ab2014-02-25 20:51:49 +0000214 }
Forest Bond92b96792009-06-13 07:38:31 -0400215}
216
Malcolm Priestley93eac3b2014-07-21 22:40:43 +0100217int 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
Malcolm Priestleyceebd902014-07-15 19:54:40 +0100247static void vnt_tx_context_complete(struct urb *urb)
Forest Bond92b96792009-06-13 07:38:31 -0400248{
Malcolm Priestley599e4b52014-02-25 20:51:51 +0000249 struct vnt_usb_send_context *context = urb->context;
Malcolm Priestley30a05b32014-05-15 22:49:11 +0100250 struct vnt_private *priv = context->priv;
Forest Bond92b96792009-06-13 07:38:31 -0400251
Malcolm Priestleye8152bf2014-02-19 21:53:35 +0000252 switch (urb->status) {
253 case 0:
Malcolm Priestley8a660262014-05-17 09:50:20 +0100254 dev_dbg(&priv->usb->dev, "Write %d bytes\n", context->buf_len);
Malcolm Priestleye8152bf2014-02-19 21:53:35 +0000255 break;
256 case -ECONNRESET:
257 case -ENOENT:
258 case -ESHUTDOWN:
Malcolm Priestley30a05b32014-05-15 22:49:11 +0100259 context->in_use = false;
Malcolm Priestleye8152bf2014-02-19 21:53:35 +0000260 return;
Malcolm Priestleyd1b2a112014-02-27 23:06:15 +0000261 case -ETIMEDOUT:
Malcolm Priestleye8152bf2014-02-19 21:53:35 +0000262 default:
Malcolm Priestley8a660262014-05-17 09:50:20 +0100263 dev_dbg(&priv->usb->dev, "BULK Out failed %d\n", urb->status);
Malcolm Priestleye8152bf2014-02-19 21:53:35 +0000264 break;
265 }
266
Malcolm Priestleyd38b13a2014-06-25 21:14:23 +0100267 if (context->type == CONTEXT_DATA_PACKET)
268 ieee80211_wake_queues(priv->hw);
Malcolm Priestley21aa2122014-02-19 21:54:45 +0000269
Malcolm Priestley71d764a2014-07-05 19:24:27 +0100270 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 }
Forest Bond92b96792009-06-13 07:38:31 -0400276}
Malcolm Priestley664b0442014-07-21 22:40:44 +0100277
278int vnt_tx_context(struct vnt_private *priv,
279 struct vnt_usb_send_context *context)
280{
281 int status;
282 struct urb *urb;
283
Malcolm Priestleycbcc9a32014-07-24 21:13:20 +0100284 if (test_bit(DEVICE_FLAGS_DISCONNECTED, &priv->flags)) {
Malcolm Priestley664b0442014-07-21 22:40:44 +0100285 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}