UHCI keeps firing interrupts

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
Arpanoid
Posts: 20
Joined: Sun May 27, 2018 8:43 pm

UHCI keeps firing interrupts

Post by Arpanoid »

Hello,

I'm learning USB driver development using Bochs, focusing for a while on a single virtual USB-key. Here's the bochs config line

Code: Select all

usb_uhci: port1=disk, options1="path:usbstick.img, sect_size:1024, size:1073479680"
Currently, all the entries in my frame list point to the same queue head at physical address 0x081fdf60. I inserted a device descriptor request into this queue and interrupts keep firing endlessly. I don't understand why. I'd expect it should fire just once.
Here's the contents of the memory before the first interrupt:

Code: Select all

    0x081fdf60: 0x00000001 0x081fdf70 0x00000000 0x00000000
    0x081fdf70: 0x081fdf84 0x19800000 0x00e0002d 0x081fdfa0
    0x081fdf80: 0x081fdf94 0x18800000 0x00e80069 0x081fdfa8
    0x081fdf90: 0x00000001 0x18800000 0xffe800e1 0x00000000
    0x081fdfa0: 0x01000680 0x00080000 0x00000000 0x00000000
And after the first interrupt:

Code: Select all

    0x081fdf60: 0x00000001 0x00000001 0x00000000 0x00000000
    0x081fdf70: 0x081fdf84 0x19000007 0x00e0002d 0x081fdfa0
    0x081fdf80: 0x081fdf94 0x18000007 0x00e80069 0x081fdfa8
    0x081fdf90: 0x00000001 0x180007ff 0xffe800e1 0x00000000
    0x081fdfa0: 0x01000680 0x00080000 0x01010112 0x40000000
I even tried to clear the IOC bit in the first TD in the interrupt handler, but this didn't help neither.

I can't understand what exactly I'm doing wrong and would appreciate any suggestions.
nullplan
Member
Member
Posts: 2051
Joined: Wed Aug 30, 2017 8:24 am

Re: UHCI keeps firing interrupts

Post by nullplan »

Have you read the USBSTS register to figure out why the interrupt is being generated?
Carpe diem!
Arpanoid
Posts: 20
Joined: Sun May 27, 2018 8:43 pm

Re: UHCI keeps firing interrupts

Post by Arpanoid »

nullplan wrote: ↑Sun Sep 14, 2025 12:31 pm Have you read the USBSTS register to figure out why the interrupt is being generated?
Here's the register values. The first column - just before inserting the queue into the QH. The second column - when the first interrupt is fired.

Code: Select all

USBCMD		| 0x0001		| 0x0001
USBSTS		| 0x0000		| 0x0001
USBINTR		| 0x000f		| 0x000f
FRNUM		| 0x0000		| 0x0002
FRBASEADD	| 0x081fe000		| 0x081fe000
SOFMOD		| 0x40			| 0x40
PORTSC1		| 0x0095		| 0x0095
PORTSC2		| 0x0080		| 0x0080
In the subsequent interrupts only the frame number changes. But I noticed a weird thing about the frame number. The first interrupt gives the frame # 2. The second interrupt also gives the frame # 2. Only starting from the third interrupt the frame numbers get incrementing. I don't understand why and if it's related.

As for the USBSTS, the value 0x0001 looks ok.
User avatar
BenLunt
Member
Member
Posts: 1039
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

Re: UHCI keeps firing interrupts

Post by BenLunt »

The UHCI will fire an interrupt at the end of the frame if any executed TD has the IOC bit set, whether the TD was active or not.

Since you keep getting interrupts, your schedule is executing a TD (valid or not) that has the IOC bit set. If you don't remove the executed TD from the schedule, the TD will be executed again, even though it is inactive, and an interrupt will fire at the end of that frame.

If that is not the case, there are a few other reasons, one being if your schedule runs off into "no man's land" and any of the dwords it tries to execute have the IOC bit set, it will fire an interrupt. What does your schedule do when all TDs have been executed in that frame? Do you have the T bit set in the last QUEUE or TD executed?

To counter this, all TDs (not counting ISOs) should be in a QUEUE. Once the TD is executed and an interrupt is fired, you remove the QUEUE from the schedule and the controller will not execute that TD again.

Ben
- https://www.fysnet.net/the_universal_serial_bus.htm
User avatar
BenLunt
Member
Member
Posts: 1039
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

Re: UHCI keeps firing interrupts

Post by BenLunt »

How about a few more notes:

1) When removing a Queue from the schedule, you *must* first write a valid address (or mark the existing address as TERMinated) to the schedule, physically removing the Queue from the linked list, *before* removing or altering the Queue or any TDs within it. The schedule must remain intact throughout the frame before you alter any part of that Queue or list of TDs. Imagine freeing the memory occupied by the Queue and TD(s) before writing a valid pointer to the previous Queue Head, removing the Queue from the linked list. This is where you can get into "no man's land" real quick.

2) Do you use Breadth first or Depth first scheduling? If you use Breadth first, the controller will execute only one TD per Queue per Frame. 1023 frames will be executed before the controller comes back to this Frame. This will make for extremely slow transfers unless you have multiple frames point to the same schedule. Even then, the controller will only execute one TD per Queue per millisecond, still making for slow transfers.

3) Whether you use Breadth first or Depth first scheduling, the controller will update the Vertical entry of the Queue with the Next Pointer value from the last executed TD in that Queue. If Breadth first, the controller will then move to and execute any other Queues in your schedule before coming back to this one. If Depth first and time allows, the controller will then execute the Vertical entry in the current Queue, which now points to the next TD in your linked list of TDs. Therefore, any previous TD in this linked list of TDs (as long as it is within a Queue), are no longer reachable by the controller and will not get executed again. With this in mind, after the last TD in the list of TDs is executed, and the controller updates the Vertical entry of the Queue, this Veritical entry should now have the T bit set, since the last TD in the list had the T bit set. Therefore, in theory, your TDs will never be executed again, as long as you follow this design.

4) Do you have a working toggle bit mechanism? The controller must see a valid toggle bit sequence for any pipe or it will stall the pipe. When a stall is found, the Vertical entry is not updated(1), and the TD can and will be tried again on the next go around, in turn firing the interrupt because the IOC bit was set.

If you follow the above notes, none of your TDs will ever be executed again. Therefore, in your current situation, you must have a bad link, a TD not within a Queue, or you are running in "no man's land" somewhere.

Hope that helps,
Ben

(1) That statement is from memory, I would have to check my notes to verify it is true, but I believe it to be.
Arpanoid
Posts: 20
Joined: Sun May 27, 2018 8:43 pm

Re: UHCI keeps firing interrupts

Post by Arpanoid »

I tried to do some checks to eliminate some hypothetical causes. One of the things I did - when handling the interrupt, I cleared the bit 0 in the USBSTS. Surprisingly, it helped - no more interrupts were fired.

Well... Sure, it makes sense that the driver should clear the status register after having handled the current status. But I can't recall having seen anywhere in the UHCI specifications that not clearing that bit will cause the interrupts being fired over and over again.... I wonder if this behavior is vendor specific.
User avatar
BenLunt
Member
Member
Posts: 1039
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

Re: UHCI keeps firing interrupts

Post by BenLunt »

I'm glad you figured it out. Sorry I didn't think to mention that you must clear the status. Most if not all hardware with this kind of status register requires you to clear it after an interrupt, and since you mentioned the status bit in a previous post, I assumed you had done so.

Anyway, glad you figured it out.
Post Reply