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.
- 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:
The endpoint address is correct
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;
}
Re: EHCI HID Problems
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.
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 »
[引用]
The keyboard itself is connected to the internal hub.
Queue head on real HW:
And the TD:
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
Code: Select all
link = 0x1 alterLink = 0x1 token = 0x80d80
Re: EHCI HID Problems
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.
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 »
[引用]
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:
[引用]
Well, currently i wanted to test this with polling of the completion of the transfer.
Code: Select all
caps = 0x41821c01
ch = 0x50085204
Re: EHCI HID Problems
[引用]
[引用]
WinExperements wrote: ↑ Sat Aug 30, 2025 11:49 am
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?Code: Select all
caps = 0x41821c01
ch = 0x50085204
[引用]
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 »
[引用]
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 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.
Re: EHCI HID Problems
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
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?
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.
[引用]
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.Re: EHCI HID Problems
[引用]
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.
[引用] This will not always work, because TD format is read-only attribute. You can get it from capability register at offset 0x08.
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.
[引用] 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 »
[引用]
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?
It looks like EHCI controller finally processing my QH but never ends the transaction?
Re: EHCI HID Problems
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.
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 »
[引用]
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.