BenLunt wrote:thewrongchristian wrote:That seems like a bit of a faff, as well as a potential waste of peak device bandwidth.
According to the spec, any link fields are read-only to controller, so the controller will not be modifying any link fields., So long as updates to the link fields are atomic, that should suffice for the controller.
In my code, for each transfer, I build offline a QH with however many TD are required to transfer all the data for the request. Being offline, and not linked into the UHCI schedule, this will not be dependent on the when the UHCI reads data based on the schedule.
Say you want to insert the new QH2 AFTER QH1, you fill in QH2.next pointer with the QH1.next, so that both QH1 and the new QH2 point to the same next QH (or null, if there is nothing else on the end of the list.)
Then, you update QH1.next to point to QH2 in a singe write. This write is (should be) atomic, so the UHCI will either get the old value, or the new value, but either way, the chain of QH should be coherent, and being read-only to the UHCI, it will not be updated by the UHCI controller.
Hi,
I don't think you understood what I was trying to express. What If the controller has read QH1 just an instant before "
you update QH1.next to point to QH2 in a singe write"? The controller will not see the update until the next time it comes around to see QH1 again, if it even does come back to QH1. Therefore, QH2 may possibly *never* get executed. Also, depending on how you construct your schedule, it could be 1023 frame times before it comes back to QH1, nearly a full 1024mS later.
My point is, and this is my humble opinion, software should not modify any QH (or TD) that can possibly be executed within the current frame. Always modify QHs and TDs that are guaranteed to not be executed until the next frame.
As for loss of peak device bandwidth, this technique loses no bandwidth as long as it is done correctly.
For example, all Frame List Pointers should point to an arbitrary count of ISO TDs, active or not, then a list of periodic QHs depending on the Frame Number value, then an arbitrary list of Bulk QHs, and finally an arbitrary list of Control QHs. The last Control QH should point to the first Bulk QH for Bulk/Control reclamation. Depending on your preference, the Control QHs can come first.
Having two complete lists as described in my previous post does not have any loss in bandwidth since any added QH, when inserted correctly, is guaranteed (errors aside) to be executed in the next frame.
Ben
So you're advocating a "current" list and a "next" list? So, only insert new QH onto the "next" list, which presumably will flip-flop each frame.
The problem with that is, how do you know what the controller considers the "current" frame? You can read the current frame number being processed from the UHCI FRNUM register. From that, you can derive the current and next frames.
But, reading FRNUM and acting on it is not atomic. You've inherently got a race between the driver and the controller, because by the time you've determined what your "next" frame list is, an interrupt could have happened and the UHCI controller has moved onto the next frame without you realising, so you'd be putting the QH on the now "current" frame, and you'd better do it in a manner that is coherent.
My actual QH list has a static QH list for the various interrupts (128ms at the head of the list, through 64ms, 32 ms etc down to, 1ms, then a control QH, then the bulk QH (I haven't yet added ISO support.):
Code: Select all
qh128ms -> qh64ms ... qh2ms -> qh1ms -> qhcontrol -> qhbulk
Then off each of these static QH, there is a dynamic QH list for outstanding requests for that queue type. So, for example, consider the following two 1ms interrupt transfers queued before the control descriptors:
Code: Select all
->qh1ms ------> qhcontrol
| /
\->QH1->QH2-/
| |
TD TD
Say QH1 is to the keyboard, and gets a NACK, but QH2 is for the mouse, and it gets an interrupt. When we process QH2, it will be removed from the list, resulting in:
Code: Select all
->qh1ms ------> qhcontrol
| /
\->QH1-----/
|
TD
My point being, from the point of view of the controller, I see either the first or the second scenario, but both scenarios are coherent valid lists of QH, and will be processed in the right order each frame. Of course, if for some reason the QH2 interrupt handler has not yet removed QH2, and the controller again visits QH1 and QH2, QH2 will be a no-op because its TD are marked as complete. This is the case I currently don't handle correctly, and need to keep the QH2/TD around for another frame in case the controller re-reads QH2, but I know for sure that when I remove QH2, QH1 is still linked into the schedule and so will be visited again.