4
\$\begingroup\$

I've had a discussion with some colleagues whether USB is polling-based or interrupt-driven. Some claim USB keyboards involve larger overhead than i.e. PS/2 keyboards due to the requirement of former being polled by the main CPU. Now, with today's typical CPU speeds it would probably be a small amount of CPU-time spent in either case, but it is still interesting to know.

I've tried to find out myself. I suspect the answer could be dependent on the USB host controller interface. I looked specifically at UHCI which is one of the original standards and is rather simple to understand (ftp://ftp.netbsd.org/pub/NetBSD/misc/blymn/uhci11d.pdf). Based on my reading of this, it seems it could be argued both ways whether USB devices are polling based or not.

So here some facts I've gathered from reading the UHCI spec:

1) It's true a USB device (at least prior to USB 3.0 which is what applies to most/all keyboards) can't actively send an interrupt. They can only answer a query whether there is a pending interrupt. Hence, this query has to be sent periodically.

So far, point 1 suggests it is indeed polling-based. But then we have:

2) It is not the CPU itself which sends the queries, but rather the host controller.

3) The main CPU does, however, have to program the host controller, by laying out a detailed schedule of what happens in each ms.

4) The UHCI host controller can send interrupts to the main CPU to alert it of completed transfers. Hence, the main CPU doesn't need to poll for completion of transfers.

So now it would seem (and this is what some colleagues argued) that from the point-of-view of the main CPU, USB isn't polling based anyway. It is the host controller that periodically polls, and then delivers the interrupts to the main CPU. This wouldn't be so bad, because the host controller doesn't have other jobs, so it's not a problem it is doing this polling.

But unfortunately, I don't think this is really the case after all, because:

5) According to my reading of UHCI, there is no way the main CPU can configure a UHCI controller and USB keyboard such that once it is configured, the main CPU will not have to do further work until something happens, e.g. the user presses a key and the keyboard sets a pending interrupt which is delivered to the host controller and ultimately host as specified above. According to my reading, the main CPU has to continuously "work" to keep it up and running. There are as I see it two reasons for this:

  • Transfer descriptors have an Interrupt-on-completion bit that can be set so that the main CPU is interrupted upon completion of the transfer. So far so good, but unfortunately there's no way to differentiate between positive and negative answers to interrupt queries, so this means the main CPU will be interrupted every time a device is queried about interrupt status, no matter whether it has a pending interrupt or not.
  • The schedule cannot be reused. Each transfer descriptor has an ACTIVE bit that must be set to 1 for the host controller to execute it. The host controller sets it 0 after it has been executed. As a result, the main CPU will have to periodically go through the transfer descriptors and set them back to active, so that they will be executed again.

So even though it seems UHCI has the potential to deliver interrupts "for free" to the main CPU, it seems the above two points ruin it and means that even if the main CPU doesn't have to be engaged in out-right polling of the keyboard, it still has to periodically reschedule the polling, even in the case of no events. Which means it is sort of engaging in polling.

I'm really interested if someone could validate these observations, especially the two bullet points above. Bonus points if someone can point out how OHCI, EHCI, and XHCI differ in these aspects!

\$\endgroup\$
  • \$\begingroup\$ I may be wrong, but I agree with most of your analysis. USB relies on the host polling the device. The USB host controller could do the polling autonomously, using relatively simple electronics which receives a device packet. AFAIK, one USB host controller can talk to multiple USB devices. So it might be able to raise one interrupt to the host CPU when it has polled all of the USB devices. That might reduce the overhead, and increase the likelihood that a USB device needs servicing. Less clear is whether or not the host's USB controller can discard 'I have nothing' USB device responses. \$\endgroup\$ – gbulmer Aug 18 '14 at 13:10
  • \$\begingroup\$ @gbulmer: I agree. The only thing I'm certain about is this aspect of whether the 1) TD's for interrupt transfers have their active bit set even with negative answers, 2) the CPU is interrupted in case of negative answers. Depending on the answers to these questions, the o/s driver could set things up so everything would fly on autopilot, and would only spend time on USB in case of actual events from USB-devices. I may have to try out myself since these aspects are not 100% clear from the UHCI spec. \$\endgroup\$ – Morty Aug 18 '14 at 13:22
  • \$\begingroup\$ Also, that spec sheet is almost 18 years old. I can imagine things have changed significantly since then because USB polling frequency has increased a lot. \$\endgroup\$ – gbulmer Aug 18 '14 at 13:45
1
\$\begingroup\$

The key point is that normally, the interrupt transfers are not considered complete until some data is received. beyondLogic's great USB protocol docs say:

If an interrupt has been queued by the device, the function will send a data packet containing data relevant to the interrupt when it receives the IN Token. [...] If on the other hand an interrupt condition was not present when the host polled the interrupt endpoint with an IN token, then the function signals this state by sending a NAK.

For your specific controller, the table on page 22 in your doc talks about retry counter in 28:27. If you look at the table, you'd notice that "NAK received" does not decrement it! So as long as you set at least one retry, the controller will retry indefinitely on NAKs, without bothering the host.


But there is a much simpler way to check this -- find any Linux system with USB keyboard, and check the interrupt counter:

watch -d -n 0.5 cat /proc/interrupts

On my system, there is a line which says "IR-PCI-MSI 327680-edge xhci_hcd", and it has non-changing number if I am not touching mouse or keyboard. Moving the mouse causes this number to increase. So this is clearly interrupt based.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.