Reap URB on disconnected device never returns

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hello,
I have found folowing bug in linux kernel (see also in http://bugzilla.kernel.org/show_bug.cgi?id=12817):

Distribution:Slackware 12.2 Kernel: 2.6.27.7 Problem Description and steps to reproduce:
1. Submit urb to read from interrupt (or bulk) endpoint from USB device
2. call ioctl to reap this urb (USBDEVS_REAPURB), there are no other requests pending for this device (fd)
3. the thread is blocked in the kernel (which is OK)
4. disconnect the USB device
5. the thread is blocked in the kernel for ever (!)
6. try to discard urb (USBDEVS_DISCARDURB)from another thread, this fails
(probably because the fd is no longer valid), but this doesn't unblock the
first thread (!)
I am attaching simple test program (modified testlibusb.c from libusb-0.1.12 to demonstrate this).
To compile: gcc -o test test.c -lusb
To run: ./test <VID> <PID>, where VID and PID are hex numbers (e.g. ./test 1234 90ab)
You need to find some USB hardware, which is NOT drive by dedicated driver (so you cannot test this by USB mouse or flash drive,
because as early as submit urb fails by error EBUSY )

Martin Poupe


/*
 * test.c
 *
  */

#include <stdio.h>
#include <string.h>
#include <usb.h>
#include <sys/ioctl.h>
#include <errno.h>

//internal defines and types from libusb-0.1.12
#define IOCTL_USB_CONTROL	_IOWR('U', 0, struct usb_ctrltransfer)
#define IOCTL_USB_BULK		_IOWR('U', 2, struct usb_bulktransfer)
#define IOCTL_USB_RESETEP	_IOR('U', 3, unsigned int)
#define IOCTL_USB_SETINTF	_IOR('U', 4, struct usb_setinterface)
#define IOCTL_USB_SETCONFIG	_IOR('U', 5, unsigned int)
#define IOCTL_USB_GETDRIVER	_IOW('U', 8, struct usb_getdriver)
#define IOCTL_USB_SUBMITURB	_IOR('U', 10, struct usb_urb)
#define IOCTL_USB_DISCARDURB	_IO('U', 11)
#define IOCTL_USB_REAPURB	_IOW('U', 12, void *)
#define IOCTL_USB_REAPURBNDELAY	_IOW('U', 13, void *)
#define IOCTL_USB_CLAIMINTF	_IOR('U', 15, unsigned int)
#define IOCTL_USB_RELEASEINTF	_IOR('U', 16, unsigned int)
#define IOCTL_USB_CONNECTINFO	_IOW('U', 17, struct usb_connectinfo)
#define IOCTL_USB_IOCTL         _IOWR('U', 18, struct usb_ioctl)
#define IOCTL_USB_HUB_PORTINFO	_IOR('U', 19, struct usb_hub_portinfo)
#define IOCTL_USB_RESET		_IO('U', 20)
#define IOCTL_USB_CLEAR_HALT	_IOR('U', 21, unsigned int)
#define IOCTL_USB_DISCONNECT	_IO('U', 22)
#define IOCTL_USB_CONNECT	_IO('U', 23)

#define USB_URB_DISABLE_SPD	1
#define USB_URB_ISO_ASAP	2
#define USB_URB_QUEUE_BULK	0x10

#define USB_URB_TYPE_ISO	0
#define USB_URB_TYPE_INTERRUPT	1
#define USB_URB_TYPE_CONTROL	2
#define USB_URB_TYPE_BULK	3


struct usb_urb {
    unsigned char type;
    unsigned char endpoint;
    int status;
    unsigned int flags;
    void *buffer;
    int buffer_length;
    int actual_length;
    int start_frame;
    int number_of_packets;
    int error_count;
    unsigned int signr;  /* signal to be sent on error, -1 if none should be sent */
    void *usercontext;
    //struct usb_iso_packet_desc iso_frame_desc[0];
};

struct usb_dev_handle {
    int fd;

    struct usb_bus *bus;
    struct usb_device *device;

    int config;
    int interface;
    int altsetting;

    /* Added by RMT so implementations can store other per-open-device data */
    void *impl_info;
};


//////////////////////////////////////////////////////////////////////////
char bEndpointAddress = 0;
char bType = 0;
void print_endpoint(struct usb_endpoint_descriptor *endpoint)
{
  printf("      bEndpointAddress: %02xh\n", endpoint->bEndpointAddress);
  printf("      bmAttributes:     %02xh\n", endpoint->bmAttributes);
  if(endpoint->bEndpointAddress & USB_ENDPOINT_IN)
  {
    switch(endpoint->bmAttributes & USB_ENDPOINT_TYPE_MASK)
    {
    case USB_ENDPOINT_TYPE_BULK:
        if(bType == 0)
        {
            bEndpointAddress = endpoint->bEndpointAddress;
            bType = USB_URB_TYPE_BULK;// USB_URB_TYPE_INTERRUPT
        }
        break;
    case USB_ENDPOINT_TYPE_INTERRUPT:
        if(bType != USB_URB_TYPE_INTERRUPT)
        {
            bEndpointAddress = endpoint->bEndpointAddress;
            bType = USB_URB_TYPE_INTERRUPT;
        }
        break;
    default:
        /* nothing */
        break;
    }
  }
  printf("      wMaxPacketSize:   %d\n", endpoint->wMaxPacketSize);
  printf("      bInterval:        %d\n", endpoint->bInterval);
  printf("      bRefresh:         %d\n", endpoint->bRefresh);
  printf("      bSynchAddress:    %d\n", endpoint->bSynchAddress);
}

void print_altsetting(struct usb_interface_descriptor *interface)
{
  int i;

  printf("    bInterfaceNumber:   %d\n", interface->bInterfaceNumber);
  printf("    bAlternateSetting:  %d\n", interface->bAlternateSetting);
  printf("    bNumEndpoints:      %d\n", interface->bNumEndpoints);
  printf("    bInterfaceClass:    %d\n", interface->bInterfaceClass);
  printf("    bInterfaceSubClass: %d\n", interface->bInterfaceSubClass);
  printf("    bInterfaceProtocol: %d\n", interface->bInterfaceProtocol);
  printf("    iInterface:         %d\n", interface->iInterface);

  for (i = 0; i < interface->bNumEndpoints; i++)
    print_endpoint(&interface->endpoint[i]);
}

void print_interface(struct usb_interface *interface)
{
  int i;

  for (i = 0; i < interface->num_altsetting; i++)
    print_altsetting(&interface->altsetting[i]);
}

void print_configuration(struct usb_config_descriptor *config)
{
  int i;

  printf("  wTotalLength:         %d\n", config->wTotalLength);
  printf("  bNumInterfaces:       %d\n", config->bNumInterfaces);
  printf("  bConfigurationValue:  %d\n", config->bConfigurationValue);
  printf("  iConfiguration:       %d\n", config->iConfiguration);
  printf("  bmAttributes:         %02xh\n", config->bmAttributes);
  printf("  MaxPower:             %d\n", config->MaxPower);

  for (i = 0; i < config->bNumInterfaces; i++)
    print_interface(&config->interface[i]);
}

int TryDevice(struct usb_device *dev)
{
  usb_dev_handle *udev;
  char description[256];
  char string[256];
  int ret, i;

  udev = usb_open(dev);
  if (udev) 
  {
    if (dev->descriptor.iManufacturer) 
    {
      ret = usb_get_string_simple(udev, dev->descriptor.iManufacturer, string, sizeof(string));
      if (ret > 0)
        snprintf(description, sizeof(description), "%s - ", string);
      else
        snprintf(description, sizeof(description), "%04X - ",
                 dev->descriptor.idVendor);
    } else
      snprintf(description, sizeof(description), "%04X - ",
               dev->descriptor.idVendor);

    if (dev->descriptor.iProduct) 
    {
      ret = usb_get_string_simple(udev, dev->descriptor.iProduct, string, sizeof(string));
      if (ret > 0)
        snprintf(description + strlen(description), sizeof(description) -
                 strlen(description), "%s", string);
      else
        snprintf(description + strlen(description), sizeof(description) -
                 strlen(description), "%04X", dev->descriptor.idProduct);
    } else
      snprintf(description + strlen(description), sizeof(description) -
               strlen(description), "%04X", dev->descriptor.idProduct);

  } else
    snprintf(description, sizeof(description), "%04X - %04X",
             dev->descriptor.idVendor, dev->descriptor.idProduct);

  printf("Dev #%d: %s\n",dev->devnum,description);

  if (udev)
  {
    if (dev->descriptor.iSerialNumber) 
    {
      ret = usb_get_string_simple(udev, dev->descriptor.iSerialNumber, string, sizeof(string));
      if (ret > 0)
        printf("Serial Number: %s\n",string);
    }
  }


  if (dev->config) 
  {
      print_configuration(&dev->config[0]);
      if(udev && bEndpointAddress && bType)
      {
          struct usb_urb aUrb = {0};  
          struct usb_urb *pUrb;  
          char cBuffer[4];
          int ret;
          int fd = udev->fd;
          printf("Trying to demonstrate problem with endpoint 0x%02x, of type %i\n",(unsigned char)bEndpointAddress,bType);
          printf("Please disconnect the device after a short while \n");
          while(1)
          {
              aUrb.type = bType;
              aUrb.endpoint = bEndpointAddress;
              aUrb.flags = 0;
              aUrb.buffer = cBuffer;
              aUrb.buffer_length = 4;
              aUrb.signr = 0;
              aUrb.actual_length = 0;
              aUrb.number_of_packets = 0;	/* don't do isochronous yet */
              aUrb.usercontext = NULL;
                    
              ret = ioctl(fd, IOCTL_USB_SUBMITURB,&aUrb);
              if(ret == 0)
              {
                ret = ioctl(fd, IOCTL_USB_REAPURB, &pUrb);
                if(ret)
                {
                    printf("IOCTL_USB_REAPURB -> %i, errno = %i\n",ret,errno);
                }
                else
                {
                    printf(".");
                }
                continue;

              }
              else
              {
                    printf("IOCTL_USB_SUBMITURB -> %i, errno = %i\n",ret,errno);
                    break;
              }
          }

      }
  }
  else
  {
      printf("  Couldn't retrieve descriptors\n");
  }
  if (udev)
      usb_close(udev);
   

  return 0;
}

int main(int argc, char *argv[])
{
  struct usb_bus *bus;
  int iVid,iPid;

  if (argc != 3)
  {
      printf("Please enter VID and PID as a parameters\n");
      return 1;
  }
  iVid = strtol(argv[1],(char **)NULL,16);
  iPid = strtol(argv[2],(char **)NULL,16);
  if((iVid == 0)||(iPid == 0))
  {
      printf("Please enter VID and PID as a number parameters\n");
      return 2;
  }

  usb_init();

  usb_find_busses();
  usb_find_devices();

  if(usb_busses == NULL)
  {
      printf("Sorry there is no USB controller\n");
      return 3;
  } 
  for (bus = usb_busses; bus; bus = bus->next) 
  {
      struct usb_device *dev;
      if(bus->devices)
      {
          for (dev = bus->devices; dev; dev = dev->next)
          {
              if((iVid == dev->descriptor.idVendor)&&(iPid == dev->descriptor.idProduct))
              {
                  TryDevice(dev);
                  return 0;
              }
          }
      }
  }
  printf("Sorry cannot find device specified\n");
  return 0;
}


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux