EHCI HID Problems

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
15 posts • Page 1 of 1
WinExperements
Member
Member
Posts: 105
Joined: Thu Jul 14, 2022 9:45 am
Contact:

EHCI HID Problems

Post by WinExperements »

Hello! So i want to make USB HID Keyboard driver, i wrote some test code and it seems like to work on both QEMU and Bochs, the data changes when keys are pressed, but it didn't work on the real hardware, with some output debugging the TD token ACTIVE bit is always set, it never clears, i think that i misunderstood how to correctly put the Queue Head into the periodic scheduling table. The table at controller initialization is pointing to one single empty queue head(all links are set to PTR_TERMINATE).

Here is my code for the device interrupt request:

Code: Select all

bool EhciDevIntr(UsbPipe *pipe, int len, void *obuff) {
 UsbDevice *dev = (UsbDevice *)pipe->usbDev;
 EhciController *ctrl = (EhciController *)dev->priv_data;
 if (pipe->controller_info == NULL) {
 // allocate and PREPARE the Queue Head and the TD, put them into periodic list.
 EhciTD *td = EhciAllocTD(ctrl);
 if (!td) {
 return false;
 }
 EhciInitTD(td,NULL,dev->last_toggle,USB_PACKET_IN,len,buff);
 EhciQueueHead *qh = EhciAllocQH(ctrl);
 // allocated queue head CAN have the H bit set, remove it.
 if (!qh) {
 td->active = 0;
 return false;
 }
 qh->curLink = PTR_TERMINATE;
 EhciInitQH(qh,td,false,dev->speed,dev->addr,dev->in_endp.addr,dev->maxPacketSize,dev);
 qh->caps &= ~(1<<15);
 qh->caps |= (0x01 <<0) | (0x1c << 8);
 qh->qhLinkPtr = PTR_TERMINATE;
 qh->ch |= QH_CH_H;
 int interval = dev->in_endp.interval;
 uint32_t physOfQh = (uint32_t)(uintptr_t)arch_mmu_getPhysical(qh);
 EhciPipeInfo *my_pipe = (EhciPipeInfo *)kmalloc(sizeof(EhciPipeInfo));
 memset(my_pipe,0,sizeof(EhciPipeInfo));
 my_pipe->qh = qh;
 my_pipe->td = td;
 my_pipe->physOfTd = (uint32_t)(uintptr_t)arch_mmu_getPhysical(td);
 pipe->controller_info = my_pipe;
 for (int i = 0; i < 1024; i += (1 << (interval - 1))) {
 uint32_t old = ctrl->periodicList[i];
 qh->qhLinkPtr = old;
 ctrl->periodicList[i] = physOfQh | PTR_QH;
 }
 } else {
 EhciPipeInfo *my_pipe = (EhciPipeInfo *)pipe->controller_info;
 if ((my_pipe->td->token & TD_TOK_ACTIVE) != 0) {
 // kprintf("EHCI says transfer didn't end....(0x%x, qh td = 0x%x)\r\n",my_pipe->td->token,my_pipe->qh->nextLink);
 // stil active, nothing to process.
 kprintf("token still active!\r\n");
 return false;
 }
 EhciInitTD(my_pipe->td,NULL,dev->last_toggle,USB_PACKET_IN,len,buff);
 my_pipe->qh->nextLink = (my_pipe->physOfTd);
 my_pipe->qh->caps &= ~(1 << 15);
 my_pipe->qh->qhLinkPtr = PTR_TERMINATE;
 }
 dev->last_toggle ^= 1;
 return true;
}
The endpoint address is correct
VSlezak
Member
Member
Posts: 337
Joined: Sat Mar 10, 2018 10:16 am

Re: EHCI HID Problems

Post by VSlezak »

How is your device responding before setting up periodic schedule? Did you successfully set address and get descriptors?

Also, it would be more useful if you would dump here how does your QH and TD look like on real hardware right before inserting into periodic schedule, because it is not easy to understand what exactly did you set up from this piece of code only.
WinExperements
Member
Member
Posts: 105
Joined: Thu Jul 14, 2022 9:45 am
Contact:

Re: EHCI HID Problems

Post by WinExperements »

[引用]
VSlezak wrote: Fri Aug 29, 2025 12:51 pm How is your device responding before setting up periodic schedule? Did you successfully set address and get descriptors?
Yes, i can successfully retrive the descriptors and enable the numlock on the keyboard.

The keyboard itself is connected to the internal hub.

Queue head on real HW:

Code: Select all

qhLinkPtr = 0x1		ch = 0x5008d204
caps = 0x40021c01	curLink = 0x1
nextLink = 0x8ea000	alterLink = 0x1
token = 0x0
And the TD:

Code: Select all

link = 0x1	alterLink = 0x1	token = 0x80d80
VSlezak
Member
Member
Posts: 337
Joined: Sat Mar 10, 2018 10:16 am

Re: EHCI HID Problems

Post by VSlezak »

You did not set Current qTD Link Pointer in QH. This value contains pointer to currently processed TD, so if it is invalid, controller will probably completely skip transaction of QH. You should set this value to start of your TD.

You set Hub Port to 0. Such port do not exist, because ports on Hub are 1-based, so you need to fix this value (probably to 1 in this case).

Also, you have Head of Reclamation flag bit set, that is used only in asychronous list, so you should leave it clear in periodic list.

And you did not set Interrupt On Transfer bit in TD, so even if it is transferred, you won't be notified about it.
WinExperements
Member
Member
Posts: 105
Joined: Thu Jul 14, 2022 9:45 am
Contact:

Re: EHCI HID Problems

Post by WinExperements »

[引用]
VSlezak wrote: Sat Aug 30, 2025 5:25 am
because ports on Hub are 1-based
Yes..i haved a bug inside my code that would set incorrect port id, but main problem still doesn't fixed, as you metioned earlier, my curLink pointed to 0x1, now it points to the physical address of my TD. After some tests, the qhLinkPtr in the queue head changes after some time(couple of ms) from 0x1 to 0x8e8062, so i think the controller picked up my QH...TD still have ACTIVE bit not cleared, here is some values that i changed from previous post:

Code: Select all

caps = 0x41821c01
ch = 0x50085204
[引用]
VSlezak wrote: Sat Aug 30, 2025 5:25 am And you did not set Interrupt On Transfer
Well, currently i wanted to test this with polling of the completion of the transfer.
VSlezak
Member
Member
Posts: 337
Joined: Sat Mar 10, 2018 10:16 am

Re: EHCI HID Problems

Post by VSlezak »

[引用]
WinExperements wrote: Sat Aug 30, 2025 11:49 am

Code: Select all

caps = 0x41821c01
ch = 0x50085204
Just to be sure, by caps you mean data that is at offset 0x08 at QH, and by ch you mean data that is at offset 0x04 at QH?
[引用]
WinExperements wrote: Sat Aug 30, 2025 11:49 am After some tests, the qhLinkPtr in the queue head changes after some time(couple of ms) from 0x1 to 0x8e8062
Do you mean Queue Head Horizontal Link Pointer? Controller should never write to that field, so if it does, it should be worth to check, if you are pointing to right structures in memory.
WinExperements
Member
Member
Posts: 105
Joined: Thu Jul 14, 2022 9:45 am
Contact:

Re: EHCI HID Problems

Post by WinExperements »

[引用]
VSlezak wrote: Sat Aug 30, 2025 12:29 pm by caps you mean data that is at offset 0x08 at QH, and by ch you mean data that is at offset 0x04 at QH
Yes.

The first entry in the periodic list table points to the physical address of my queue head(0x8e8180 + 0x2), the usbcmd in controller registers is set to 0x10031 so the periodic scheduling is enabled.

About the qhLinkPtr, this is changed by my code, not by the controller...qhLinkPtr now never changes, value is 0x1.
VSlezak
Member
Member
Posts: 337
Joined: Sat Mar 10, 2018 10:16 am

Re: EHCI HID Problems

Post by VSlezak »

Did you set protocol and disable idle? Some keyboards will not work if you do not do that.
WinExperements
Member
Member
Posts: 105
Joined: Thu Jul 14, 2022 9:45 am
Contact:

Re: EHCI HID Problems

Post by WinExperements »

[引用]
VSlezak wrote: Sun Aug 31, 2025 2:10 am Did you set protocol and disable idle? Some keyboards will not work if you do not do that.
Yes i do. I think i incorrectly place the queue head onto the periodic table, so how to correctly do this for full-speed device?
VSlezak
Member
Member
Posts: 337
Joined: Sat Mar 10, 2018 10:16 am

Re: EHCI HID Problems

Post by VSlezak »

There is nothing special about inserting Queue Head to periodic list, you simply put address of Queue Head with type 0b01 to entries, I think you are doing it right.

If it do not work, we need more debugging. Did you disable BIOS legacy support? Is periodic list really running according to Status register? Did you check if controller expects TDs to be in 32-bit or 64-bit format?
WinExperements
Member
Member
Posts: 105
Joined: Thu Jul 14, 2022 9:45 am
Contact:

Re: EHCI HID Problems

Post by WinExperements »

[引用]
VSlezak wrote: Sun Aug 31, 2025 6:47 am Did you disable BIOS legacy support? Is periodic list really running according to Status register?
Yes the legacy support is disabled, periodic list i think running, since if i link my queue head to the global queue head of the periodic list(list is initialized by pointing to single empty QH) then the controller sets HALT bit on my QTD and returns micro-frame mismatch error, this is reason why i think that something is wrong with my insertion to the list. USBSTS itself has vaue of 0xc008. [引用]
VSlezak wrote: Sun Aug 31, 2025 6:47 am Did you check if controller expects TDs to be in 32-bit or 64-bit format?
Actually no, since i don't really know how to do it, i know that the controller can work in 64 bit mode, but i don't really use this feature so i assume it expects 32 bit format.
VSlezak
Member
Member
Posts: 337
Joined: Sat Mar 10, 2018 10:16 am

Re: EHCI HID Problems

Post by VSlezak »

[引用]
WinExperements wrote: Sun Aug 31, 2025 7:54 am if i link my queue head to the global queue head of the periodic list(list is initialized by pointing to single empty QH) then the controller sets HALT bit on my QTD and returns micro-frame mismatch error
Then you should check if after process of inserting frame you are really changing entries in periodic frame list. Do you use correct pointer?

Also why do you initialize list with empty QH? You can simply set terminate bit on all entries and they won't be executed at all.
[引用]
WinExperements wrote: Sun Aug 31, 2025 7:54 am i assume it expects 32 bit format.
This will not always work, because TD format is read-only attribute. You can get it from capability register at offset 0x08.
WinExperements
Member
Member
Posts: 105
Joined: Thu Jul 14, 2022 9:45 am
Contact:

Re: EHCI HID Problems

Post by WinExperements »

[引用]
VSlezak wrote: Sun Aug 31, 2025 10:02 am Do you use correct pointer?
Yes. And also, played a little bit with the periodic list table(based on the code in your repository that linked in the signature of messages), now when i insert my queue head, the hardware seems to copy the token from QTD and upon debugging the token constantly changes from 0x80d80 to 0x80d82 and back, pressing the key on keyboard does nothing. nextLink also changes to 0x1.

It looks like EHCI controller finally processing my QH but never ends the transaction?
VSlezak
Member
Member
Posts: 337
Joined: Sat Mar 10, 2018 10:16 am

Re: EHCI HID Problems

Post by VSlezak »

This sounds good. It means that controller recognized your QH and TD and tries to process them. nextLink as 0x1 is expected, because it is field copies from TD, where it was set to 0x1. So problem should be in values in QH and TD.

As first thing I would double check if you are really using correct device address, endpoint number, hub address and hub port.

If those values are correct, can you please dump full QH with copied TD and how it changes during time? With that we can further analyze what went wrong.
WinExperements
Member
Member
Posts: 105
Joined: Thu Jul 14, 2022 9:45 am
Contact:

Re: EHCI HID Problems

Post by WinExperements »

[引用]
VSlezak wrote: Sun Aug 31, 2025 12:58 pm If those values are correct
Checked what endpoint address my code tryed to talk to, turns out...it tries to talk to invalid endpoint, yikes. At least we fixed issue where the QTD never been executing, the system now successfully registers the key press. Thanks for helping.
Post Reply

15 posts • Page 1 of 1

Return to "OS Development"