On Thu, 13 Dec 2007, Phil Endecott wrote:

> Dear Experts,
> 
> I have a simple test program that will try to read e.g. a device 
> descriptor using ioctl(USBDEVFS_CONTROL).  I've been trying this with 
> my assorted USB devices, with an analyser hooked up, and it's 
> remarkable how even something as basic as this can break some 
> non-compliant devices.
> 
> When a full-speed device is attached, the kernel first tries a 64-byte 
> read of the device descriptor.  If the device has an 8-byte ep0 max 
> packet size, it will return the first 8 bytes of the descriptor; the 
> host immediately does the status stage.
> 
> Later it reads the device descriptor with an 18-byte read and gets the 
> data in 3 packets (8+8+2).
> 
> If I read the descriptor using USBDEVFS_CONTROL, I'll also get the 18 
> bytes in 3 packets.  With most devices I'll get the same behaviour if I 
> ask for 18 bytes or if I ask for 64 bytes.  However, if I ask my 
> printer for 64 bytes it will return the first 8 and then NAK until the 
> host times out.
> 
> I presume that my printer is not allowed to do this: it should return 
> 18 bytes in 3 packets, and the host should realise that the data is 
> complete after the short 2-byte packet and do the status stage.  It's 
> yet another device that's somewhat non-compliant, right?  But what I 
> don't understand is why the kernel's initial 64-byte read and my 
> 64-byte read behave differently (and not just on this broken device): 
> why does the host controller consider the data complete and send the 
> status stage after one packet of 8 bytes when the kernel does it, but 
> keep sending INs (which are NAKed) when I do it?
> 
> Can anyone explain what's going on?  I'm curious because I fear that 
> there's another layer of complexity hiding in there, e.g. some 
> additional state that changes the host controller behaviour, that I 
> have yet to understand.

There is indeed a piece of information you're not taking into account.

The difference in behavior is caused by the fact that during the 
initial enumeration, the kernel doesn't know the ep0 maxpacket size.  
All it knows is the device's speed.  Low-speed devices must have a 
maxpacket size of 8, and high-speed devices must have a maxpacket size 
of 64 -- that's in the spec.  But full-speed devices can have any power 
of 2 within that range.

So the kernel guesses that the maxpacket size is 64 and tries to do a
64-byte transfer.  No matter what the real maxpacket size is, the
result will always be a short transfer because the device descriptor is
only 18 bytes long.  The host receives the first packet of the response
and then terminates the transfer, treating it as a short response since
it's less than the 64-byte maxpacket size.  Since that first packet is 
at least 8 bytes long, it contains the device's actual ep0 maxpacket 
value.  In this way the kernel learns what the value should be.

(BTW, this scheme isn't the one recommended in the USB spec.  It is the 
one used by Windows, and overall more devices seem to be compatible 
with it than with the "official" recommendation.)

But now consider what happens when _you_ try to do the same thing.  The
kernel already knows that the real maxpacket size is 8.  So instead of
expecting to receive a single 64-byte packet as a response, the host
controller driver expects to receive eight 8-byte packets.  When the
first 8-byte packet arrives it isn't treated as a short response since
it meets the driver's expectation.

Evidently nobody ever tested the printer in the way you have, so the 
firmware was never written to cope properly.

Alan Stern

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

Reply via email to