1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
4  * All rights reserved.
5  *
6  * File: usbpipe.c
7  *
8  * Purpose: Handle USB control endpoint
9  *
10  * Author: Warren Hsu
11  *
12  * Date: Mar. 29, 2005
13  *
14  * Functions:
15  *	vnt_control_out - Write variable length bytes to MEM/BB/MAC/EEPROM
16  *	vnt_control_in - Read variable length bytes from MEM/BB/MAC/EEPROM
17  *	vnt_control_out_u8 - Write one byte to MEM/BB/MAC/EEPROM
18  *	vnt_control_in_u8 - Read one byte from MEM/BB/MAC/EEPROM
19  *
20  * Revision History:
21  *      04-05-2004 Jerry Chen: Initial release
22  *      11-24-2004 Warren Hsu: Add ControlvWriteByte,ControlvReadByte,
23  *                             ControlvMaskByte
24  *
25  */
26 
27 #include "int.h"
28 #include "rxtx.h"
29 #include "dpc.h"
30 #include "desc.h"
31 #include "device.h"
32 #include "usbpipe.h"
33 
34 #define USB_CTL_WAIT	500 /* ms */
35 
vnt_control_out(struct vnt_private * priv,u8 request,u16 value,u16 index,u16 length,u8 * buffer)36 int vnt_control_out(struct vnt_private *priv, u8 request, u16 value,
37 		    u16 index, u16 length, u8 *buffer)
38 {
39 	int ret = 0;
40 	u8 *usb_buffer;
41 
42 	if (test_bit(DEVICE_FLAGS_DISCONNECTED, &priv->flags)) {
43 		ret = -EINVAL;
44 		goto end;
45 	}
46 
47 	mutex_lock(&priv->usb_lock);
48 
49 	usb_buffer = kmemdup(buffer, length, GFP_KERNEL);
50 	if (!usb_buffer) {
51 		ret = -ENOMEM;
52 		goto end_unlock;
53 	}
54 
55 	ret = usb_control_msg(priv->usb,
56 			      usb_sndctrlpipe(priv->usb, 0),
57 			      request, 0x40, value,
58 			      index, usb_buffer, length, USB_CTL_WAIT);
59 
60 	kfree(usb_buffer);
61 
62 	if (ret >= 0 && ret < (int)length)
63 		ret = -EIO;
64 
65 end_unlock:
66 	mutex_unlock(&priv->usb_lock);
67 end:
68 	return ret;
69 }
70 
vnt_control_out_u8(struct vnt_private * priv,u8 reg,u8 reg_off,u8 data)71 int vnt_control_out_u8(struct vnt_private *priv, u8 reg, u8 reg_off, u8 data)
72 {
73 	return vnt_control_out(priv, MESSAGE_TYPE_WRITE,
74 			       reg_off, reg, sizeof(u8), &data);
75 }
76 
vnt_control_in(struct vnt_private * priv,u8 request,u16 value,u16 index,u16 length,u8 * buffer)77 int vnt_control_in(struct vnt_private *priv, u8 request, u16 value,
78 		   u16 index, u16 length, u8 *buffer)
79 {
80 	int ret = 0;
81 	u8 *usb_buffer;
82 
83 	if (test_bit(DEVICE_FLAGS_DISCONNECTED, &priv->flags)) {
84 		ret = -EINVAL;
85 		goto end;
86 	}
87 
88 	mutex_lock(&priv->usb_lock);
89 
90 	usb_buffer = kmalloc(length, GFP_KERNEL);
91 	if (!usb_buffer) {
92 		ret = -ENOMEM;
93 		goto end_unlock;
94 	}
95 
96 	ret = usb_control_msg(priv->usb,
97 			      usb_rcvctrlpipe(priv->usb, 0),
98 			      request, 0xc0, value,
99 			      index, usb_buffer, length, USB_CTL_WAIT);
100 
101 	if (ret == length)
102 		memcpy(buffer, usb_buffer, length);
103 
104 	kfree(usb_buffer);
105 
106 	if (ret >= 0 && ret < (int)length)
107 		ret = -EIO;
108 
109 end_unlock:
110 	mutex_unlock(&priv->usb_lock);
111 end:
112 	return ret;
113 }
114 
vnt_control_in_u8(struct vnt_private * priv,u8 reg,u8 reg_off,u8 * data)115 int vnt_control_in_u8(struct vnt_private *priv, u8 reg, u8 reg_off, u8 *data)
116 {
117 	return vnt_control_in(priv, MESSAGE_TYPE_READ,
118 			      reg_off, reg, sizeof(u8), data);
119 }
120 
vnt_start_interrupt_urb_complete(struct urb * urb)121 static void vnt_start_interrupt_urb_complete(struct urb *urb)
122 {
123 	struct vnt_private *priv = urb->context;
124 	int status = urb->status;
125 
126 	switch (status) {
127 	case 0:
128 	case -ETIMEDOUT:
129 		break;
130 	case -ECONNRESET:
131 	case -ENOENT:
132 	case -ESHUTDOWN:
133 		priv->int_buf.in_use = false;
134 		return;
135 	default:
136 		break;
137 	}
138 
139 	if (status) {
140 		priv->int_buf.in_use = false;
141 
142 		dev_dbg(&priv->usb->dev, "%s status = %d\n", __func__, status);
143 	} else {
144 		vnt_int_process_data(priv);
145 	}
146 
147 	status = usb_submit_urb(priv->interrupt_urb, GFP_ATOMIC);
148 	if (status)
149 		dev_dbg(&priv->usb->dev, "Submit int URB failed %d\n", status);
150 	else
151 		priv->int_buf.in_use = true;
152 }
153 
vnt_start_interrupt_urb(struct vnt_private * priv)154 int vnt_start_interrupt_urb(struct vnt_private *priv)
155 {
156 	int ret = 0;
157 
158 	if (priv->int_buf.in_use) {
159 		ret = -EBUSY;
160 		goto err;
161 	}
162 
163 	priv->int_buf.in_use = true;
164 
165 	usb_fill_int_urb(priv->interrupt_urb,
166 			 priv->usb,
167 			 usb_rcvintpipe(priv->usb, 1),
168 			 priv->int_buf.data_buf,
169 			 MAX_INTERRUPT_SIZE,
170 			 vnt_start_interrupt_urb_complete,
171 			 priv,
172 			 priv->int_interval);
173 
174 	ret = usb_submit_urb(priv->interrupt_urb, GFP_ATOMIC);
175 	if (ret) {
176 		dev_dbg(&priv->usb->dev, "Submit int URB failed %d\n", ret);
177 		goto err_submit;
178 	}
179 
180 	return 0;
181 
182 err_submit:
183 	priv->int_buf.in_use = false;
184 err:
185 	return ret;
186 }
187 
vnt_submit_rx_urb_complete(struct urb * urb)188 static void vnt_submit_rx_urb_complete(struct urb *urb)
189 {
190 	struct vnt_rcb *rcb = urb->context;
191 	struct vnt_private *priv = rcb->priv;
192 
193 	switch (urb->status) {
194 	case 0:
195 		break;
196 	case -ECONNRESET:
197 	case -ENOENT:
198 	case -ESHUTDOWN:
199 		return;
200 	case -ETIMEDOUT:
201 	default:
202 		dev_dbg(&priv->usb->dev, "BULK In failed %d\n", urb->status);
203 		break;
204 	}
205 
206 	if (urb->actual_length) {
207 		if (vnt_rx_data(priv, rcb, urb->actual_length)) {
208 			rcb->skb = dev_alloc_skb(priv->rx_buf_sz);
209 			if (!rcb->skb) {
210 				rcb->in_use = false;
211 				return;
212 			}
213 		} else {
214 			skb_push(rcb->skb, skb_headroom(rcb->skb));
215 			skb_trim(rcb->skb, 0);
216 		}
217 
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 
225 		rcb->in_use = false;
226 	}
227 }
228 
vnt_submit_rx_urb(struct vnt_private * priv,struct vnt_rcb * rcb)229 int vnt_submit_rx_urb(struct vnt_private *priv, struct vnt_rcb *rcb)
230 {
231 	int ret = 0;
232 	struct urb *urb = rcb->urb;
233 
234 	if (!rcb->skb) {
235 		dev_dbg(&priv->usb->dev, "rcb->skb is null\n");
236 		ret = -EINVAL;
237 		goto end;
238 	}
239 
240 	usb_fill_bulk_urb(urb,
241 			  priv->usb,
242 			  usb_rcvbulkpipe(priv->usb, 2),
243 			  skb_put(rcb->skb, skb_tailroom(rcb->skb)),
244 			  MAX_TOTAL_SIZE_WITH_ALL_HEADERS,
245 			  vnt_submit_rx_urb_complete,
246 			  rcb);
247 
248 	ret = usb_submit_urb(urb, GFP_ATOMIC);
249 	if (ret) {
250 		dev_dbg(&priv->usb->dev, "Submit Rx URB failed %d\n", ret);
251 		goto end;
252 	}
253 
254 	rcb->in_use = true;
255 
256 end:
257 	return ret;
258 }
259 
vnt_tx_context_complete(struct urb * urb)260 static void vnt_tx_context_complete(struct urb *urb)
261 {
262 	struct vnt_usb_send_context *context = urb->context;
263 	struct vnt_private *priv = context->priv;
264 
265 	switch (urb->status) {
266 	case 0:
267 		dev_dbg(&priv->usb->dev, "Write %d bytes\n", context->buf_len);
268 		break;
269 	case -ECONNRESET:
270 	case -ENOENT:
271 	case -ESHUTDOWN:
272 		context->in_use = false;
273 		return;
274 	case -ETIMEDOUT:
275 	default:
276 		dev_dbg(&priv->usb->dev, "BULK Out failed %d\n", urb->status);
277 		break;
278 	}
279 
280 	if (context->type == CONTEXT_DATA_PACKET)
281 		ieee80211_wake_queues(priv->hw);
282 
283 	if (urb->status || context->type == CONTEXT_BEACON_PACKET) {
284 		if (context->skb)
285 			ieee80211_free_txskb(priv->hw, context->skb);
286 
287 		context->in_use = false;
288 	}
289 }
290 
vnt_tx_context(struct vnt_private * priv,struct vnt_usb_send_context * context)291 int vnt_tx_context(struct vnt_private *priv,
292 		   struct vnt_usb_send_context *context)
293 {
294 	int status;
295 	struct urb *urb = context->urb;
296 
297 	if (test_bit(DEVICE_FLAGS_DISCONNECTED, &priv->flags)) {
298 		context->in_use = false;
299 		return STATUS_RESOURCES;
300 	}
301 
302 	usb_fill_bulk_urb(urb,
303 			  priv->usb,
304 			  usb_sndbulkpipe(priv->usb, 3),
305 			  context->data,
306 			  context->buf_len,
307 			  vnt_tx_context_complete,
308 			  context);
309 
310 	status = usb_submit_urb(urb, GFP_ATOMIC);
311 	if (status) {
312 		dev_dbg(&priv->usb->dev, "Submit Tx URB failed %d\n", status);
313 
314 		context->in_use = false;
315 		return STATUS_FAILURE;
316 	}
317 
318 	return STATUS_PENDING;
319 }
320