USB on real hw

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.
sounds
Member
Member
Posts: 112
Joined: Sat Feb 04, 2012 5:03 pm

Re: USB on real hw

Post by sounds »

If I understand correctly, the EHCI controller is in bypass mode, since the UHCI controller detected a device connected on port 0. Someone correct me if that's wrong.

Bonfra, maybe try modifying this:

Code: Select all

if(i == num_packets - 1)
            tds[i].flags |= TD_IOC;
How about setting TD_IOC for every TD, temporarily at least? And can we all see the UHCI controller registers and the TD Control and Status dword (DWORD at offset 4) when the driver gets stuck? Run/stop, Swdbg, HCHalt, HCPE, HSE, USB Error Interrupt, and the TD Control and Status are the way the UHCI controller indicates what it's doing.
User avatar
Bonfra
Member
Member
Posts: 270
Joined: Wed Feb 19, 2020 1:08 pm
Libera.chat IRC: Bonfra
Location: Italy

Re: USB on real hw

Post by Bonfra »

sounds wrote:If I understand correctly, the EHCI controller is in bypass mode, since the UHCI controller detected a device connected on port 0. Someone correct me if that's wrong.
Yes this is the assumption we are relying on. There is not a 100% confirmation from the BIOS since the config page doesn't show anything about this but the device is found in the port. probably, it's possible to assume that it is in bypass mode.
sounds wrote: Bonfra, maybe try modifying this:

Code: Select all

if(i == num_packets - 1)
            tds[i].flags |= TD_IOC;
How about setting TD_IOC for every TD, temporarily at least?
Sadly it didn't change nothing... I'ts like the schedule its ignoring those TDs

Here are all the logs:
Image
I honestly don't know what's going on here, the controllers seems to be entered in HCRESET state and the first td's timeout flag is set
Regards, Bonfra.
sounds
Member
Member
Posts: 112
Joined: Sat Feb 04, 2012 5:03 pm

Re: USB on real hw

Post by sounds »

Bonfra wrote:Sadly it didn't change nothing... I'ts like the schedule its ignoring those TDs
Could you please leave the TD_IOC flag set on all packets, temporarily at least, while the driver is getting stuck attempting to initiate the first transfer after a reset? If you didn't revert to setting TD_IOC only on the last TD in the set, perfect.
Bonfra wrote:the controllers seems to be entered in HCRESET state and the first td's timeout flag is set
That USBCMD doesn't look like HCRESET to me. It looks like Run/Stop in state Run (bit 0).

Code: Select all

Registers after 1000000 spins
USBCMD:  0x1
USBSTS:  0x0
USBINTR: 0x0
FRNUM:   0x323
SOFMOD:  0x40
Also the FRNUM value seems to match about what I'd expect the number of 1 ms frames would be if your code is spinning 10^6 times.

Lastly, are you sure TD[1]'s BUFPTR value should be 0x0?
User avatar
Bonfra
Member
Member
Posts: 270
Joined: Wed Feb 19, 2020 1:08 pm
Libera.chat IRC: Bonfra
Location: Italy

Re: USB on real hw

Post by Bonfra »

sounds wrote:Could you please leave the TD_IOC flag set on all packets, temporarily at least, while the driver is getting stuck attempting to initiate the first transfer after a reset? If you didn't revert to setting TD_IOC only on the last TD in the set, perfect.
In the screenshot, the IOC bit is only set in the last TD, I reverted the change to post a more coherent screenshot. At the end of this post I'll add on a future edit a screenshot of the logs with the IOC bit set in all packets. Sorry for the misunderstanding.
sounds wrote:That USBCMD doesn't look like HCRESET to me. It looks like Run/Stop in state Run (bit 0). [...]Also the FRNUM value seems to match about what I'd expect the number of 1 ms frames would be if your code is spinning 10^6 times
Yea sorry I misread the specs. So even the internal state appears to be fine.
sounds wrote:Lastly, are you sure TD[1]'s BUFPTR value should be 0x0?
Well, it's the last packet of the sequence and it doesn't need any buffer to write anything to. Is that field something more than a buffer? Does it also contain some flags? Not that I think about it I've seen some implementations with that final packet.buffer being set to 0x80000000; is there a specific reason for this?
Regards, Bonfra.
sounds
Member
Member
Posts: 112
Joined: Sat Feb 04, 2012 5:03 pm

Re: USB on real hw

Post by sounds »

Bonfra wrote:
sounds wrote:Lastly, are you sure TD[1]'s BUFPTR value should be 0x0?
Well, it's the last packet of the sequence and it doesn't need any buffer to write anything to. Is that field something more than a buffer? Does it also contain some flags? Not that I think about it I've seen some implementations with that final packet.buffer being set to 0x80000000; is there a specific reason for this?
This is where I'm really not sure what is the right thing to do. And changing td[1].BUFPTR value probably has nothing to do with why the controller is reporting the timeout error on td[0].

So while I am attempting to find any problems for you to look at, don't take this next statement with any weight. Just out of curiosity - you're sending a SETUP packet in td[0] followed by td[1], an IN packet len = 0x800 but the buffer says the controller can write bytes to physical address 0, if the IN packet receives anything. Are you sure the controller doesn't need a valid BUFPTR? I remain uncertain.

And if you did want to try setting a valid BUFPTR, what about setting len = 8 (encoded as len = 7)? This first SETUP packet is low speed, and the maximum transfer size is 8 for low speed transactions.
User avatar
BenLunt
Member
Member
Posts: 941
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

Re: USB on real hw

Post by BenLunt »

sounds wrote:If I understand correctly, the EHCI controller is in bypass mode, since the UHCI controller detected a device connected on port 0. Someone correct me if that's wrong.
That is correct, however it doesn't mean the SMI has given up control of the EHCI and UHCI(s).

Bonfra, since your stack works as expected on two emulators, may I suggest that it isn't your stack nor your buffers. It is hardware related. At the "Resetting Port 0" stage, print the port's value, before, during, and after. I still think that your code isn't waiting long enough and/or not enabling the port correctly. Does the port actually become enabled? Once it does, do you wait a few mS before you start your schedule?

As I mentioned before, an emulator will enable the port simply because you set the bit, *and* it will be instantly enabled, ready for packets. Hardware will not. You must make sure the reset was correct, then the enable is correct, and then wait for it to actually be enabled and ready.

Again, since your schedule (stack) works on two emulators, I strongly believe it is not your schedule. I believe it is hardware related.

Hope this helps,
Ben
User avatar
Bonfra
Member
Member
Posts: 270
Joined: Wed Feb 19, 2020 1:08 pm
Libera.chat IRC: Bonfra
Location: Italy

Re: USB on real hw

Post by Bonfra »

BenLunt wrote: however it doesn't mean the SMI has given up control of the EHCI and UHCI(s).
This should only apply to HDIs so it should not be a problem (since I'm working with an msd) but I've followed your suggestion so it should not interfere.
BenLunt wrote:A simple write to PCI configuration word 0xC0 of 0x8F00 does the trick.
BenLunt wrote: Bonfra, since your stack works as expected on two emulators, may I suggest that it isn't your stack nor your buffers. It is hardware related. At the "Resetting Port 0" stage, print the port's value, before, during, and after. I still think that your code isn't waiting long enough and/or not enabling the port correctly. Does the port actually become enabled? Once it does, do you wait a few mS before you start your schedule?
Sorry if I pursued the stack path but I wanted to be 101% sure that it wasn't faulty on that side. Obviously you were right but still some more confirmation doesn't hurt ;P
Here follows the log of the port register during the reset. This is the code that is responsible for the port reset; I just added a sleep of 50ms at line 164 before returning so to ensure some time passes after the port is enabled.
Image
After the "Waiting for port to be ready" message 10 iterations are performed spinning on the PORTSC_ENABLE bit sleeping 10ms between each and logging the value each time. I'm not quite sure about that 0xE2 value repeated three times but idk I'm a bit confused
Regards, Bonfra.
sounds
Member
Member
Posts: 112
Joined: Sat Feb 04, 2012 5:03 pm

Re: USB on real hw

Post by sounds »

Just a quick thought, do you need to set Low Speed (LS, bit 26) in the TD flags?
User avatar
BenLunt
Member
Member
Posts: 941
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

Re: USB on real hw

Post by BenLunt »

Bonfra wrote:
BenLunt wrote: however it doesn't mean the SMI has given up control of the EHCI and UHCI(s).
This should only apply to HDIs so it should not be a problem (since I'm working with an msd) but I've followed your suggestion so it should not interfere.
BenLunt wrote:A simple write to PCI configuration word 0xC0 of 0x8F00 does the trick.
I am guessing you meant HID instead of HDI. However, with newer firmware, it manages MSDs as well, to be able to boot from them. However, since you sent the "release", I don't think this is a problem.
Bonfra wrote:After the "Waiting for port to be ready" message 10 iterations are performed spinning on the PORTSC_ENABLE bit sleeping 10ms between each and logging the value each time. I'm not quite sure about that 0xE2 value repeated three times but idk I'm a bit confused
I don't know where you got the 0xE2 from either. I doubt the port had a value of 0xE2. Place a "printf()" at line 152 and see what value it has. Something that will print the iteration number and the value. For example:

Code: Select all

printf(" %i:  0x%04X\n", iteration++, status); // of course defining 'iteration' as an integer with a value of zero at the top of your function.
Curious, is your UHCI an HP brand (vendor id = 0x103C)?

At Line 143, you clear the reset bit, but also write a 1 to the CSC bit (the CSC bit is set after the reset, so writing the existing value writes a 1 to the CSC bit at line 143). I have found that this doesn't work on some controllers.

Throughout my research with the UHCI, the following code has worked the best:
(see the note at line 2. 'base' is the portIO base value, your 'controller->io' value)
(udelay() delays for a given microseconds while mdelay() for a given milliseconds)

Code: Select all

      ret = -1;
      const bit8u port = ??????;  // Bonfra: 'port' = 0x10, 0x12, etc. Modify to match your code...
      bit16u val = 0;
      
      // reset the port, holding the bit set at least 50 ms for a root hub
      val = inpw(base + port);
      outpw(base + port, val | (1<<9));
      mdelay(USB_TDRSTR);
      
      // clear the reset bit, do not clear the CSC bit while
      //  we clear the reset.  The controller needs to have
      //  it cleared (written to) while *not* in reset
      // also, write a zero to the enable bit
      val = inpw(base + port);
      outpw(base + port, val & 0xFCB1);
      udelay(300);  // note that this is *not* the USB specification delay
      // if we wait the recommended USB_TRSTRCY time after clearing the reset,
      //  the device will not enable when we set the enable bit below.
      
      // the CSC bit must be clear *before* we set the Enable bit
      //  so clear the CSC bit (Write Clear), then set the Enable bit
      val = inpw(base + port);
      outpw(base + port, val | 0x0003);
      outpw(base + port, val | 0x0005);
      
      // wait for it to be enabled
      udelay(50);
      
      // now clear the PEDC bit, and CSC if it still
      //  happens to be set, while making sure to keep the
      //  Enable bit and CCS bits set
      val = inpw(base + port);
      outpw(base + port, val | 0xF);
      
      // short delay before we start sending packets
      mdelay(50);
      
      // return successful if the Enabled bit was set
      if (inpw(base + port) & (1<<2))
        ret = SUCCESS_RESET;
      
      return ret;
I have found this code to work on 99% of all low-, full-, high-, and super-speed devices I have plugged into an UHCI. (Granted, the high- and super-speed devices revert to full-speed).

Try this and see what happens.

Ben
- https://www.fysnet.net/the_universal_serial_bus.htm
rdos
Member
Member
Posts: 3296
Joined: Wed Oct 01, 2008 1:55 pm

Re: USB on real hw

Post by rdos »

AFAIK, the UHCI controller has reasonable settings for a controller that is running. Bit 0 in the cmd register is 1, which means it is executing the schedule.

Also, the status response for the first TD has active bit set, and CRC/timeout error. The last bit indicates that the packet is not accepted by the endpoint. Which could be because of errors in the control message, like wrong sequence, endpoint or similar. You should be able to analyse this using the UHCI document since you log the packet contents.

You really should have an USB analyser for this task, or analyse packet contents in your emulator. There are specific rules for how setup packets must be formatted, and I'm not convinced that emulators will check these properly.
User avatar
BenLunt
Member
Member
Posts: 941
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

Re: USB on real hw

Post by BenLunt »

I have seen on actual hardware that the Run bit gets set, etc, and the controller tries to execute the schedule for a device. However, the device was not enabled correctly, even though the Enable bit is set, and the schedule is not properly executed for that device. What Bonfra is explaining seems to be exactly what I have witnessed. Even though the Enable bit is set, the device was not correctly enabled, both on the controller side and the device side, and the schedule does not execute correctly.

I am hoping that he tries my posted code and the schedule starts to work as expected. I am not saying that it has to be this, though I have experienced this personally, so this is my current solution, until it proves otherwise.
There are specific rules for how setup packets must be formatted, and I'm not convinced that emulators will check these properly.
I whole-heartedly agree.

Thanks,
Ben
rdos
Member
Member
Posts: 3296
Joined: Wed Oct 01, 2008 1:55 pm

Re: USB on real hw

Post by rdos »

BenLunt wrote:I have seen on actual hardware that the Run bit gets set, etc, and the controller tries to execute the schedule for a device. However, the device was not enabled correctly, even though the Enable bit is set, and the schedule is not properly executed for that device. What Bonfra is explaining seems to be exactly what I have witnessed. Even though the Enable bit is set, the device was not correctly enabled, both on the controller side and the device side, and the schedule does not execute correctly.

I am hoping that he tries my posted code and the schedule starts to work as expected. I am not saying that it has to be this, though I have experienced this personally, so this is my current solution, until it proves otherwise.
There are specific rules for how setup packets must be formatted, and I'm not convinced that emulators will check these properly.
I whole-heartedly agree.

Thanks,
Ben
It could also be that the USB device was not properly initiated (IOW, the problem is with the USB device, not the UHCI controller). The reset timing needs to be correct, and some devices require longer time than others to be properly initialized. I bet emulators do not handle this the same way as real devices either. Note that for UHCI, it's the OS driver that is responsible for the correct reset timing, not the controller. My reset port for UHCI has a fixed 200 ms delay after the reset command is sent & accepted.
User avatar
BenLunt
Member
Member
Posts: 941
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

Re: USB on real hw

Post by BenLunt »

rdos wrote:It could also be that the USB device was not properly initiated (IOW, the problem is with the USB device, not the UHCI controller). The reset timing needs to be correct, and some devices require longer time than others to be properly initialized.
Agreed, hence the posted reset code. I tested many devices and the timings in that posted code have proved to be sufficient for each.
rdos wrote:I bet emulators do not handle this the same way as real devices either. Note that for UHCI, it's the OS driver that is responsible for the correct reset timing, not the controller. My reset port for UHCI has a fixed 200 ms delay after the reset command is sent & accepted.
Again, I agree. I have found that 50ms has been enough for all devices I have tried. However, it doesn't hurt one bit to wait the 200ms you suggest. Better safe than sorry.

Ben
User avatar
Bonfra
Member
Member
Posts: 270
Joined: Wed Feb 19, 2020 1:08 pm
Libera.chat IRC: Bonfra
Location: Italy

Re: USB on real hw

Post by Bonfra »

Hi, sorry for my long absence. The start of the year is being a bit of a tough period between university and work.
I'll never thank you enough for following me in this hard journey fighting not only with hardware and software but also with my absenteeism :P
BenLunt wrote: I am guessing you meant HID instead of HDI. However, with newer firmware, it manages MSDs as well, to be able to boot from them. However, since you sent the "release", I don't think this is a problem.
Yea sorry, little typo. Yes to be sure I'm writing the LEGSUP register before and after the controller initialization.
BenLunt wrote: I don't know where you got the 0xE2 from either. I doubt the port had a value of 0xE2. Place a "printf()" at line 152 and see what value it has. Something that will print the iteration number and the value. For example:

Code: Select all

printf(" %i:  0x%04X\n", iteration++, status); // of course defining 'iteration' as an integer with a value of zero at the top of your function.
Every log that starts with "PORTSC:" is in that line you asked for. I'm not printing the iteration number but you can assume everything before a message of "No device on port %d" or "Port enable" belongs to the same cycles of iteration.
BenLunt wrote: Curious, is your UHCI an HP brand (vendor id = 0x103C)?
Nope, PCI vendor id (first two words) is 0x8086

I tried implementing and executing the code you posted but still only works on VMs... I'm pushing the updated code on github so you can take a look (I inflated a bit the delays not having a udelay). This code is progressively getting more and more unreadable, tell me if it needs a little clean so you don't get crazy reading it XD
Regards, Bonfra.
User avatar
BenLunt
Member
Member
Posts: 941
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

Re: USB on real hw

Post by BenLunt »

Bonfra wrote:Hi, sorry for my long absence.
No apology needed.

Would you please post a bootable image of your latest again?

Thanks,
Ben
Post Reply