UHCI ports aren't enabled on real hardware

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
Klakap
Member
Member
Posts: 297
Joined: Sat Mar 10, 2018 10:16 am

UHCI ports aren't enabled on real hardware

Post by Klakap »

Good day,

I have UHCI driver that good work in Bochs, so I try it on real hardware. And I am surprise, because UHCI ports aren't enabled. I use this code:

Code: Select all

//EDIT: code is on [url]https://github.com/Klaykap/CelerOS/blob/master/source/core/drivers/usb_uhci.c[/url]
Here is result from real hardware:
Image

Please what I forget?
Last edited by Klakap on Tue Mar 31, 2020 12:22 pm, edited 1 time in total.
User avatar
BenLunt
Member
Member
Posts: 941
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

Re: UHCI ports aren't enabled on real hardware

Post by BenLunt »

Did you power the port? Even reading the speed will return zero, even though it happens that the FULL SPEED indication is zero.

Nothing happens until you power the port.

Ben
- http://www.fysnet.net/the_universal_serial_bus.htm
Klakap
Member
Member
Posts: 297
Joined: Sat Mar 10, 2018 10:16 am

Re: UHCI ports aren't enabled on real hardware

Post by Klakap »

Please, how I can power the port? I cant found anything about powering the UHCI port. Is it set bit 2 in root port register?
User avatar
BenLunt
Member
Member
Posts: 941
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

Re: UHCI ports aren't enabled on real hardware

Post by BenLunt »

Wow, it has been so long that I have used UHCI, that I completely forgot that it is always powered. It has been such a common mistake with the other more recent controllers, I just jumped right to it. Sorry, my mistake.

Anyway, here is the error in your code.

Code: Select all

  //reset uhci
   write_uhci_cmd(usb, (read_uhci_cmd(usb) | 0x0004));
   wait(100);
   write_uhci_cmd(usb, (read_uhci_cmd(usb) | 0xFFFB));
You are setting all but bit 2 in the command register with that last line. Also, since you set the bit before hand, all of them are now (presumably) set. Which means the controller is still in reset.

Try:

Code: Select all

  write_uhci_cmd(usb, (read_uhci_cmd(usb) & 0xFFFB));
or

Code: Select all

  write_uhci_cmd(usb, (read_uhci_cmd(usb) & ~0x004));
or better yet

Code: Select all

  write_uhci_cmd(usb, (read_uhci_cmd(usb) & ~(1<<4)));
or even better yet

Code: Select all

  write_uhci_cmd(usb, (read_uhci_cmd(usb) & UHCI_CNTRLR_RESET_MASK));
As for your code working in Bochs with the reset bit still set, this might be something I need to investigate. I don't remember if I actually check for the bit still set. My code probably just assumes you will clear it.

Yep. See Line 382.

Looks like a patch is in order.

[Edit: patch applied 30Mar2020]

Ben
- http://www.fysnet.net/the_universal_serial_bus.htm
Last edited by BenLunt on Mon Mar 30, 2020 5:45 pm, edited 1 time in total.
Klakap
Member
Member
Posts: 297
Joined: Sat Mar 10, 2018 10:16 am

Re: UHCI ports aren't enabled on real hardware

Post by Klakap »

Thank you! It was problem. But on real hardware nothing change. In command port is still set Global Susupent and Global Reset. I have new problem too. Interrupt transfers dont work. Previously, when the global reset bit in bochs was set, interrupt transfers worked with: in and DATA0 or DATA1. Its strange, because in UHCI spec is write that uhci support only IN, OUT and SETUP packages. Now IN is TD status 0x04400007 and nothing is transfer. In QEMU is TD status 0x00040000 . Please what I am doing wrong? https://github.com/Klaykap/CelerOS/blob ... usb_uhci.c (code is in progres, so I have some things commented)

I use code with:

Code: Select all

uhci_reset(0);
uhci_packet_get_configuration(0);
uhci_packet_in(0);
//EDIT:
I solve one thing, I didn'ŧ send setup packet SET_ADDRESS. After it, in QEMU is TD status 0x00400000. But it is still error.
User avatar
BenLunt
Member
Member
Posts: 941
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

Re: UHCI ports aren't enabled on real hardware

Post by BenLunt »

I took a quick look over your code and nothing jumps out at me except for line 19

Code: Select all

#define MEM_TD(usb, td) ( (uint32_t)uhci_td + (usb * 131072) + (td * 32) )
That just looks funny to me. 128k of RAM per TD?

Since you are able to use Bochs, can you set the debug messages on? Either by compiling them in or through the Config button at the top of the "session".

This will tell you what is going on.

Ben
Klakap
Member
Member
Posts: 297
Joined: Sat Mar 10, 2018 10:16 am

Re: UHCI ports aren't enabled on real hardware

Post by Klakap »

I have this debug result:

Code: Select all

00159456993d[UHCI ] register read from address 0xC020:  0x00000000 (16 bits)
00159457032d[UHCI ] register write to  address 0xC020:  0x00000000 (16 bits)
00159457032d[UHCI ] Schedule bit clear in Command register
00159457117d[UHCI ] read  PCI register 0x04 value 0x02800005
00159457205d[UHCI ] write PCI register 0x04 value 0x00000005
00159457240d[UHCI ] register write to  address 0xC020:  0x00000004 (16 bits)
00159457240d[UHCI ] Global Reset
00159457240d[UHCI ] Schedule bit clear in Command register
00159845309d[UHCI ] register read from address 0xC020:  0x00000004 (16 bits)
00159845348d[UHCI ] register write to  address 0xC020:  0x00000000 (16 bits)
00159845348d[UHCI ] Schedule bit clear in Command register
00159845383d[UHCI ] register write to  address 0xC020:  0x00000002 (16 bits)
00159845383d[UHCI ] Schedule bit clear in Command register
00160236202d[UHCI ] register read from address 0xC020:  0x00000000 (16 bits)
00160236241d[UHCI ] register write to  address 0xC020:  0x00000000 (16 bits)
00160236241d[UHCI ] Schedule bit clear in Command register
00160236315d[UHCI ] write PCI register 0xc0 value 0x00008f00
00160244019d[UHCI ] register write to  address 0xC030:  0x00000000 (16 bits)
00160244019d[UHCI ] write to port #1 register bit 7 = 0
00160244049d[UHCI ] register write to  address 0xC032:  0x00000000 (16 bits)
00160244049d[UHCI ] write to port #2 register bit 7 = 0
00160244079d[UHCI ] register write to  address 0xC034:  0x00000000 (16 bits)
00160244079e[UHCI ] write to non existant offset 0x14 (port #3)
00160244272d[UHCI ] register write to  address 0xC024:  0x00000000 (16 bits)
00160244315d[UHCI ] register write to  address 0xC020:  0x00000000 (16 bits)
00160244315d[UHCI ] Schedule bit clear in Command register
00160244354d[UHCI ] register write to  address 0xC02C:  0x00000040 ( 8 bits)
00160244389d[UHCI ] register write to  address 0xC028:  0x003CE2C0 (32 bits)
00160244389d[UHCI ] write to frame base register with bits 11:0 not zero: 0x003ce2c0
00160244425d[UHCI ] register write to  address 0xC026:  0x00000000 (16 bits)
00160244461d[UHCI ] register write to  address 0xC030:  0x00000200 (16 bits)
00160244461d[UHCI ] write to port #1 register bit 7 = 0
00160244461i[UHCI ] Port1: Reset
00160634908d[UHCI ] register write to  address 0xC030:  0x00000010 (16 bits)
00160634908d[UHCI ] write to one or more read-only bits in port #1 register: 0x0010
00160634908d[UHCI ] write to port #1 register bit 7 = 0
00161025788d[UHCI ] register read from address 0xC030:  0x000005AB (16 bits)
00161063304d[UHCI ] register write to  address 0xC030:  0x0000000E (16 bits)
00161063304d[UHCI ] write to port #1 register bit 7 = 0
00161063346d[UHCI ] register write to  address 0xC024:  0x0000000F (16 bits)
00161063346d[UHCI ] Host set Enable Interrupt on Short Packet
00161063346d[UHCI ] Host set Enable Interrupt on Complete
00161063346d[UHCI ] Host set Enable Interrupt on Resume
00161063383d[UHCI ] register write to  address 0xC020:  0x000000D1 (16 bits)
00161063383d[UHCI ] Schedule bit set in Command register
00161139159d[UHCI ] register write to  address 0xC020:  0x000000C1 (16 bits)
00161139159d[UHCI ] Schedule bit set in Command register
00161530039d[UHCI ] register read from address 0xC022:  0x00000000 (16 bits)
00161596847d[UHCI ] register read from address 0xC020:  0x000000C1 (16 bits)
00161596886d[UHCI ] register write to  address 0xC020:  0x000000C0 (16 bits)
00161596886d[UHCI ] Schedule bit clear in Command register
00161597220d[UHCI ] register read from address 0xC020:  0x000000C0 (16 bits)
00161597259d[UHCI ] register write to  address 0xC020:  0x000000C0 (16 bits)
00161597259d[UHCI ] Schedule bit clear in Command register
00161597945d[UHCI ] register write to  address 0xC026:  0x00000001 (16 bits)
00161597979d[UHCI ] register read from address 0xC020:  0x000000C0 (16 bits)
00161598018d[UHCI ] register write to  address 0xC020:  0x000000C1 (16 bits)
00161598018d[UHCI ] Schedule bit set in Command register
Here is setup packet GET_DESCRIPTOR
00162304000d[UHCI ] Frame: 0177 (0x00B1)
00162304000d[UHCI ] QH000:TD found at address: 0x002ACCC0
00162304000d[UHCI ]   002ACCE0   04800000   00E0002D   0038D720
00162304000d[UHCI ]  r_actlen = 0x0008 r_maxlen = 0x0008
00162304000d[UHCI ] Frame: 0177 (0x00B1)
00162304000d[UHCI ] QH000:TD found at address: 0x002ACCE0
00162304000d[UHCI ]   002ACD00   04800000   00E80069   0038D72C
00162304000d[UHCI ]  r_actlen = 0x0008 r_maxlen = 0x0008
00162304000d[UHCI ] Frame: 0177 (0x00B1)
00162304000d[UHCI ] QH000:TD found at address: 0x002ACD00
00162304000d[UHCI ]   002ACD20   04800000   00E00069   0038D734
00162304000d[UHCI ]  r_actlen = 0x0008 r_maxlen = 0x0008
00162304000d[UHCI ] Frame: 0177 (0x00B1)
00162304000d[UHCI ] QH000:TD found at address: 0x002ACD20
00162304000d[UHCI ]   002ACD40   04800000   00E80069   0038D73C
00162304000d[UHCI ]  r_actlen = 0x0008 r_maxlen = 0x0008
00162304000d[UHCI ] Frame: 0177 (0x00B1)
00162304000d[UHCI ] QH000:TD found at address: 0x002ACD40
00162304000d[UHCI ]   002ACD60   04800000   00E00069   0038D744
00162304000d[UHCI ]  r_actlen = 0x0008 r_maxlen = 0x0008
00162304000d[UHCI ] Frame: 0177 (0x00B1)
00162304000d[UHCI ] QH000:TD found at address: 0x002ACD60
00162304000d[UHCI ]   002ACD80   04800000   00E80069   0038D74C
00162304000d[UHCI ]  r_actlen = 0x0002 r_maxlen = 0x0008
00162304000d[UHCI ] Frame: 0177 (0x00B1)
00162304000d[UHCI ] QH000:TD found at address: 0x002ACD80
00162304000d[UHCI ]   00000001   04800000   00E800E1   0038D74C
00162304000d[UHCI ]  r_actlen = 0x0008 r_maxlen = 0x0008
00162304047d[UHCI ] register read from address 0xC020:  0x000000C1 (16 bits)
00162304086d[UHCI ] register write to  address 0xC020:  0x000000C0 (16 bits)
00162304086d[UHCI ] Schedule bit clear in Command register
00162731811d[UHCI ] register read from address 0xC020:  0x000000C0 (16 bits)
00162731850d[UHCI ] register write to  address 0xC020:  0x000000C0 (16 bits)
00162731850d[UHCI ] Schedule bit clear in Command register
00162732080d[UHCI ] register write to  address 0xC026:  0x00000000 (16 bits)
00162732114d[UHCI ] register read from address 0xC020:  0x000000C0 (16 bits)
00162732153d[UHCI ] register write to  address 0xC020:  0x000000C1 (16 bits)
00162732153d[UHCI ] Schedule bit set in Command register
Here is setup packet SET_ADDRESS
00163440000d[UHCI ] Frame: 0176 (0x00B0)
00163440000d[UHCI ] QH000:TD found at address: 0x002ACF20
00163440000d[UHCI ]   002ACF40   04800000   00E0002D   0038D720
00163440000d[UHCI ]  r_actlen = 0x0008 r_maxlen = 0x0008
00163440000d[UHCI ] Frame: 0176 (0x00B0)
00163440000d[UHCI ] QH000:TD found at address: 0x002ACF40
00163440000d[UHCI ]   00000001   04800000   00E80069   0038D720
00163440000d[UHCI ]  r_actlen = 0x0000 r_maxlen = 0x0008
00163440042d[UHCI ] register read from address 0xC020:  0x000000C1 (16 bits)
00163440081d[UHCI ] register write to  address 0xC020:  0x000000C0 (16 bits)
00163440081d[UHCI ] Schedule bit clear in Command register
00163529438d[UHCI ] register read from address 0xC020:  0x000000C0 (16 bits)
00163529477d[UHCI ] register write to  address 0xC020:  0x000000C0 (16 bits)
00163529477d[UHCI ] Schedule bit clear in Command register
00163529622d[UHCI ] register write to  address 0xC026:  0x00000002 (16 bits)
00163529656d[UHCI ] register read from address 0xC020:  0x000000C0 (16 bits)
00163529695d[UHCI ] register write to  address 0xC020:  0x000000C1 (16 bits)
00163529695d[UHCI ] Schedule bit set in Command register
Here is IN packet
00164236000d[UHCI ] Frame: 0178 (0x00B2)
00164236000d[UHCI ] QH000:TD found at address: 0x002ACDE0
00164236000d[UHCI ]   00000001   04800000   00E18169   0038D770
00164236000d[UHCI ]  r_actlen = 0x0008 r_maxlen = 0x0008
00164236000d[UHCI ]  [stalled] We want it to fire here (Frame: 0178)
00164257257d[UHCI ] register read from address 0xC022:  0x00000003 (16 bits)
00164257297d[UHCI ] register write to  address 0xC022:  0x00000002 (16 bits)
00164257433d[UHCI ] register read from address 0xC020:  0x000000C1 (16 bits)
00164257472d[UHCI ] register write to  address 0xC020:  0x000000C0 (16 bits)
00164257472d[UHCI ] Schedule bit clear in Command register
User avatar
BenLunt
Member
Member
Posts: 941
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

Re: UHCI ports aren't enabled on real hardware

Post by BenLunt »

I count seven (7) TDs for your Get Descriptor transaction. Why seven? If you have one for the SETUP at the beginning, and one OUT at then end, that leaves five IN packets. Five times eight is 40 bytes. A standard Device Descriptor has a max length of 18 bytes. Somewhere along the lines, one of your TD's (the third IN packet) will SPD. At the SPD, you must move directly to the OUT packet or the remaining IN packets will STALL.

You must create your schedule so that on a SPD, the controller stops execution in the vertical direction and moves to the Horizontal pointer of the Queue Head, which should contain the ending zero length OUT packet. This way, none of the "extra" IN packets get executed.

Code: Select all

[QH:HORZ]  ----> [Ending zero length OUT packet.  AKA: Status packet]
[QH:VERT]   \----------------------\
   |                               |
   v                               |
  [TD0] --> [TD1] --> [TD2] --> [TD3] --> [TD4]--> [TDn]
 (SETUP)     (IN)      (IN)      (IN)      (IN) ... (OUT)
In the (crude) example above, the controller will start with the VERT pointer, execute TD0 (setup), TD1 (eight bytes), TD2 (eight bytes), TD3 (two bytes and SPD), stop execution in the Vertical direction and move back to the Horizontal pointer, which now points to the ending OUT packet. TD3 through TDn never get executed. This is the whole design of the UHCI stack.

Does that make any sense?

With your current code, after the 7th TD of the Get Descriptor call, the "stack" is in the STALL state. As soon as you get to the SET_ADDRESS call, the SETUP packet will clear the stall. This is a good thing, however, at the same time, the device is still expecting the OUT packet from the previous call. Therefore, a SET_ADDRESS command might fail, in your case it most likely is.

It is good practice, as far as the UHCI is concerned, to always request 255 bytes for every Control request, allowing the controller to SPD (Short Packet Detect) and move to the Horizontal pointer and the ending IN or OUT packet, AKA the STATUS packet.

Your interrupt routine should process the URB (URB = USB Request Block = All of the TDs from SETUP to STATUS) and return the count of bytes actually transferred, while removing this queue from the schedule.

Hope all of this makes sense. My book explains this in great detail. It also explains that after a reset, you should only request the first 8 bytes of the Device Descriptor, do another reset, then request all 18 bytes, all *before* trying to set the address of the device. Even though the design specifies that all 18 are available, there are devices that will only return the first 8 after a (first) reset.

At first, dealing with the USB, whether it be UHCI or xHCI, it can be quite frustrating. However, after you get a good working knowledge of the hardware, it actually becomes addicting, which I won't admit at all that I am addicted. Nope, won't do it :-)

Hope this helps,
Ben
Klakap
Member
Member
Posts: 297
Joined: Sat Mar 10, 2018 10:16 am

Re: UHCI ports aren't enabled on real hardware

Post by Klakap »

Oh, sorry for my mistake, I didn't take GET_DESCRIPTOR but GET_CONFIGURATION. Sorry. I read 40 bytes because it return interface and endpoints. Probably it isnt good idea, but I dont have better. For QH, I cant get it works, so I don't use it. But now after your answer I think I must try to get it works. Is it right? Need I QH for UHCI transfers?
User avatar
BenLunt
Member
Member
Posts: 941
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

Re: UHCI ports aren't enabled on real hardware

Post by BenLunt »

If you request the exact correct amount of bytes for each and every transfer, *and* there will never be a SPD (Short Packet), *and* there will never be a stall, then, no you don't need Queue heads. But that is like digging a hole with only the spade part of the shovel. It can be done, but the handle sure helps a lot.

If you set up the UHCI controller and your Stack correctly, remembering to use Depth first, it will move to the Horizontal Pointer of the Queue Head on a any error, which includes a SPD. This makes transfers much easier.

So yes, my answer is: You must use Queue Heads.... :-)

Ben
Post Reply