USB questions

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
xeyes
Member
Member
Posts: 212
Joined: Mon Dec 07, 2020 8:09 am

USB questions

Post by xeyes »

Got a bunch of questions while trying to make usb work:

1. power and PM

I know these are huge areas themselves and have no intention to get into them now, but have 2 questions:

Power:
IIRC software needs to keep track of the total power usage. There are also max current/self-powered fields in configuration descriptor.

But where do I get info on the total amount of power available to a port or to the whole root hub?

Is it okay to simply use devices without looking at their required power and let the port figure that out and go into over current state as needed?

PM:
Not trying to save a few mini watts now and I do disable features like hw LPM.

Does PM have implications other than power usage though? Such as meaningfully damage (heat?) the device/controller because of not allowing them to go into lower power states?

There's also a "Max Exit Latency" field which the software should fill but seems quite difficult to calculate. It seems to be quite closely related to link and topology, and hub is something I don't have any support for.

Is it okay to keep Max Exit Latency 0 on real hardware? What are the downsides of doing so?

2. MSD (BBB)

cmd set:

I only tried qemu's "usb-storage" drive. Some scsi cmds work on it, while others like the 16B read capacity (opcode 0x9E) return a cmd failed error (CSW status 1).

Is there a good list of the subset of scsi cmd set commonly supported by BBB flush drives?

LUN:

As I understand, LUN is a concept about SANs or big raid boxes, where physical disks are "raided" into logical arrays and then exposed as LUNs to the users. How does this apply to a flash drive?

Reliability and error handling:

The word stall appears a lot in the specs. Almost giving me the feeling that a BBB drive is ready to stall on a whim anytime. Fortunately the qemu usb-storage drive seems quite stable.

For real world flush drives though, does the wiki warning "In real hardware, floppy drives are extremely unreliable. On emulators, it doesn't matter -- but for code that is intended for real hardware, make sure to retry every command at least twice more after any error." about floppies apply?

In other words, if the drive indeed stalls the bulk pipe, is it a good idea to retry the same "CBW->DATA->CSW" sequence a few times, or is this unlikely to work if the first attempt failed?

3. HID

More than 1 keycodes per report:

Spec 1.11 says that "the order of keycodes in array fields has no significance".

How should I deal with the situation of more than 1 keycode in a report? For now discarding any reports like that gave the "expected" behavior when typing fast.

But I can't believe that this is the "correct" way, otherwise, why would the keyboard have the feature to report more than 1 keycode?

Maybe this is a general keyboard question, should I try to track the "make" and "break" of non-modifier keys? What would be a good use case where someone would hold down more than 1 (non-modifier) keys at the same time?

Report descriptor parsing:

It seems that report descriptors, at least the qemu ones, are easy to parse manually. But there's a huge usage table and depending on the chosen page there can be lots of variations. Which makes it be difficult to write code to process them automatically.

For now I created a short list of supported devices, and don't operate on any device not on the list.

Is there a good middle ground between this and a "real" HID parser?
nullplan
Member
Member
Posts: 1790
Joined: Wed Aug 30, 2017 8:24 am

Re: USB questions

Post by nullplan »

xeyes wrote:Is there a good list of the subset of scsi cmd set commonly supported by BBB flush drives?
First of all, Seagate has this wonderful reference manual: https://www.seagate.com/files/staticfil ... 93068j.pdf

The bInterfaceSubClass field of the interface descriptor is supposed to identify the command set. There is a table given in the mass storage specification overview. The interface class invokes the mass storage driver, the interface sub class identifies the command set, and the interface protocol identifies the transport. BBB is only the transport, not the command set. However, the Mass Storage Bootability specification lists all the SCSI commands an MSC device must support in order to allow a system to boot from the device. So that may mean it is a good subset.
xeyes wrote:As I understand, LUN is a concept about SANs or big raid boxes, where physical disks are "raided" into logical arrays and then exposed as LUNs to the users. How does this apply to a flash drive?
Well, that at least I can answer. Each LUN represents its own drive. This could be used, for example, for a CD changer (each sub-drive as its own LUN). In practice, most BBB devices only have a single LUN, but you really don't need to care. You only send a Get Max LUN request to the drive and spin off a driver instance for each LUN (make sure to only send one transaction at a time to the device).
xeyes wrote:The word stall appears a lot in the specs.
That's a specific type of reply on the USB. And it is used basically whenever the device doesn't like something. Sent a command it couldn't handle? Stall. Made some kind of protocol error? Stall. The only thing it means is that the transfer failed.

Additionally, a stall on endpoint 0 resolves itself, but a stall on any other endpoint must be cleared with a Clear Feature request (because somehow, being stalled is a feature of endpoints). The BBB specification contains the more specific Reset Recovery procedure (send a BBB Reset request, then clear the stall on one pipe, then on the other, not sure about the order right now).
xeyes wrote:In other words, if the drive indeed stalls the bulk pipe, is it a good idea to retry the same "CBW->DATA->CSW" sequence a few times, or is this unlikely to work if the first attempt failed?
Well, if the drive actually stalls out, then there was a protocol error. So that means no, it is probably not a good idea to do that. It is a good idea to log what happened, then perform a reset recovery. If there was an actual read failure, you would get garbage data, followed by a CSW that indicates failure. And then you do what the BBB spec tells you to (depends on the actual status). If there was indeed a permanent failure, a retry is unlikely to matter.
xeyes wrote:More than 1 keycodes per report:
Well, how do you handle input devices in general? I intend to make them as generic as possible; to me, an input device is a collection of buttons, relative axes, and absolute axes. A keyboard has 105 of the former and none of the latter two. And the events the device reports are the rising and falling edges of the buttons.

So you parse the incoming reports as just a collection of buttons that are now pressed. By comparing the result of that with the previous report, you can see which buttons were pressed and which were released. Another driver can turn this into input bytes for the terminal, but I would make that a later stage. At the USB report stage, you only keep track of buttons pressed and released.
xeyes wrote:For now discarding any reports like that gave the "expected" behavior when typing fast.
Well, then you likely never typed more than two letters at a time, so the keyboard would send you a report with the first button, then the first and the second (which you discard) and then only the second. But what happens if you hold on to one key, then repeatedly strike another? I should think, with your current strategy you would see the first key being typed again every time you release the second key. That is not the expected behavior. That is why you should keep track of pressed and released buttons. Come on, it is only 105 bits, I think you can spare those.
Carpe diem!
xeyes
Member
Member
Posts: 212
Joined: Mon Dec 07, 2020 8:09 am

Re: USB questions

Post by xeyes »

Thanks! Yes I meant the SCSI subclass. Bootability spec is quite useful and the CD changer example makes sense.
nullplan wrote: That's a specific type of reply on the USB. And it is used basically whenever the device doesn't like something. Sent a command it couldn't handle? Stall. Made some kind of protocol error? Stall. The only thing it means is that the transfer failed.

Additionally, a stall on endpoint 0 resolves itself, but a stall on any other endpoint must be cleared with a Clear Feature request (because somehow, being stalled is a feature of endpoints). The BBB specification contains the more specific Reset Recovery procedure (send a BBB Reset request, then clear the stall on one pipe, then on the other, not sure about the order right now).

Well, if the drive actually stalls out, then there was a protocol error. So that means no, it is probably not a good idea to do that. It is a good idea to log what happened, then perform a reset recovery. If there was an actual read failure, you would get garbage data, followed by a CSW that indicates failure. And then you do what the BBB spec tells you to (depends on the actual status). If there was indeed a permanent failure, a retry is unlikely to matter.
The BBB spec I found from 1999 is too terse, there's no mentioning of many possible stall cases outside of the 13 cases.

Tried some drives on hand, most are well behaving, no stall at all during mixed reads and writes.

One of them though (you can probably guess the maker, who has already made a "fine" name for themselves among flash drive users and people from related industries), gives nothing but trouble.

It seems to work as a read only drive, but can stall either a CBW out or a CSW in once write is attempted. The write may work, or it may stall, it's highly random. If the write worked, some other read or write after it will soon stall.

When it stalls, if I follow the spec for a CSW stall (the spec doesn't talk about CBW stall at all), the drive will stall again, at which point the spec says that a reset recovery is needed.

But if a reset recovery is done, the drive will actually STALL very soon after that, again either on the next CBW out, or a DATA in or out after the CBW, or a few cmds done the road, but it will do this for sure very soon after a reset recovery.

STALL is not a "USB stall" and looks more like a hard hang, during which the drive will flash its LED slowly at 1hz or so. The affected transaction doesn't complete, doesn't fail and doesn't timeout. If I hand it back in this state to a Linux host, Linux will print screen full of red texts in dmesg, with a conclusion of "unable to enumerate USB device". Only a physical power cycle can recover the drive from this "stall".

It also throws a lot of other strange errors from time to time, such as reporting that it's not ready because 'media is not present'.

Obviously one would want some kind of retry rather than keep panicking every time such a temperamental drive acts up?
nullplan wrote: Well, how do you handle input devices in general? I intend to make them as generic as possible; to me, an input device is a collection of buttons, relative axes, and absolute axes. A keyboard has 105 of the former and none of the latter two. And the events the device reports are the rising and falling edges of the buttons.
I actually don't handle input in a general way. Keyboard is handled as keyboard (interpret scancode), and mouse is handled as mouse (passthrough to user space as is). Something else (hard to come up with an example of another input device, maybe a mic?) will have to be handled as something else. Like your grand plan to unify them all though. A mic might have buttons like mute and axis like XY but it seems quite difficult to treat a mic using some code meant for a keyboard.
nullplan wrote: So you parse the incoming reports as just a collection of buttons that are now pressed. By comparing the result of that with the previous report, you can see which buttons were pressed and which were released. Another driver can turn this into input bytes for the terminal, but I would make that a later stage. At the USB report stage, you only keep track of buttons pressed and released.

Well, then you likely never typed more than two letters at a time, so the keyboard would send you a report with the first button, then the first and the second (which you discard) and then only the second. But what happens if you hold on to one key, then repeatedly strike another? I should think, with your current strategy you would see the first key being typed again every time you release the second key. That is not the expected behavior. That is why you should keep track of pressed and released buttons. Come on, it is only 105 bits, I think you can spare those.
For now my USB stage converts keycodes that are not discarded to scancode and hand that and the modifier status over to the PS/2 keyboard code.

Yes it indeed behaves just like what you mentioned, yet for PS/2 the 2nd key is repeated instead. Learned something new as I always thought that pressing down more than 1 key causes undefined behavior.

Still don't get the use case here. If soneone would like the 2nd key to be repeated, how about pressing that key by itself? Why holding the 1st one down as well?

You probably only need 48 bits to save the state of the previous reports, but without a well defined convention on how things should behave, processing this can be a huge headache.

What should happen if one holds 6 keys down and then releases the 4th one? How about releasing the 2nd and the 5th together? Are there standards/conventions for this cross different software/platforms?
nullplan
Member
Member
Posts: 1790
Joined: Wed Aug 30, 2017 8:24 am

Re: USB questions

Post by nullplan »

xeyes wrote:But if a reset recovery is done, the drive will actually STALL very soon after that, again either on the next CBW out, or a DATA in or out after the CBW, or a few cmds done the road, but it will do this for sure very soon after a reset recovery.

STALL is not a "USB stall" and looks more like a hard hang, during which the drive will flash its LED slowly at 1hz or so. The affected transaction doesn't complete, doesn't fail and doesn't timeout. If I hand it back in this state to a Linux host, Linux will print screen full of red texts in dmesg, with a conclusion of "unable to enumerate USB device". Only a physical power cycle can recover the drive from this "stall".

It also throws a lot of other strange errors from time to time, such as reporting that it's not ready because 'media is not present'.
That sounds like a healthy device. In your place, I would probably resort to reading the Linux source code to find out what requests it is sending to the device (if it can manage to somehow make the device not fail horribly). It would appear that there are two ways to build a device in the industry. One is to go by the spec, the other is to slap something together and call it a day if Windows doesn't crash. And for this sort of tomfoolery, the Linux source code is a really good source to find all the workarounds and errata.
xeyes wrote:I actually don't handle input in a general way. Keyboard is handled as keyboard (interpret scancode),
Well, then for USB keyboards, you have to keep track of the pressed buttons and generate the scan codes that way. Figure out which button had a rising edge. This also allows you to repeat the most recently pressed key if it is held down long enough. The only reports I would ignore are the ones that tell you about an overflow. Just keep the previous state, and recover one the overflow is gone.

Ben Heck demonstrated on his Youtube channel that some really cheap keyboards can go into overflow with as few as three buttons pressed. The reason is that those have buttons with no diodes, so if three buttons are pressed that are arranged just right on the scanning grid, a fourth "phantom press" will be generated. Cheap keyboards recognize the situation and immediately send an overflow. More expensive ones have diodes in the buttons so the problem doesn't happen.
xeyes wrote:Something else (hard to come up with an example of another input device, maybe a mic?)
Try joystick or gamepad. A microphone would be an audio device, and have to be handled differently. Honestly, for now I am planning to avoid isochronous transfers. Although sound support would be nice to have somewhere down the line.
xeyes wrote:Learned something new as I always thought that pressing down more than 1 key causes undefined behavior.
Er... no? That would be pretty much unusable for many applications. Such as games. Imagine playing an FPS where you could not go forward and left at the same time.
xeyes wrote:Still don't get the use case here.
USB is not interested in your desire for scancodes. It only wants to tell you of the state of the keyboard. PS/2 does the same, but in a different format. For some reason, USB thinks it is vital to keep reporting what you already know. So it tells you that the A key is pressed even though you already knew that, and only adds the L key to the report when it gets pressed. PS/2 will instead send you the Make code for the A key, and then the Make and Break codes for the L key as it is pressed and released.
xeyes wrote: If soneone would like the 2nd key to be repeated, how about pressing that key by itself? Why holding the 1st one down as well?
Well, I doubt a typist would do something like that. More likely, something like this would happen in a game, or some weird and complicated GUI application. Or maybe a piece of chocolate dropped between the keys and is holding down a button.

Anyway, most users will expect the behavior you have seen on PS/2.
xeyes wrote:You probably only need 48 bits to save the state of the previous reports, but without a well defined convention on how things should behave, processing this can be a huge headache.
Just save one bit for each button, whether it is pressed or not. When a new report comes in, and it is not an overflow report, turn it into the same format, and then find the rising edges (the bits that went from 0 to 1), and then you know which scan codes to push into the scan code buffer. Once you are at the stage where applications want to know about key release as well, do the same with falling edges. Personally, I don't find "~old & new" to be a huge headache.
xeyes wrote:What should happen if one holds 6 keys down and then releases the 4th one? How about releasing the 2nd and the 5th together? Are there standards/conventions for this cross different software/platforms?
Generally, I would expect the most recent key I pressed to be repeated, if I did hold multiple keys down. Also, if that key is released, no further repeats are made (e.g. I press A, S, and D in order, then d is repeated until I release the D key. Doesn't matter if I release A or S, but if I hold onto them, they are not repeated). Also, importantly, I would expect the scan code to be pushed to the application on the key press, not the key release of all the other keys. That doesn't really matter when typing fast, but might matter when multiple keys are typed because of stuck keys or something. The idea is: Finger hits key --> eye sees new letter appear. You have ca. 100ms to make that work (humans are slowpokes).
Carpe diem!
Klakap
Member
Member
Posts: 297
Joined: Sat Mar 10, 2018 10:16 am

Re: USB questions

Post by Klakap »

are you sending INQUIRY as first command before anything else and then REQUEST_SENSE command?
xeyes
Member
Member
Posts: 212
Joined: Mon Dec 07, 2020 8:09 am

Re: USB questions

Post by xeyes »

Klakap wrote:are you sending INQUIRY as first command before anything else and then REQUEST_SENSE command?
First SCSI cmd I send is INQUIRY, then mode sense=>test unit ready=>read capacity. REQUEST_SENSE is only sent when some other SCSI cmd failed.

If I add a REQUEST_SENSE anyway, the drive returns SENSE CURRENT + SENSE KEY NONE and then continue to behave as it was.

I did fix an error in my clear feature halt code but this wasn't able to help this drive either, which still STALLs or hangs.
xeyes
Member
Member
Posts: 212
Joined: Mon Dec 07, 2020 8:09 am

Re: USB questions

Post by xeyes »

nullplan wrote:That sounds like a healthy device. In your place, I would probably resort to reading the Linux source code to find out what requests it is sending to the device (if it can manage to somehow make the device not fail horribly). It would appear that there are two ways to build a device in the industry. One is to go by the spec, the other is to slap something together and call it a day if Windows doesn't crash. And for this sort of tomfoolery, the Linux source code is a really good source to find all the workarounds and errata.
If a drive that doesn't work sounds healthy, do the drives that work sound unhealthy :wink:?

Linux can write this drive fine. That said, I'd rather figure something out myself, with occasional hints from folks more familar with a topic, like you; than looking at code, or "the correct answer".

For devices made using "the other way", if I'm not taking care of commonly seen issues or setup steps, I'd change/fix my code. But if a maker gets too creative in their interpretation of the specs and the resultant device needs things like:

Code: Select all

if(DID == 0xA && VID == 0xB) {apply_device_specific_workarounds();}
I'd be happy to not use it or use it as a read only device.
nullplan wrote:Well, then for USB keyboards, you have to keep track of the pressed buttons and generate the scan codes that way. Figure out which button had a rising edge. This also allows you to repeat the most recently pressed key if it is held down long enough. The only reports I would ignore are the ones that tell you about an overflow. Just keep the previous state, and recover one the overflow is gone.

Ben Heck demonstrated on his Youtube channel that some really cheap keyboards can go into overflow with as few as three buttons pressed. The reason is that those have buttons with no diodes, so if three buttons are pressed that are arranged just right on the scanning grid, a fourth "phantom press" will be generated. Cheap keyboards recognize the situation and immediately send an overflow. More expensive ones have diodes in the buttons so the problem doesn't happen.
xeyes wrote:Something else (hard to come up with an example of another input device, maybe a mic?)
Try joystick or gamepad. A microphone would be an audio device, and have to be handled differently. Honestly, for now I am planning to avoid isochronous transfers. Although sound support would be nice to have somewhere down the line.
xeyes wrote:Learned something new as I always thought that pressing down more than 1 key causes undefined behavior.
Er... no? That would be pretty much unusable for many applications. Such as games. Imagine playing an FPS where you could not go forward and left at the same time.
Okay, I can see it being a very useful feature for games and my approach won't work there. OTOH, it's not like my kernel can support any game, maybe text adventures that don't need multiple keys pressed together :lol:

Yes I remembered that for people into keyboards things like "10 key rollover" is a big thing, I don't have one but hopefully those highend ones also support boot protocol (with reduced capability of "6 key rollover").

Audio/Isoch, at least certain types, is easier than I thought. I probably spent as much time on audio as MSD, and the code can run a 1.0 adaptive device quite reliably now, even through a hub. Still having issue with a 2.0 async device though (it keeps saying that it wants 0.00146 more samples per frame than what I believe is necessary, and there's audiable distortions from time to time). Give a try esp. if you have a adaptive device on hand, you might be surprised at how easy it is to get it working.
nullplan wrote:USB is not interested in your desire for scancodes. It only wants to tell you of the state of the keyboard. PS/2 does the same, but in a different format. For some reason, USB thinks it is vital to keep reporting what you already know. So it tells you that the A key is pressed even though you already knew that, and only adds the L key to the report when it gets pressed.
Maybe folks drafting the spec overlooked the fact that people serious about games tend to use a controller rather than a USB keyboard.
nullplan wrote: PS/2 will instead send you the Make code for the A key, and then the Make and Break codes for the L key as it is pressed and released.
xeyes wrote: If soneone would like the 2nd key to be repeated, how about pressing that key by itself? Why holding the 1st one down as well?
Well, I doubt a typist would do something like that. More likely, something like this would happen in a game, or some weird and complicated GUI application. Or maybe a piece of chocolate dropped between the keys and is holding down a button.

Anyway, most users will expect the behavior you have seen on PS/2.
Interestingly, my PS/2 code is shared with USB keyboard besides the keycode conversion. Yet PS/2 keyboard behaves "correctly" in the multi-key test.
nullplan wrote:Just save one bit for each button, whether it is pressed or not. When a new report comes in, and it is not an overflow report, turn it into the same format, and then find the rising edges (the bits that went from 0 to 1), and then you know which scan codes to push into the scan code buffer. Once you are at the stage where applications want to know about key release as well, do the same with falling edges. Personally, I don't find "~old & new" to be a huge headache.
xeyes wrote:What should happen if one holds 6 keys down and then releases the 4th one? How about releasing the 2nd and the 5th together? Are there standards/conventions for this cross different software/platforms?
Generally, I would expect the most recent key I pressed to be repeated, if I did hold multiple keys down. Also, if that key is released, no further repeats are made (e.g. I press A, S, and D in order, then d is repeated until I release the D key. Doesn't matter if I release A or S, but if I hold onto them, they are not repeated). Also, importantly, I would expect the scan code to be pushed to the application on the key press, not the key release of all the other keys. That doesn't really matter when typing fast, but might matter when multiple keys are typed because of stuck keys or something. The idea is: Finger hits key --> eye sees new letter appear. You have ca. 100ms to make that work (humans are slowpokes).
The headache is really about not able to think concretely about a sink for all these data and the interface it might want, as for now everything is CLI based. Maybe it's a better idea to wait for a specific need to show up before working on the plumbing.

Where did you find that interesting 100ms number though?
nullplan
Member
Member
Posts: 1790
Joined: Wed Aug 30, 2017 8:24 am

Re: USB questions

Post by nullplan »

xeyes wrote:If a drive that doesn't work sounds healthy, do the drives that work sound unhealthy :wink:?
That was sarcasm.
xeyes wrote:That said, I'd rather figure something out myself, with occasional hints from folks more familar with a topic, like you; than looking at code, or "the correct answer".
What, you rely on me? Oh, you poor soul. Nobody deserves this.

Anyway, sometimes the "correct" answer is "this device does not work at all except with the precise sequence of commands Windows Me emitted when the developers created the controller in 1999", and everyone had to work around it ever since.
xeyes wrote:Where did you find that interesting 100ms number though?
Can't remember that, actually. Probably my ergonomics course at Uni (which was otherwise a waste of time). 100ms is about the time difference two events can have such that humans will perceive them as simultaneous. Not under all circumstances though (our ears are more time-precise than our eyes, for example). Which is why the common advice given is that all applications should respond to all recognized and accepted inputs in some way within 100ms. And if it is to turn the mouse pointer into a sand clock if the processing takes longer.
Carpe diem!
xeyes
Member
Member
Posts: 212
Joined: Mon Dec 07, 2020 8:09 am

Re: USB questions

Post by xeyes »

nullplan wrote:
xeyes wrote:If a drive that doesn't work sounds healthy, do the drives that work sound unhealthy :wink:?
That was sarcasm.
Sorry didn't get that. #-o
nullplan wrote:
xeyes wrote:That said, I'd rather figure something out myself, with occasional hints from folks more familar with a topic, like you; than looking at code, or "the correct answer".
What, you rely on me? Oh, you poor soul. Nobody deserves this.
Instead of trying to rely on, it's more like that I appreciate insights from folks here who are both knowledable and willing to help, like yourself.
nullplan wrote: Anyway, sometimes the "correct" answer is "this device does not work at all except with the precise sequence of commands Windows Me emitted when the developers created the controller in 1999", and everyone had to work around it ever since.
I found "the answer" after reading this excellent post

Turns out that this drive doesn't like a storage reset at the beginning, despite the spec seems to hint that a storage reset shall happen before the first CBW.
nullplan wrote:Which is why the common advice given is that all applications should respond to all recognized and accepted inputs in some way within 100ms. And if it is to turn the mouse pointer into a sand clock if the processing takes longer.
LOL I'm not sure whether the sand clock/spinning ball helps or harms though. Probably the most annoying mouse cursor of all time?
Post Reply