blob: c975c3b870938eb582d022785e9a0bdb0144e7fc [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
Abdul Hussain5699c0f2015-06-16 05:43:16 +0000144 if (priv->int_buf.in_use)
Malcolm Priestleyeaa56792014-07-21 22:40:42 +0100145 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;
Forest Bond92b96792009-06-13 07:38:31 -0400171
Malcolm Priestley67638982014-02-25 20:51:48 +0000172 switch (urb->status) {
173 case 0:
Malcolm Priestley67638982014-02-25 20:51:48 +0000174 break;
175 case -ECONNRESET:
176 case -ENOENT:
177 case -ESHUTDOWN:
178 return;
179 case -ETIMEDOUT:
180 default:
Malcolm Priestley8a660262014-05-17 09:50:20 +0100181 dev_dbg(&priv->usb->dev, "BULK In failed %d\n", urb->status);
Malcolm Priestley67638982014-02-25 20:51:48 +0000182 break;
183 }
Forest Bond92b96792009-06-13 07:38:31 -0400184
Malcolm Priestleyd4fa2ab2014-02-25 20:51:49 +0000185 if (urb->actual_length) {
Malcolm Priestleyf5283272014-07-05 19:24:26 +0100186 if (vnt_rx_data(priv, rcb, urb->actual_length)) {
187 rcb->skb = dev_alloc_skb(priv->rx_buf_sz);
188 if (!rcb->skb) {
189 dev_dbg(&priv->usb->dev,
190 "Failed to re-alloc rx skb\n");
Malcolm Priestleyd4fa2ab2014-02-25 20:51:49 +0000191
Malcolm Priestley325de982014-07-18 06:36:11 +0100192 rcb->in_use = false;
Malcolm Priestleyf5283272014-07-05 19:24:26 +0100193 return;
194 }
Malcolm Priestley618ff9c2014-07-06 23:19:46 +0100195 } else {
196 skb_push(rcb->skb, skb_headroom(rcb->skb));
197 skb_trim(rcb->skb, 0);
Malcolm Priestley8cffb3c2014-06-28 23:55:42 +0100198 }
Malcolm Priestleyd4fa2ab2014-02-25 20:51:49 +0000199
Malcolm Priestley8cffb3c2014-06-28 23:55:42 +0100200 urb->transfer_buffer = skb_put(rcb->skb,
201 skb_tailroom(rcb->skb));
202 }
203
204 if (usb_submit_urb(urb, GFP_ATOMIC)) {
205 dev_dbg(&priv->usb->dev, "Failed to re submit rx skb\n");
206
Malcolm Priestley325de982014-07-18 06:36:11 +0100207 rcb->in_use = false;
Malcolm Priestleyd4fa2ab2014-02-25 20:51:49 +0000208 }
Forest Bond92b96792009-06-13 07:38:31 -0400209}
210
Malcolm Priestley93eac3b2014-07-21 22:40:43 +0100211int vnt_submit_rx_urb(struct vnt_private *priv, struct vnt_rcb *rcb)
212{
213 int status = 0;
214 struct urb *urb;
215
216 urb = rcb->urb;
217 if (rcb->skb == NULL) {
218 dev_dbg(&priv->usb->dev, "rcb->skb is null\n");
219 return status;
220 }
221
222 usb_fill_bulk_urb(urb,
223 priv->usb,
224 usb_rcvbulkpipe(priv->usb, 2),
225 skb_put(rcb->skb, skb_tailroom(rcb->skb)),
226 MAX_TOTAL_SIZE_WITH_ALL_HEADERS,
227 vnt_submit_rx_urb_complete,
228 rcb);
229
230 status = usb_submit_urb(urb, GFP_ATOMIC);
231 if (status != 0) {
232 dev_dbg(&priv->usb->dev, "Submit Rx URB failed %d\n", status);
233 return STATUS_FAILURE;
234 }
235
236 rcb->in_use = true;
237
238 return status;
239}
240
Malcolm Priestleyceebd902014-07-15 19:54:40 +0100241static void vnt_tx_context_complete(struct urb *urb)
Forest Bond92b96792009-06-13 07:38:31 -0400242{
Malcolm Priestley599e4b52014-02-25 20:51:51 +0000243 struct vnt_usb_send_context *context = urb->context;
Malcolm Priestley30a05b32014-05-15 22:49:11 +0100244 struct vnt_private *priv = context->priv;
Forest Bond92b96792009-06-13 07:38:31 -0400245
Malcolm Priestleye8152bf2014-02-19 21:53:35 +0000246 switch (urb->status) {
247 case 0:
Malcolm Priestley8a660262014-05-17 09:50:20 +0100248 dev_dbg(&priv->usb->dev, "Write %d bytes\n", context->buf_len);
Malcolm Priestleye8152bf2014-02-19 21:53:35 +0000249 break;
250 case -ECONNRESET:
251 case -ENOENT:
252 case -ESHUTDOWN:
Malcolm Priestley30a05b32014-05-15 22:49:11 +0100253 context->in_use = false;
Malcolm Priestleye8152bf2014-02-19 21:53:35 +0000254 return;
Malcolm Priestleyd1b2a112014-02-27 23:06:15 +0000255 case -ETIMEDOUT:
Malcolm Priestleye8152bf2014-02-19 21:53:35 +0000256 default:
Malcolm Priestley8a660262014-05-17 09:50:20 +0100257 dev_dbg(&priv->usb->dev, "BULK Out failed %d\n", urb->status);
Malcolm Priestleye8152bf2014-02-19 21:53:35 +0000258 break;
259 }
260
Malcolm Priestleyd38b13a2014-06-25 21:14:23 +0100261 if (context->type == CONTEXT_DATA_PACKET)
262 ieee80211_wake_queues(priv->hw);
Malcolm Priestley21aa2122014-02-19 21:54:45 +0000263
Malcolm Priestley71d764a2014-07-05 19:24:27 +0100264 if (urb->status || context->type == CONTEXT_BEACON_PACKET) {
265 if (context->skb)
266 ieee80211_free_txskb(priv->hw, context->skb);
267
268 context->in_use = false;
269 }
Forest Bond92b96792009-06-13 07:38:31 -0400270}
Malcolm Priestley664b0442014-07-21 22:40:44 +0100271
272int vnt_tx_context(struct vnt_private *priv,
273 struct vnt_usb_send_context *context)
274{
275 int status;
276 struct urb *urb;
277
Malcolm Priestleycbcc9a32014-07-24 21:13:20 +0100278 if (test_bit(DEVICE_FLAGS_DISCONNECTED, &priv->flags)) {
Malcolm Priestley664b0442014-07-21 22:40:44 +0100279 context->in_use = false;
280 return STATUS_RESOURCES;
281 }
282
283 urb = context->urb;
284
285 usb_fill_bulk_urb(urb,
286 priv->usb,
287 usb_sndbulkpipe(priv->usb, 3),
288 context->data,
289 context->buf_len,
290 vnt_tx_context_complete,
291 context);
292
293 status = usb_submit_urb(urb, GFP_ATOMIC);
294 if (status != 0) {
295 dev_dbg(&priv->usb->dev, "Submit Tx URB failed %d\n", status);
296
297 context->in_use = false;
298 return STATUS_FAILURE;
299 }
300
301 return STATUS_PENDING;
302}