Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v5.3 319 lines 6.6 kB view raw
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 36int 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 65end_unlock: 66 mutex_unlock(&priv->usb_lock); 67end: 68 return ret; 69} 70 71int 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 77int 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 109end_unlock: 110 mutex_unlock(&priv->usb_lock); 111end: 112 return ret; 113} 114 115int 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 121static 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 154int 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 182err_submit: 183 priv->int_buf.in_use = false; 184err: 185 return ret; 186} 187 188static 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 229int 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 256end: 257 return ret; 258} 259 260static 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 291int 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}