blob: 126294d1f1bb788569c6f3ec3e0880689ed79a79 [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 * ControlvMaskByte - Read one byte from MEM/BB/MAC/EEPROM and clear/set some bits in the same address
34 *
35 * Revision History:
36 * 04-05-2004 Jerry Chen: Initial release
37 * 11-24-2004 Warren Hsu: Add ControlvWriteByte,ControlvReadByte,ControlvMaskByte
38 *
39 */
40
Forest Bond92b96792009-06-13 07:38:31 -040041#include "int.h"
Forest Bond92b96792009-06-13 07:38:31 -040042#include "rxtx.h"
Forest Bond92b96792009-06-13 07:38:31 -040043#include "dpc.h"
Forest Bond92b96792009-06-13 07:38:31 -040044#include "desc.h"
Forest Bond92b96792009-06-13 07:38:31 -040045#include "device.h"
Malcolm Priestley62c85262014-05-26 13:59:07 +010046#include "usbpipe.h"
Forest Bond92b96792009-06-13 07:38:31 -040047
Forest Bond92b96792009-06-13 07:38:31 -040048//endpoint def
49//endpoint 0: control
50//endpoint 1: interrupt
51//endpoint 2: read bulk
52//endpoint 3: write bulk
53
Forest Bond92b96792009-06-13 07:38:31 -040054#define USB_CTL_WAIT 500 //ms
55
56#ifndef URB_ASYNC_UNLINK
57#define URB_ASYNC_UNLINK 0
58#endif
59
Malcolm Priestley34f98e32014-07-15 19:54:38 +010060static void vnt_start_interrupt_urb_complete(struct urb *urb);
Malcolm Priestley08823af2014-07-15 19:54:42 +010061static void vnt_submit_rx_urb_complete(struct urb *urb);
Malcolm Priestleyceebd902014-07-15 19:54:40 +010062static void vnt_tx_context_complete(struct urb *urb);
Forest Bond92b96792009-06-13 07:38:31 -040063
Malcolm Priestley1390b022014-05-26 13:59:01 +010064int vnt_control_out(struct vnt_private *priv, u8 request, u16 value,
Malcolm Priestley0f06a732014-05-17 09:50:21 +010065 u16 index, u16 length, u8 *buffer)
Forest Bond92b96792009-06-13 07:38:31 -040066{
Malcolm Priestley0f06a732014-05-17 09:50:21 +010067 int status = 0;
Forest Bond92b96792009-06-13 07:38:31 -040068
Malcolm Priestley0f06a732014-05-17 09:50:21 +010069 if (priv->Flags & fMP_DISCONNECTED)
Malcolm Priestleye1feda12013-10-14 19:44:13 +010070 return STATUS_FAILURE;
71
Malcolm Priestley0f06a732014-05-17 09:50:21 +010072 mutex_lock(&priv->usb_lock);
Malcolm Priestleyc91b1862014-05-15 22:49:19 +010073
Malcolm Priestley0f06a732014-05-17 09:50:21 +010074 status = usb_control_msg(priv->usb,
75 usb_sndctrlpipe(priv->usb, 0), request, 0x40, value,
76 index, buffer, length, USB_CTL_WAIT);
Forest Bond92b96792009-06-13 07:38:31 -040077
Malcolm Priestley0f06a732014-05-17 09:50:21 +010078 mutex_unlock(&priv->usb_lock);
Malcolm Priestleyc91b1862014-05-15 22:49:19 +010079
Malcolm Priestley0f06a732014-05-17 09:50:21 +010080 if (status < (int)length)
Malcolm Priestley7021b682014-05-15 22:49:22 +010081 return STATUS_FAILURE;
82
83 return STATUS_SUCCESS;
Forest Bond92b96792009-06-13 07:38:31 -040084}
85
Malcolm Priestley285d58c2014-05-26 13:59:03 +010086void vnt_control_out_u8(struct vnt_private *priv, u8 reg, u8 reg_off, u8 data)
87{
88 vnt_control_out(priv, MESSAGE_TYPE_WRITE,
89 reg_off, reg, sizeof(u8), &data);
90}
91
Malcolm Priestley441c21c2014-05-26 13:59:02 +010092int vnt_control_in(struct vnt_private *priv, u8 request, u16 value,
Malcolm Priestley9af49fd2014-05-17 09:50:22 +010093 u16 index, u16 length, u8 *buffer)
Forest Bond92b96792009-06-13 07:38:31 -040094{
Malcolm Priestley9af49fd2014-05-17 09:50:22 +010095 int status;
Forest Bond92b96792009-06-13 07:38:31 -040096
Malcolm Priestley9af49fd2014-05-17 09:50:22 +010097 if (priv->Flags & fMP_DISCONNECTED)
Malcolm Priestleye1feda12013-10-14 19:44:13 +010098 return STATUS_FAILURE;
99
Malcolm Priestley9af49fd2014-05-17 09:50:22 +0100100 mutex_lock(&priv->usb_lock);
Malcolm Priestleyc91b1862014-05-15 22:49:19 +0100101
Malcolm Priestley9af49fd2014-05-17 09:50:22 +0100102 status = usb_control_msg(priv->usb,
103 usb_rcvctrlpipe(priv->usb, 0), request, 0xc0, value,
104 index, buffer, length, USB_CTL_WAIT);
Forest Bond92b96792009-06-13 07:38:31 -0400105
Malcolm Priestley9af49fd2014-05-17 09:50:22 +0100106 mutex_unlock(&priv->usb_lock);
Malcolm Priestleyc91b1862014-05-15 22:49:19 +0100107
Malcolm Priestley9af49fd2014-05-17 09:50:22 +0100108 if (status < (int)length)
Malcolm Priestley0fb2af32014-05-15 22:49:23 +0100109 return STATUS_FAILURE;
Forest Bond92b96792009-06-13 07:38:31 -0400110
Malcolm Priestley0fb2af32014-05-15 22:49:23 +0100111 return STATUS_SUCCESS;
Forest Bond92b96792009-06-13 07:38:31 -0400112}
113
Malcolm Priestley53742902014-05-26 13:59:06 +0100114void vnt_control_in_u8(struct vnt_private *priv, u8 reg, u8 reg_off, u8 *data)
115{
116 vnt_control_in(priv, MESSAGE_TYPE_READ,
117 reg_off, reg, sizeof(u8), data);
118}
119
Malcolm Priestleyb0bc5722014-07-15 19:54:37 +0100120int vnt_start_interrupt_urb(struct vnt_private *priv)
Forest Bond92b96792009-06-13 07:38:31 -0400121{
Malcolm Priestley5f38b782014-02-19 18:37:32 +0000122 int status = STATUS_FAILURE;
Forest Bond92b96792009-06-13 07:38:31 -0400123
Malcolm Priestleyf764e002014-02-19 18:39:09 +0000124 if (priv->int_buf.in_use == true)
Malcolm Priestley5f38b782014-02-19 18:37:32 +0000125 return STATUS_FAILURE;
Forest Bond92b96792009-06-13 07:38:31 -0400126
Malcolm Priestleyf764e002014-02-19 18:39:09 +0000127 priv->int_buf.in_use = true;
Forest Bond92b96792009-06-13 07:38:31 -0400128
Malcolm Priestley5f38b782014-02-19 18:37:32 +0000129 usb_fill_int_urb(priv->pInterruptURB,
130 priv->usb,
Malcolm Priestleyb9d93d12014-03-16 12:34:11 +0000131 usb_rcvintpipe(priv->usb, 1),
Malcolm Priestleyf764e002014-02-19 18:39:09 +0000132 priv->int_buf.data_buf,
Forest Bond92b96792009-06-13 07:38:31 -0400133 MAX_INTERRUPT_SIZE,
Malcolm Priestley34f98e32014-07-15 19:54:38 +0100134 vnt_start_interrupt_urb_complete,
Malcolm Priestley5f38b782014-02-19 18:37:32 +0000135 priv,
136 priv->int_interval);
Forest Bond92b96792009-06-13 07:38:31 -0400137
Malcolm Priestley5f38b782014-02-19 18:37:32 +0000138 status = usb_submit_urb(priv->pInterruptURB, GFP_ATOMIC);
139 if (status) {
Malcolm Priestley8a660262014-05-17 09:50:20 +0100140 dev_dbg(&priv->usb->dev, "Submit int URB failed %d\n", status);
Malcolm Priestleyf764e002014-02-19 18:39:09 +0000141 priv->int_buf.in_use = false;
Malcolm Priestley59858f52014-02-19 18:36:37 +0000142 }
Forest Bond92b96792009-06-13 07:38:31 -0400143
Malcolm Priestley5f38b782014-02-19 18:37:32 +0000144 return status;
Forest Bond92b96792009-06-13 07:38:31 -0400145}
146
Malcolm Priestley34f98e32014-07-15 19:54:38 +0100147static void vnt_start_interrupt_urb_complete(struct urb *urb)
Forest Bond92b96792009-06-13 07:38:31 -0400148{
Malcolm Priestley599e4b52014-02-25 20:51:51 +0000149 struct vnt_private *priv = urb->context;
Malcolm Priestleyd9ad7a92014-02-17 21:27:30 +0000150 int status;
Forest Bond92b96792009-06-13 07:38:31 -0400151
Malcolm Priestleyc98fbf92014-02-17 21:16:20 +0000152 switch (urb->status) {
153 case 0:
154 case -ETIMEDOUT:
155 break;
156 case -ECONNRESET:
157 case -ENOENT:
158 case -ESHUTDOWN:
Malcolm Priestleyf764e002014-02-19 18:39:09 +0000159 priv->int_buf.in_use = false;
Malcolm Priestleyc98fbf92014-02-17 21:16:20 +0000160 return;
161 default:
162 break;
163 }
164
Malcolm Priestleyd9ad7a92014-02-17 21:27:30 +0000165 status = urb->status;
Forest Bond92b96792009-06-13 07:38:31 -0400166
Malcolm Priestleyd9ad7a92014-02-17 21:27:30 +0000167 if (status != STATUS_SUCCESS) {
Malcolm Priestleyf764e002014-02-19 18:39:09 +0000168 priv->int_buf.in_use = false;
Forest Bond92b96792009-06-13 07:38:31 -0400169
Malcolm Priestley8a660262014-05-17 09:50:20 +0100170 dev_dbg(&priv->usb->dev, "%s status = %d\n", __func__, status);
Malcolm Priestley247b4b62014-02-17 21:12:51 +0000171 } else {
Malcolm Priestleye360d2b2014-07-15 19:54:34 +0100172 vnt_int_process_data(priv);
Malcolm Priestley247b4b62014-02-17 21:12:51 +0000173 }
Malcolm Priestley749627f2014-02-17 21:24:33 +0000174
Malcolm Priestleyd9ad7a92014-02-17 21:27:30 +0000175 status = usb_submit_urb(priv->pInterruptURB, GFP_ATOMIC);
176 if (status) {
Malcolm Priestley8a660262014-05-17 09:50:20 +0100177 dev_dbg(&priv->usb->dev, "Submit int URB failed %d\n", status);
Malcolm Priestleyd9ad7a92014-02-17 21:27:30 +0000178 } else {
Malcolm Priestleyf764e002014-02-19 18:39:09 +0000179 priv->int_buf.in_use = true;
Malcolm Priestleyd9ad7a92014-02-17 21:27:30 +0000180 }
181
182 return;
Forest Bond92b96792009-06-13 07:38:31 -0400183}
184
Malcolm Priestley2dc37af2014-07-15 19:54:41 +0100185int vnt_submit_rx_urb(struct vnt_private *priv, struct vnt_rcb *rcb)
Forest Bond92b96792009-06-13 07:38:31 -0400186{
Malcolm Priestley0b787d72014-02-25 20:51:50 +0000187 int status = 0;
188 struct urb *urb;
Forest Bond92b96792009-06-13 07:38:31 -0400189
Malcolm Priestley325de982014-07-18 06:36:11 +0100190 urb = rcb->urb;
Malcolm Priestley0b787d72014-02-25 20:51:50 +0000191 if (rcb->skb == NULL) {
Malcolm Priestley8a660262014-05-17 09:50:20 +0100192 dev_dbg(&priv->usb->dev, "rcb->skb is null\n");
Malcolm Priestley0b787d72014-02-25 20:51:50 +0000193 return status;
194 }
Forest Bond92b96792009-06-13 07:38:31 -0400195
Malcolm Priestley0b787d72014-02-25 20:51:50 +0000196 usb_fill_bulk_urb(urb,
197 priv->usb,
198 usb_rcvbulkpipe(priv->usb, 2),
Malcolm Priestley63b99072014-06-25 21:14:22 +0100199 skb_put(rcb->skb, skb_tailroom(rcb->skb)),
Forest Bond92b96792009-06-13 07:38:31 -0400200 MAX_TOTAL_SIZE_WITH_ALL_HEADERS,
Malcolm Priestley08823af2014-07-15 19:54:42 +0100201 vnt_submit_rx_urb_complete,
Malcolm Priestley0b787d72014-02-25 20:51:50 +0000202 rcb);
Forest Bond92b96792009-06-13 07:38:31 -0400203
Malcolm Priestley0b787d72014-02-25 20:51:50 +0000204 status = usb_submit_urb(urb, GFP_ATOMIC);
205 if (status != 0) {
Malcolm Priestley8a660262014-05-17 09:50:20 +0100206 dev_dbg(&priv->usb->dev, "Submit Rx URB failed %d\n", status);
Forest Bond92b96792009-06-13 07:38:31 -0400207 return STATUS_FAILURE ;
208 }
Forest Bond92b96792009-06-13 07:38:31 -0400209
Malcolm Priestley325de982014-07-18 06:36:11 +0100210 rcb->in_use = true;
Malcolm Priestley0b787d72014-02-25 20:51:50 +0000211
212 return status;
Forest Bond92b96792009-06-13 07:38:31 -0400213}
214
Malcolm Priestley08823af2014-07-15 19:54:42 +0100215static void vnt_submit_rx_urb_complete(struct urb *urb)
Forest Bond92b96792009-06-13 07:38:31 -0400216{
Malcolm Priestley599e4b52014-02-25 20:51:51 +0000217 struct vnt_rcb *rcb = urb->context;
Malcolm Priestley325de982014-07-18 06:36:11 +0100218 struct vnt_private *priv = rcb->priv;
Malcolm Priestley29b02372014-05-15 22:49:20 +0100219 unsigned long flags;
Forest Bond92b96792009-06-13 07:38:31 -0400220
Malcolm Priestley67638982014-02-25 20:51:48 +0000221 switch (urb->status) {
222 case 0:
Malcolm Priestley67638982014-02-25 20:51:48 +0000223 break;
224 case -ECONNRESET:
225 case -ENOENT:
226 case -ESHUTDOWN:
227 return;
228 case -ETIMEDOUT:
229 default:
Malcolm Priestley8a660262014-05-17 09:50:20 +0100230 dev_dbg(&priv->usb->dev, "BULK In failed %d\n", urb->status);
Malcolm Priestley67638982014-02-25 20:51:48 +0000231 break;
232 }
Forest Bond92b96792009-06-13 07:38:31 -0400233
Malcolm Priestleyd4fa2ab2014-02-25 20:51:49 +0000234 if (urb->actual_length) {
Malcolm Priestley29b02372014-05-15 22:49:20 +0100235 spin_lock_irqsave(&priv->lock, flags);
Forest Bond92b96792009-06-13 07:38:31 -0400236
Malcolm Priestleyf5283272014-07-05 19:24:26 +0100237 if (vnt_rx_data(priv, rcb, urb->actual_length)) {
238 rcb->skb = dev_alloc_skb(priv->rx_buf_sz);
239 if (!rcb->skb) {
240 dev_dbg(&priv->usb->dev,
241 "Failed to re-alloc rx skb\n");
Malcolm Priestleyd4fa2ab2014-02-25 20:51:49 +0000242
Malcolm Priestley325de982014-07-18 06:36:11 +0100243 rcb->in_use = false;
Malcolm Priestleyf5283272014-07-05 19:24:26 +0100244 spin_unlock_irqrestore(&priv->lock, flags);
245 return;
246 }
Malcolm Priestley618ff9c2014-07-06 23:19:46 +0100247 } else {
248 skb_push(rcb->skb, skb_headroom(rcb->skb));
249 skb_trim(rcb->skb, 0);
Malcolm Priestley8cffb3c2014-06-28 23:55:42 +0100250 }
Malcolm Priestleyd4fa2ab2014-02-25 20:51:49 +0000251
Malcolm Priestley8cffb3c2014-06-28 23:55:42 +0100252 urb->transfer_buffer = skb_put(rcb->skb,
253 skb_tailroom(rcb->skb));
Malcolm Priestleyf5283272014-07-05 19:24:26 +0100254
255 spin_unlock_irqrestore(&priv->lock, flags);
Malcolm Priestley8cffb3c2014-06-28 23:55:42 +0100256 }
257
258 if (usb_submit_urb(urb, GFP_ATOMIC)) {
259 dev_dbg(&priv->usb->dev, "Failed to re submit rx skb\n");
260
Malcolm Priestley325de982014-07-18 06:36:11 +0100261 rcb->in_use = false;
Malcolm Priestleyd4fa2ab2014-02-25 20:51:49 +0000262 }
263
264 return;
Forest Bond92b96792009-06-13 07:38:31 -0400265}
266
Malcolm Priestley476e7d92014-07-15 19:54:39 +0100267int vnt_tx_context(struct vnt_private *priv,
Malcolm Priestley3f382902014-02-25 20:51:45 +0000268 struct vnt_usb_send_context *context)
Forest Bond92b96792009-06-13 07:38:31 -0400269{
Malcolm Priestleyfe5d00e2012-12-10 22:14:36 +0000270 int status;
Malcolm Priestley3f382902014-02-25 20:51:45 +0000271 struct urb *urb;
Forest Bond92b96792009-06-13 07:38:31 -0400272
Malcolm Priestley3f382902014-02-25 20:51:45 +0000273 if (!(MP_IS_READY(priv) && priv->Flags & fMP_POST_WRITES)) {
Malcolm Priestley30a05b32014-05-15 22:49:11 +0100274 context->in_use = false;
Malcolm Priestley13338302014-02-25 20:51:43 +0000275 return STATUS_RESOURCES;
276 }
Forest Bond92b96792009-06-13 07:38:31 -0400277
Malcolm Priestley30a05b32014-05-15 22:49:11 +0100278 urb = context->urb;
Forest Bond92b96792009-06-13 07:38:31 -0400279
Malcolm Priestley3f382902014-02-25 20:51:45 +0000280 usb_fill_bulk_urb(urb,
281 priv->usb,
282 usb_sndbulkpipe(priv->usb, 3),
Malcolm Priestley30a05b32014-05-15 22:49:11 +0100283 context->data,
284 context->buf_len,
Malcolm Priestleyceebd902014-07-15 19:54:40 +0100285 vnt_tx_context_complete,
Malcolm Priestley3f382902014-02-25 20:51:45 +0000286 context);
287
288 status = usb_submit_urb(urb, GFP_ATOMIC);
289 if (status != 0) {
Malcolm Priestley8a660262014-05-17 09:50:20 +0100290 dev_dbg(&priv->usb->dev, "Submit Tx URB failed %d\n", status);
291
Malcolm Priestley30a05b32014-05-15 22:49:11 +0100292 context->in_use = false;
Malcolm Priestley3f382902014-02-25 20:51:45 +0000293 return STATUS_FAILURE;
294 }
295
296 return STATUS_PENDING;
Forest Bond92b96792009-06-13 07:38:31 -0400297}
298
Malcolm Priestleyceebd902014-07-15 19:54:40 +0100299static void vnt_tx_context_complete(struct urb *urb)
Forest Bond92b96792009-06-13 07:38:31 -0400300{
Malcolm Priestley599e4b52014-02-25 20:51:51 +0000301 struct vnt_usb_send_context *context = urb->context;
Malcolm Priestley30a05b32014-05-15 22:49:11 +0100302 struct vnt_private *priv = context->priv;
Forest Bond92b96792009-06-13 07:38:31 -0400303
Malcolm Priestleye8152bf2014-02-19 21:53:35 +0000304 switch (urb->status) {
305 case 0:
Malcolm Priestley8a660262014-05-17 09:50:20 +0100306 dev_dbg(&priv->usb->dev, "Write %d bytes\n", context->buf_len);
Malcolm Priestleye8152bf2014-02-19 21:53:35 +0000307 break;
308 case -ECONNRESET:
309 case -ENOENT:
310 case -ESHUTDOWN:
Malcolm Priestley30a05b32014-05-15 22:49:11 +0100311 context->in_use = false;
Malcolm Priestleye8152bf2014-02-19 21:53:35 +0000312 return;
Malcolm Priestleyd1b2a112014-02-27 23:06:15 +0000313 case -ETIMEDOUT:
Malcolm Priestleye8152bf2014-02-19 21:53:35 +0000314 default:
Malcolm Priestley8a660262014-05-17 09:50:20 +0100315 dev_dbg(&priv->usb->dev, "BULK Out failed %d\n", urb->status);
Malcolm Priestleye8152bf2014-02-19 21:53:35 +0000316 break;
317 }
318
Malcolm Priestleyd38b13a2014-06-25 21:14:23 +0100319 if (context->type == CONTEXT_DATA_PACKET)
320 ieee80211_wake_queues(priv->hw);
Malcolm Priestley21aa2122014-02-19 21:54:45 +0000321
Malcolm Priestley71d764a2014-07-05 19:24:27 +0100322 if (urb->status || context->type == CONTEXT_BEACON_PACKET) {
323 if (context->skb)
324 ieee80211_free_txskb(priv->hw, context->skb);
325
326 context->in_use = false;
327 }
Malcolm Priestley21aa2122014-02-19 21:54:45 +0000328
329 return;
Forest Bond92b96792009-06-13 07:38:31 -0400330}