| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /**************************************************************** |
|---|
| 2 | 3 | * |
|---|
| 3 | 4 | * kaweth.c - driver for KL5KUSB101 based USB->Ethernet |
|---|
| .. | .. |
|---|
| 13 | 14 | * Based off of (and with thanks to) Petko Manolov's pegaus.c driver. |
|---|
| 14 | 15 | * Also many thanks to Joel Silverman and Ed Surprenant at Kawasaki |
|---|
| 15 | 16 | * for providing the firmware and driver resources. |
|---|
| 16 | | - * |
|---|
| 17 | | - * This program is free software; you can redistribute it and/or |
|---|
| 18 | | - * modify it under the terms of the GNU General Public License as |
|---|
| 19 | | - * published by the Free Software Foundation; either version 2, or |
|---|
| 20 | | - * (at your option) any later version. |
|---|
| 21 | | - * |
|---|
| 22 | | - * This program is distributed in the hope that it will be useful, |
|---|
| 23 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 24 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 25 | | - * GNU General Public License for more details. |
|---|
| 26 | | - * |
|---|
| 27 | | - * You should have received a copy of the GNU General Public License |
|---|
| 28 | | - * along with this program; if not, see <http://www.gnu.org/licenses/>. |
|---|
| 29 | 17 | * |
|---|
| 30 | 18 | ****************************************************************/ |
|---|
| 31 | 19 | |
|---|
| .. | .. |
|---|
| 115 | 103 | const struct usb_device_id *id /* from id_table */ |
|---|
| 116 | 104 | ); |
|---|
| 117 | 105 | static void kaweth_disconnect(struct usb_interface *intf); |
|---|
| 118 | | -static int kaweth_internal_control_msg(struct usb_device *usb_dev, |
|---|
| 119 | | - unsigned int pipe, |
|---|
| 120 | | - struct usb_ctrlrequest *cmd, void *data, |
|---|
| 121 | | - int len, int timeout); |
|---|
| 122 | 106 | static int kaweth_suspend(struct usb_interface *intf, pm_message_t message); |
|---|
| 123 | 107 | static int kaweth_resume(struct usb_interface *intf); |
|---|
| 124 | 108 | |
|---|
| .. | .. |
|---|
| 248 | 232 | }; |
|---|
| 249 | 233 | |
|---|
| 250 | 234 | /**************************************************************** |
|---|
| 251 | | - * kaweth_control |
|---|
| 252 | | - ****************************************************************/ |
|---|
| 253 | | -static int kaweth_control(struct kaweth_device *kaweth, |
|---|
| 254 | | - unsigned int pipe, |
|---|
| 255 | | - __u8 request, |
|---|
| 256 | | - __u8 requesttype, |
|---|
| 257 | | - __u16 value, |
|---|
| 258 | | - __u16 index, |
|---|
| 259 | | - void *data, |
|---|
| 260 | | - __u16 size, |
|---|
| 261 | | - int timeout) |
|---|
| 262 | | -{ |
|---|
| 263 | | - struct usb_ctrlrequest *dr; |
|---|
| 264 | | - int retval; |
|---|
| 265 | | - |
|---|
| 266 | | - if(in_interrupt()) { |
|---|
| 267 | | - netdev_dbg(kaweth->net, "in_interrupt()\n"); |
|---|
| 268 | | - return -EBUSY; |
|---|
| 269 | | - } |
|---|
| 270 | | - |
|---|
| 271 | | - dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC); |
|---|
| 272 | | - if (!dr) |
|---|
| 273 | | - return -ENOMEM; |
|---|
| 274 | | - |
|---|
| 275 | | - dr->bRequestType = requesttype; |
|---|
| 276 | | - dr->bRequest = request; |
|---|
| 277 | | - dr->wValue = cpu_to_le16(value); |
|---|
| 278 | | - dr->wIndex = cpu_to_le16(index); |
|---|
| 279 | | - dr->wLength = cpu_to_le16(size); |
|---|
| 280 | | - |
|---|
| 281 | | - retval = kaweth_internal_control_msg(kaweth->dev, |
|---|
| 282 | | - pipe, |
|---|
| 283 | | - dr, |
|---|
| 284 | | - data, |
|---|
| 285 | | - size, |
|---|
| 286 | | - timeout); |
|---|
| 287 | | - |
|---|
| 288 | | - kfree(dr); |
|---|
| 289 | | - return retval; |
|---|
| 290 | | -} |
|---|
| 291 | | - |
|---|
| 292 | | -/**************************************************************** |
|---|
| 293 | 235 | * kaweth_read_configuration |
|---|
| 294 | 236 | ****************************************************************/ |
|---|
| 295 | 237 | static int kaweth_read_configuration(struct kaweth_device *kaweth) |
|---|
| 296 | 238 | { |
|---|
| 297 | | - int retval; |
|---|
| 298 | | - |
|---|
| 299 | | - retval = kaweth_control(kaweth, |
|---|
| 300 | | - usb_rcvctrlpipe(kaweth->dev, 0), |
|---|
| 239 | + return usb_control_msg(kaweth->dev, usb_rcvctrlpipe(kaweth->dev, 0), |
|---|
| 301 | 240 | KAWETH_COMMAND_GET_ETHERNET_DESC, |
|---|
| 302 | 241 | USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE, |
|---|
| 303 | | - 0, |
|---|
| 304 | | - 0, |
|---|
| 305 | | - (void *)&kaweth->configuration, |
|---|
| 242 | + 0, 0, |
|---|
| 243 | + &kaweth->configuration, |
|---|
| 306 | 244 | sizeof(kaweth->configuration), |
|---|
| 307 | 245 | KAWETH_CONTROL_TIMEOUT); |
|---|
| 308 | | - |
|---|
| 309 | | - return retval; |
|---|
| 310 | 246 | } |
|---|
| 311 | 247 | |
|---|
| 312 | 248 | /**************************************************************** |
|---|
| .. | .. |
|---|
| 314 | 250 | ****************************************************************/ |
|---|
| 315 | 251 | static int kaweth_set_urb_size(struct kaweth_device *kaweth, __u16 urb_size) |
|---|
| 316 | 252 | { |
|---|
| 317 | | - int retval; |
|---|
| 318 | | - |
|---|
| 319 | 253 | netdev_dbg(kaweth->net, "Setting URB size to %d\n", (unsigned)urb_size); |
|---|
| 320 | 254 | |
|---|
| 321 | | - retval = kaweth_control(kaweth, |
|---|
| 322 | | - usb_sndctrlpipe(kaweth->dev, 0), |
|---|
| 323 | | - KAWETH_COMMAND_SET_URB_SIZE, |
|---|
| 324 | | - USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, |
|---|
| 325 | | - urb_size, |
|---|
| 326 | | - 0, |
|---|
| 327 | | - (void *)&kaweth->scratch, |
|---|
| 328 | | - 0, |
|---|
| 329 | | - KAWETH_CONTROL_TIMEOUT); |
|---|
| 330 | | - |
|---|
| 331 | | - return retval; |
|---|
| 255 | + return usb_control_msg(kaweth->dev, usb_sndctrlpipe(kaweth->dev, 0), |
|---|
| 256 | + KAWETH_COMMAND_SET_URB_SIZE, |
|---|
| 257 | + USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, |
|---|
| 258 | + urb_size, 0, |
|---|
| 259 | + &kaweth->scratch, 0, |
|---|
| 260 | + KAWETH_CONTROL_TIMEOUT); |
|---|
| 332 | 261 | } |
|---|
| 333 | 262 | |
|---|
| 334 | 263 | /**************************************************************** |
|---|
| .. | .. |
|---|
| 336 | 265 | ****************************************************************/ |
|---|
| 337 | 266 | static int kaweth_set_sofs_wait(struct kaweth_device *kaweth, __u16 sofs_wait) |
|---|
| 338 | 267 | { |
|---|
| 339 | | - int retval; |
|---|
| 340 | | - |
|---|
| 341 | 268 | netdev_dbg(kaweth->net, "Set SOFS wait to %d\n", (unsigned)sofs_wait); |
|---|
| 342 | 269 | |
|---|
| 343 | | - retval = kaweth_control(kaweth, |
|---|
| 344 | | - usb_sndctrlpipe(kaweth->dev, 0), |
|---|
| 345 | | - KAWETH_COMMAND_SET_SOFS_WAIT, |
|---|
| 346 | | - USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, |
|---|
| 347 | | - sofs_wait, |
|---|
| 348 | | - 0, |
|---|
| 349 | | - (void *)&kaweth->scratch, |
|---|
| 350 | | - 0, |
|---|
| 351 | | - KAWETH_CONTROL_TIMEOUT); |
|---|
| 352 | | - |
|---|
| 353 | | - return retval; |
|---|
| 270 | + return usb_control_msg(kaweth->dev, usb_sndctrlpipe(kaweth->dev, 0), |
|---|
| 271 | + KAWETH_COMMAND_SET_SOFS_WAIT, |
|---|
| 272 | + USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, |
|---|
| 273 | + sofs_wait, 0, |
|---|
| 274 | + &kaweth->scratch, 0, |
|---|
| 275 | + KAWETH_CONTROL_TIMEOUT); |
|---|
| 354 | 276 | } |
|---|
| 355 | 277 | |
|---|
| 356 | 278 | /**************************************************************** |
|---|
| .. | .. |
|---|
| 359 | 281 | static int kaweth_set_receive_filter(struct kaweth_device *kaweth, |
|---|
| 360 | 282 | __u16 receive_filter) |
|---|
| 361 | 283 | { |
|---|
| 362 | | - int retval; |
|---|
| 363 | | - |
|---|
| 364 | 284 | netdev_dbg(kaweth->net, "Set receive filter to %d\n", |
|---|
| 365 | 285 | (unsigned)receive_filter); |
|---|
| 366 | 286 | |
|---|
| 367 | | - retval = kaweth_control(kaweth, |
|---|
| 368 | | - usb_sndctrlpipe(kaweth->dev, 0), |
|---|
| 369 | | - KAWETH_COMMAND_SET_PACKET_FILTER, |
|---|
| 370 | | - USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, |
|---|
| 371 | | - receive_filter, |
|---|
| 372 | | - 0, |
|---|
| 373 | | - (void *)&kaweth->scratch, |
|---|
| 374 | | - 0, |
|---|
| 375 | | - KAWETH_CONTROL_TIMEOUT); |
|---|
| 376 | | - |
|---|
| 377 | | - return retval; |
|---|
| 287 | + return usb_control_msg(kaweth->dev, usb_sndctrlpipe(kaweth->dev, 0), |
|---|
| 288 | + KAWETH_COMMAND_SET_PACKET_FILTER, |
|---|
| 289 | + USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, |
|---|
| 290 | + receive_filter, 0, |
|---|
| 291 | + &kaweth->scratch, 0, |
|---|
| 292 | + KAWETH_CONTROL_TIMEOUT); |
|---|
| 378 | 293 | } |
|---|
| 379 | 294 | |
|---|
| 380 | 295 | /**************************************************************** |
|---|
| .. | .. |
|---|
| 419 | 334 | kaweth->firmware_buf, kaweth); |
|---|
| 420 | 335 | netdev_dbg(kaweth->net, "Firmware length: %d\n", data_len); |
|---|
| 421 | 336 | |
|---|
| 422 | | - return kaweth_control(kaweth, |
|---|
| 423 | | - usb_sndctrlpipe(kaweth->dev, 0), |
|---|
| 337 | + return usb_control_msg(kaweth->dev, usb_sndctrlpipe(kaweth->dev, 0), |
|---|
| 424 | 338 | KAWETH_COMMAND_SCAN, |
|---|
| 425 | 339 | USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, |
|---|
| 426 | | - 0, |
|---|
| 427 | | - 0, |
|---|
| 428 | | - (void *)kaweth->firmware_buf, |
|---|
| 429 | | - data_len, |
|---|
| 340 | + 0, 0, |
|---|
| 341 | + kaweth->firmware_buf, data_len, |
|---|
| 430 | 342 | KAWETH_CONTROL_TIMEOUT); |
|---|
| 431 | 343 | } |
|---|
| 432 | 344 | |
|---|
| .. | .. |
|---|
| 445 | 357 | kaweth->firmware_buf[6] = 0x00; |
|---|
| 446 | 358 | kaweth->firmware_buf[7] = 0x00; |
|---|
| 447 | 359 | |
|---|
| 448 | | - return kaweth_control(kaweth, |
|---|
| 449 | | - usb_sndctrlpipe(kaweth->dev, 0), |
|---|
| 450 | | - KAWETH_COMMAND_SCAN, |
|---|
| 451 | | - USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, |
|---|
| 452 | | - 0, |
|---|
| 453 | | - 0, |
|---|
| 454 | | - (void *)kaweth->firmware_buf, |
|---|
| 455 | | - 8, |
|---|
| 456 | | - KAWETH_CONTROL_TIMEOUT); |
|---|
| 360 | + return usb_control_msg(kaweth->dev, usb_sndctrlpipe(kaweth->dev, 0), |
|---|
| 361 | + KAWETH_COMMAND_SCAN, |
|---|
| 362 | + USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, |
|---|
| 363 | + 0, 0, |
|---|
| 364 | + (void *)kaweth->firmware_buf, 8, |
|---|
| 365 | + KAWETH_CONTROL_TIMEOUT); |
|---|
| 457 | 366 | } |
|---|
| 458 | 367 | |
|---|
| 459 | 368 | /**************************************************************** |
|---|
| .. | .. |
|---|
| 576 | 485 | return result; |
|---|
| 577 | 486 | } |
|---|
| 578 | 487 | |
|---|
| 579 | | -static void kaweth_async_set_rx_mode(struct kaweth_device *kaweth); |
|---|
| 488 | +static void kaweth_async_set_rx_mode(struct kaweth_device *kaweth, |
|---|
| 489 | + bool may_sleep); |
|---|
| 580 | 490 | |
|---|
| 581 | 491 | /**************************************************************** |
|---|
| 582 | 492 | * kaweth_usb_receive |
|---|
| .. | .. |
|---|
| 706 | 616 | |
|---|
| 707 | 617 | netif_start_queue(net); |
|---|
| 708 | 618 | |
|---|
| 709 | | - kaweth_async_set_rx_mode(kaweth); |
|---|
| 619 | + kaweth_async_set_rx_mode(kaweth, true); |
|---|
| 710 | 620 | return 0; |
|---|
| 711 | 621 | |
|---|
| 712 | 622 | err_out: |
|---|
| .. | .. |
|---|
| 794 | 704 | |
|---|
| 795 | 705 | spin_lock_irq(&kaweth->device_lock); |
|---|
| 796 | 706 | |
|---|
| 797 | | - kaweth_async_set_rx_mode(kaweth); |
|---|
| 707 | + kaweth_async_set_rx_mode(kaweth, false); |
|---|
| 798 | 708 | netif_stop_queue(net); |
|---|
| 799 | 709 | if (IS_BLOCKED(kaweth->status)) { |
|---|
| 800 | 710 | goto skip; |
|---|
| .. | .. |
|---|
| 871 | 781 | /**************************************************************** |
|---|
| 872 | 782 | * kaweth_async_set_rx_mode |
|---|
| 873 | 783 | ****************************************************************/ |
|---|
| 874 | | -static void kaweth_async_set_rx_mode(struct kaweth_device *kaweth) |
|---|
| 784 | +static void kaweth_async_set_rx_mode(struct kaweth_device *kaweth, |
|---|
| 785 | + bool may_sleep) |
|---|
| 875 | 786 | { |
|---|
| 876 | | - int result; |
|---|
| 787 | + int ret; |
|---|
| 877 | 788 | __u16 packet_filter_bitmap = kaweth->packet_filter_bitmap; |
|---|
| 878 | 789 | |
|---|
| 879 | 790 | kaweth->packet_filter_bitmap = 0; |
|---|
| 880 | 791 | if (packet_filter_bitmap == 0) |
|---|
| 881 | 792 | return; |
|---|
| 882 | 793 | |
|---|
| 883 | | - if (in_interrupt()) |
|---|
| 794 | + if (!may_sleep) |
|---|
| 884 | 795 | return; |
|---|
| 885 | 796 | |
|---|
| 886 | | - result = kaweth_control(kaweth, |
|---|
| 887 | | - usb_sndctrlpipe(kaweth->dev, 0), |
|---|
| 888 | | - KAWETH_COMMAND_SET_PACKET_FILTER, |
|---|
| 889 | | - USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, |
|---|
| 890 | | - packet_filter_bitmap, |
|---|
| 891 | | - 0, |
|---|
| 892 | | - (void *)&kaweth->scratch, |
|---|
| 893 | | - 0, |
|---|
| 894 | | - KAWETH_CONTROL_TIMEOUT); |
|---|
| 895 | | - |
|---|
| 896 | | - if(result < 0) { |
|---|
| 797 | + ret = usb_control_msg(kaweth->dev, usb_sndctrlpipe(kaweth->dev, 0), |
|---|
| 798 | + KAWETH_COMMAND_SET_PACKET_FILTER, |
|---|
| 799 | + USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, |
|---|
| 800 | + packet_filter_bitmap, 0, |
|---|
| 801 | + &kaweth->scratch, 0, |
|---|
| 802 | + KAWETH_CONTROL_TIMEOUT); |
|---|
| 803 | + if (ret < 0) |
|---|
| 897 | 804 | dev_err(&kaweth->intf->dev, "Failed to set Rx mode: %d\n", |
|---|
| 898 | | - result); |
|---|
| 899 | | - } |
|---|
| 900 | | - else { |
|---|
| 805 | + ret); |
|---|
| 806 | + else |
|---|
| 901 | 807 | netdev_dbg(kaweth->net, "Set Rx mode to %d\n", |
|---|
| 902 | 808 | packet_filter_bitmap); |
|---|
| 903 | | - } |
|---|
| 904 | 809 | } |
|---|
| 905 | 810 | |
|---|
| 906 | 811 | /**************************************************************** |
|---|
| 907 | 812 | * kaweth_tx_timeout |
|---|
| 908 | 813 | ****************************************************************/ |
|---|
| 909 | | -static void kaweth_tx_timeout(struct net_device *net) |
|---|
| 814 | +static void kaweth_tx_timeout(struct net_device *net, unsigned int txqueue) |
|---|
| 910 | 815 | { |
|---|
| 911 | 816 | struct kaweth_device *kaweth = netdev_priv(net); |
|---|
| 912 | 817 | |
|---|
| .. | .. |
|---|
| 1207 | 1112 | free_netdev(netdev); |
|---|
| 1208 | 1113 | } |
|---|
| 1209 | 1114 | |
|---|
| 1210 | | - |
|---|
| 1211 | | -// FIXME this completion stuff is a modified clone of |
|---|
| 1212 | | -// an OLD version of some stuff in usb.c ... |
|---|
| 1213 | | -struct usb_api_data { |
|---|
| 1214 | | - wait_queue_head_t wqh; |
|---|
| 1215 | | - int done; |
|---|
| 1216 | | -}; |
|---|
| 1217 | | - |
|---|
| 1218 | | -/*-------------------------------------------------------------------* |
|---|
| 1219 | | - * completion handler for compatibility wrappers (sync control/bulk) * |
|---|
| 1220 | | - *-------------------------------------------------------------------*/ |
|---|
| 1221 | | -static void usb_api_blocking_completion(struct urb *urb) |
|---|
| 1222 | | -{ |
|---|
| 1223 | | - struct usb_api_data *awd = (struct usb_api_data *)urb->context; |
|---|
| 1224 | | - |
|---|
| 1225 | | - awd->done=1; |
|---|
| 1226 | | - wake_up(&awd->wqh); |
|---|
| 1227 | | -} |
|---|
| 1228 | | - |
|---|
| 1229 | | -/*-------------------------------------------------------------------* |
|---|
| 1230 | | - * COMPATIBILITY STUFF * |
|---|
| 1231 | | - *-------------------------------------------------------------------*/ |
|---|
| 1232 | | - |
|---|
| 1233 | | -// Starts urb and waits for completion or timeout |
|---|
| 1234 | | -static int usb_start_wait_urb(struct urb *urb, int timeout, int* actual_length) |
|---|
| 1235 | | -{ |
|---|
| 1236 | | - struct usb_api_data awd; |
|---|
| 1237 | | - int status; |
|---|
| 1238 | | - |
|---|
| 1239 | | - init_waitqueue_head(&awd.wqh); |
|---|
| 1240 | | - awd.done = 0; |
|---|
| 1241 | | - |
|---|
| 1242 | | - urb->context = &awd; |
|---|
| 1243 | | - status = usb_submit_urb(urb, GFP_ATOMIC); |
|---|
| 1244 | | - if (status) { |
|---|
| 1245 | | - // something went wrong |
|---|
| 1246 | | - usb_free_urb(urb); |
|---|
| 1247 | | - return status; |
|---|
| 1248 | | - } |
|---|
| 1249 | | - |
|---|
| 1250 | | - if (!wait_event_timeout(awd.wqh, awd.done, timeout)) { |
|---|
| 1251 | | - // timeout |
|---|
| 1252 | | - dev_warn(&urb->dev->dev, "usb_control/bulk_msg: timeout\n"); |
|---|
| 1253 | | - usb_kill_urb(urb); // remove urb safely |
|---|
| 1254 | | - status = -ETIMEDOUT; |
|---|
| 1255 | | - } |
|---|
| 1256 | | - else { |
|---|
| 1257 | | - status = urb->status; |
|---|
| 1258 | | - } |
|---|
| 1259 | | - |
|---|
| 1260 | | - if (actual_length) { |
|---|
| 1261 | | - *actual_length = urb->actual_length; |
|---|
| 1262 | | - } |
|---|
| 1263 | | - |
|---|
| 1264 | | - usb_free_urb(urb); |
|---|
| 1265 | | - return status; |
|---|
| 1266 | | -} |
|---|
| 1267 | | - |
|---|
| 1268 | | -/*-------------------------------------------------------------------*/ |
|---|
| 1269 | | -// returns status (negative) or length (positive) |
|---|
| 1270 | | -static int kaweth_internal_control_msg(struct usb_device *usb_dev, |
|---|
| 1271 | | - unsigned int pipe, |
|---|
| 1272 | | - struct usb_ctrlrequest *cmd, void *data, |
|---|
| 1273 | | - int len, int timeout) |
|---|
| 1274 | | -{ |
|---|
| 1275 | | - struct urb *urb; |
|---|
| 1276 | | - int retv; |
|---|
| 1277 | | - int length = 0; /* shut up GCC */ |
|---|
| 1278 | | - |
|---|
| 1279 | | - urb = usb_alloc_urb(0, GFP_ATOMIC); |
|---|
| 1280 | | - if (!urb) |
|---|
| 1281 | | - return -ENOMEM; |
|---|
| 1282 | | - |
|---|
| 1283 | | - usb_fill_control_urb(urb, usb_dev, pipe, (unsigned char*)cmd, data, |
|---|
| 1284 | | - len, usb_api_blocking_completion, NULL); |
|---|
| 1285 | | - |
|---|
| 1286 | | - retv = usb_start_wait_urb(urb, timeout, &length); |
|---|
| 1287 | | - if (retv < 0) { |
|---|
| 1288 | | - return retv; |
|---|
| 1289 | | - } |
|---|
| 1290 | | - else { |
|---|
| 1291 | | - return length; |
|---|
| 1292 | | - } |
|---|
| 1293 | | -} |
|---|
| 1294 | 1115 | |
|---|
| 1295 | 1116 | module_usb_driver(kaweth_driver); |
|---|