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;
}