Re: gadgetfs isn't sending interrupt messages

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

 



On Sun, 5 Jul 2009, Adam Nielsen wrote:

> >> In this particular case I know that VirtualBox isn't to blame, because if I
> >> plug in the real hardware device then it works perfectly (this is how I was
> >> able to initially reverse engineer the communication protocol.)  It's only my
> >> gadgetfs device that isn't communicating properly.
> > 
> > It may be that the Windows app behaves differently because of the 
> > differences between your gadget and the real thing.  Have you compared 
> > usbmon traces for the two cases?
> 
> Okay, I've been through the whole HID initialisation sequence (which resulted
> in some new dissector code for Wireshark) and here are the only differences I
> can see:
> 
>  - In the device descriptor:
>     - Hardware is USB1.1, gadgetfs is USB2.0
>     - Hardware bcdDevice is 0x101, gadgetfs overrides this as 0x100

I don't see anything in the gadgetfs source code to override bcdDevice.  
Are you sure it happens?

>     - Hardware bMaxPacketSize0 is 8, gadgetfs overrides this as 64
>  - In the config descriptor:
>     - Hardware bmAttributes is bus powered, remote wakeup.  Gadgetfs device
> won't be detected under Linux unless it's listed as self-powered, no remote
> wakeup.

I don't understand that.  What do you mean, it won't be detected?  What 
will happen?

>  - After returning the HID descriptor, the host sends the hardware an empty
> write on its interrupt output endpoint.  This message never appears in the
> usbmon trace for the gadgetfs driver. (But only under win32, the interrupt
> does appear when Linux is accessing the device.)

This is probably the result of a bug in usbfs (and possibly a bug in 
libusb as well); 0-length transfers aren't accepted.

> I think the last point is the most significant - interrupt messages don't seem
> to be passed through regardless of which direction they're in.

Are you sure the problem is interrupt transfers and not 0-length 
transfers?

>  I'm not sure
> why the host is trying to write zero bytes to the read-only interrupt
> endpoint, but my gadgetfs code gets an error if I try to read from the
> interrupt endpoint (which is normally used for writing data out to the host.)

Above you wrote that the host sends a 0-length transfer to the 
"interrupt output endpoint".  Here you say the "read-only interrupt 
endpoint".  Clearly "output" != "read-only".  Which is it?


> > "arrives on an interrupt endpoint" is a little misleading.  The data 
> > may be sitting in the device's buffer for that endpoint, but it won't 
> > arrive at the host until the host asks for it.
> 
> So in the case of a keyboard sending keypresses over an interrupt endpoint,
> how does the host know when keys are pressed?  Does it need to poll the
> interrupt endpoint just in case some data arrives?

That's right.

>  Why not then use a bulk
> endpoint?

Three reasons.  First, interrupt endpoints have guaranteed maximum 
latency (important for real-time response to input events) and bulk 
endpoints don't.  Second, polling bulk endpoints uses up a lot more 
bandwidth than polling interrupt endpoints.  Third, low-speed devices 
(which includes the majority of USB mice) aren't allowed to have bulk 
endpoints.

>  I always assumed the host would be notified when there is data to
> read from an interrupt endpoint.

Your assumption was wrong.  This is all explained in the USB 2.0
specification, freely available from www.usb.org.  Much of it is quite
readable.

> > Comparison of the usbmon logs should show the important differences.  
> > In fact, you've got four cases to look at, by varying the application 
> > (Windows app vs. your test program) and the device (real thing vs. your 
> > gadget).
> 
> Well apart from the minor issues listed above, the usbmon traces look
> identical for all four cases, with one of them (Windows to gadgetfs) having no
> interrupt messages.  The HID initialisation code (which tells the host there
> is an interrupt endpoint and where it is) is identical.
> 
> Any other ideas?  I'm happy to post my code if it would help.

Here is a patch to make usbfs accept 0-length transfers.  You may need
to tweak it somewhat to make it apply to your kernel.  And you should
check libusb to see if it rejects 0-length transfers as well.

Alan Stern



Index: usb-2.6/drivers/usb/core/devio.c
===================================================================
--- usb-2.6.orig/drivers/usb/core/devio.c
+++ usb-2.6/drivers/usb/core/devio.c
@@ -984,7 +984,7 @@ static int proc_do_submiturb(struct dev_
 				USBDEVFS_URB_ZERO_PACKET |
 				USBDEVFS_URB_NO_INTERRUPT))
 		return -EINVAL;
-	if (!uurb->buffer)
+	if (uurb->buffer_length > 0 && !uurb->buffer)
 		return -EINVAL;
 	if (!(uurb->type == USBDEVFS_URB_TYPE_CONTROL &&
 	    (uurb->endpoint & ~USB_ENDPOINT_DIR_MASK) == 0)) {
@@ -1040,11 +1040,6 @@ static int proc_do_submiturb(struct dev_
 			is_in = 0;
 			uurb->endpoint &= ~USB_DIR_IN;
 		}
-		if (!access_ok(is_in ? VERIFY_WRITE : VERIFY_READ,
-				uurb->buffer, uurb->buffer_length)) {
-			kfree(dr);
-			return -EFAULT;
-		}
 		break;
 
 	case USBDEVFS_URB_TYPE_BULK:
@@ -1057,9 +1052,6 @@ static int proc_do_submiturb(struct dev_
 		uurb->number_of_packets = 0;
 		if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE)
 			return -EINVAL;
-		if (!access_ok(is_in ? VERIFY_WRITE : VERIFY_READ,
-				uurb->buffer, uurb->buffer_length))
-			return -EFAULT;
 		break;
 
 	case USBDEVFS_URB_TYPE_ISO:
@@ -1099,27 +1091,34 @@ static int proc_do_submiturb(struct dev_
 			return -EINVAL;
 		if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE)
 			return -EINVAL;
-		if (!access_ok(is_in ? VERIFY_WRITE : VERIFY_READ,
-				uurb->buffer, uurb->buffer_length))
-			return -EFAULT;
 		break;
 
 	default:
 		return -EINVAL;
 	}
-	as = alloc_async(uurb->number_of_packets);
-	if (!as) {
+	if (uurb->buffer_length > 0 &&
+			!access_ok(is_in ? VERIFY_WRITE : VERIFY_READ,
+				uurb->buffer, uurb->buffer_length)) {
 		kfree(isopkt);
 		kfree(dr);
-		return -ENOMEM;
+		return -EFAULT;
 	}
-	as->urb->transfer_buffer = kmalloc(uurb->buffer_length, GFP_KERNEL);
-	if (!as->urb->transfer_buffer) {
+	as = alloc_async(uurb->number_of_packets);
+	if (!as) {
 		kfree(isopkt);
 		kfree(dr);
-		free_async(as);
 		return -ENOMEM;
 	}
+	if (uurb->buffer_length > 0) {
+		as->urb->transfer_buffer = kmalloc(uurb->buffer_length,
+				GFP_KERNEL);
+		if (!as->urb->transfer_buffer) {
+			kfree(isopkt);
+			kfree(dr);
+			free_async(as);
+			return -ENOMEM;
+		}
+	}
 	as->urb->dev = ps->dev;
 	as->urb->pipe = (uurb->type << 30) |
 			__create_pipe(ps->dev, uurb->endpoint & 0xf) |
@@ -1161,7 +1160,7 @@ static int proc_do_submiturb(struct dev_
 	kfree(isopkt);
 	as->ps = ps;
 	as->userurb = arg;
-	if (uurb->endpoint & USB_DIR_IN)
+	if (is_in && uurb->buffer_length > 0)
 		as->userbuffer = uurb->buffer;
 	else
 		as->userbuffer = NULL;
@@ -1171,9 +1170,9 @@ static int proc_do_submiturb(struct dev_
 	as->uid = cred->uid;
 	as->euid = cred->euid;
 	security_task_getsecid(current, &as->secid);
-	if (!is_in) {
+	if (!is_in && uurb->buffer_length > 0) {
 		if (copy_from_user(as->urb->transfer_buffer, uurb->buffer,
-				as->urb->transfer_buffer_length)) {
+				uurb->buffer_length)) {
 			free_async(as);
 			return -EFAULT;
 		}

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

  Powered by Linux