diff options
Diffstat (limited to 'drivers/staging/usbip/stub_dev.c')
-rw-r--r-- | drivers/staging/usbip/stub_dev.c | 101 |
1 files changed, 92 insertions, 9 deletions
diff --git a/drivers/staging/usbip/stub_dev.c b/drivers/staging/usbip/stub_dev.c index 3f95605427a..b6b753a4934 100644 --- a/drivers/staging/usbip/stub_dev.c +++ b/drivers/staging/usbip/stub_dev.c | |||
@@ -393,11 +393,14 @@ static int stub_probe(struct usb_interface *interface, | |||
393 | struct stub_device *sdev = NULL; | 393 | struct stub_device *sdev = NULL; |
394 | const char *udev_busid = dev_name(interface->dev.parent); | 394 | const char *udev_busid = dev_name(interface->dev.parent); |
395 | int err = 0; | 395 | int err = 0; |
396 | struct bus_id_priv *busid_priv; | ||
396 | 397 | ||
397 | dev_dbg(&interface->dev, "Enter\n"); | 398 | dev_dbg(&interface->dev, "Enter\n"); |
398 | 399 | ||
399 | /* check we should claim or not by busid_table */ | 400 | /* check we should claim or not by busid_table */ |
400 | if (match_busid(udev_busid)) { | 401 | busid_priv = get_busid_priv(udev_busid); |
402 | if (!busid_priv || (busid_priv->status == STUB_BUSID_REMOV) || | ||
403 | (busid_priv->status == STUB_BUSID_OTHER)) { | ||
401 | dev_info(&interface->dev, | 404 | dev_info(&interface->dev, |
402 | "this device %s is not in match_busid table. skip!\n", | 405 | "this device %s is not in match_busid table. skip!\n", |
403 | udev_busid); | 406 | udev_busid); |
@@ -422,28 +425,80 @@ static int stub_probe(struct usb_interface *interface, | |||
422 | return -ENODEV; | 425 | return -ENODEV; |
423 | } | 426 | } |
424 | 427 | ||
428 | |||
429 | if (busid_priv->status == STUB_BUSID_ALLOC) { | ||
430 | busid_priv->interf_count++; | ||
431 | sdev = busid_priv->sdev; | ||
432 | if (!sdev) | ||
433 | return -ENODEV; | ||
434 | |||
435 | dev_info(&interface->dev, | ||
436 | "USB/IP Stub: register a new interface " | ||
437 | "(bus %u dev %u ifn %u)\n", udev->bus->busnum, udev->devnum, | ||
438 | interface->cur_altsetting->desc.bInterfaceNumber); | ||
439 | |||
440 | /* set private data to usb_interface */ | ||
441 | usb_set_intfdata(interface, sdev); | ||
442 | |||
443 | err = stub_add_files(&interface->dev); | ||
444 | if (err) { | ||
445 | dev_err(&interface->dev, "create sysfs files for %s\n", | ||
446 | udev_busid); | ||
447 | usb_set_intfdata(interface, NULL); | ||
448 | busid_priv->interf_count--; | ||
449 | |||
450 | return err; | ||
451 | } | ||
452 | |||
453 | return 0; | ||
454 | } | ||
455 | |||
425 | /* ok. this is my device. */ | 456 | /* ok. this is my device. */ |
426 | sdev = stub_device_alloc(interface); | 457 | sdev = stub_device_alloc(interface); |
427 | if (!sdev) | 458 | if (!sdev) |
428 | return -ENOMEM; | 459 | return -ENOMEM; |
429 | 460 | ||
430 | dev_info(&interface->dev, "USB/IP Stub: register a new interface " | 461 | dev_info(&interface->dev, "USB/IP Stub: register a new device " |
431 | "(bus %u dev %u ifn %u)\n", udev->bus->busnum, udev->devnum, | 462 | "(bus %u dev %u ifn %u)\n", udev->bus->busnum, udev->devnum, |
432 | interface->cur_altsetting->desc.bInterfaceNumber); | 463 | interface->cur_altsetting->desc.bInterfaceNumber); |
433 | 464 | ||
465 | busid_priv->interf_count = 0; | ||
466 | busid_priv->shutdown_busid = 0; | ||
467 | |||
434 | /* set private data to usb_interface */ | 468 | /* set private data to usb_interface */ |
435 | usb_set_intfdata(interface, sdev); | 469 | usb_set_intfdata(interface, sdev); |
470 | busid_priv->interf_count++; | ||
471 | |||
472 | busid_priv->sdev = sdev; | ||
436 | 473 | ||
437 | err = stub_add_files(&interface->dev); | 474 | err = stub_add_files(&interface->dev); |
438 | if (err) { | 475 | if (err) { |
439 | dev_err(&interface->dev, "create sysfs files for %s\n", | 476 | dev_err(&interface->dev, "create sysfs files for %s\n", |
440 | udev_busid); | 477 | udev_busid); |
478 | usb_set_intfdata(interface, NULL); | ||
479 | busid_priv->interf_count = 0; | ||
480 | |||
481 | busid_priv->sdev = NULL; | ||
482 | stub_device_free(sdev); | ||
441 | return err; | 483 | return err; |
442 | } | 484 | } |
485 | busid_priv->status = STUB_BUSID_ALLOC; | ||
443 | 486 | ||
444 | return 0; | 487 | return 0; |
445 | } | 488 | } |
446 | 489 | ||
490 | static void shutdown_busid(struct bus_id_priv *busid_priv) | ||
491 | { | ||
492 | if (busid_priv->sdev && !busid_priv->shutdown_busid) { | ||
493 | busid_priv->shutdown_busid = 1; | ||
494 | usbip_event_add(&busid_priv->sdev->ud, SDEV_EVENT_REMOVED); | ||
495 | |||
496 | /* 2. wait for the stop of the event handler */ | ||
497 | usbip_stop_eh(&busid_priv->sdev->ud); | ||
498 | } | ||
499 | |||
500 | } | ||
501 | |||
447 | 502 | ||
448 | /* | 503 | /* |
449 | * called in usb_disconnect() or usb_deregister() | 504 | * called in usb_disconnect() or usb_deregister() |
@@ -451,10 +506,21 @@ static int stub_probe(struct usb_interface *interface, | |||
451 | */ | 506 | */ |
452 | static void stub_disconnect(struct usb_interface *interface) | 507 | static void stub_disconnect(struct usb_interface *interface) |
453 | { | 508 | { |
454 | struct stub_device *sdev = usb_get_intfdata(interface); | 509 | struct stub_device *sdev; |
510 | const char *udev_busid = dev_name(interface->dev.parent); | ||
511 | struct bus_id_priv *busid_priv; | ||
512 | |||
513 | busid_priv = get_busid_priv(udev_busid); | ||
455 | 514 | ||
456 | usbip_udbg("Enter\n"); | 515 | usbip_udbg("Enter\n"); |
457 | 516 | ||
517 | if (!busid_priv) { | ||
518 | BUG(); | ||
519 | return; | ||
520 | } | ||
521 | |||
522 | sdev = usb_get_intfdata(interface); | ||
523 | |||
458 | /* get stub_device */ | 524 | /* get stub_device */ |
459 | if (!sdev) { | 525 | if (!sdev) { |
460 | err(" could not get device from inteface data"); | 526 | err(" could not get device from inteface data"); |
@@ -464,22 +530,39 @@ static void stub_disconnect(struct usb_interface *interface) | |||
464 | 530 | ||
465 | usb_set_intfdata(interface, NULL); | 531 | usb_set_intfdata(interface, NULL); |
466 | 532 | ||
467 | |||
468 | /* | 533 | /* |
469 | * NOTE: | 534 | * NOTE: |
470 | * rx/tx threads are invoked for each usb_device. | 535 | * rx/tx threads are invoked for each usb_device. |
471 | */ | 536 | */ |
472 | stub_remove_files(&interface->dev); | 537 | stub_remove_files(&interface->dev); |
473 | 538 | ||
474 | /* 1. shutdown the current connection */ | 539 | /*If usb reset called from event handler*/ |
475 | usbip_event_add(&sdev->ud, SDEV_EVENT_REMOVED); | 540 | if (busid_priv->sdev->ud.eh.thread == current) { |
541 | busid_priv->interf_count--; | ||
542 | return; | ||
543 | } | ||
544 | |||
545 | if (busid_priv->interf_count > 1) { | ||
546 | busid_priv->interf_count--; | ||
547 | shutdown_busid(busid_priv); | ||
548 | return; | ||
549 | } | ||
550 | |||
551 | busid_priv->interf_count = 0; | ||
476 | 552 | ||
477 | /* 2. wait for the stop of the event handler */ | 553 | |
478 | usbip_stop_eh(&sdev->ud); | 554 | /* 1. shutdown the current connection */ |
555 | shutdown_busid(busid_priv); | ||
479 | 556 | ||
480 | /* 3. free sdev */ | 557 | /* 3. free sdev */ |
558 | busid_priv->sdev = NULL; | ||
481 | stub_device_free(sdev); | 559 | stub_device_free(sdev); |
482 | 560 | ||
483 | 561 | if (busid_priv->status == STUB_BUSID_ALLOC) { | |
562 | busid_priv->status = STUB_BUSID_ADDED; | ||
563 | } else { | ||
564 | busid_priv->status = STUB_BUSID_OTHER; | ||
565 | del_match_busid((char *)udev_busid); | ||
566 | } | ||
484 | usbip_udbg("bye\n"); | 567 | usbip_udbg("bye\n"); |
485 | } | 568 | } |