I'm developing a low-latency USB audio-MIDI driver using AudioDriverKit and USBDriverKit for macOS and iPadOS.
I've observed occasional delays in USB completion callbacks. The manufacturer's official macOS driver uses IOUSBHost to queue longer isochronous transfers and update their mapped OUT buffers as audio samples become available.
I've reviewed the relevant IOKit and IOUSBHost documentation, but haven't established whether USBDriverKit supports an equivalent approach.
Does USBDriverKit permit modifying portions of a CreateIOBuffer OUT buffer while an IsochIO request is still pending, specifically to supply audio for future USB frames?
If so, what synchronization requirements and controller-prefetch guarantees determine the latest safe time to update those regions?
If this isn't supported, is there a recommended alternative for maintaining low-latency isochronous audio despite delayed completion callbacks?
Thanks.
I’m developing a low-latency USB audio-MIDI driver using AudioDriverKit and USBDriverKit for macOS and iPadOS.
I've observed occasional delays in USB completion callbacks. The manufacturer's official macOS driver uses IOUSBHost to queue longer isochronous transfers and update their mapped OUT buffers as audio samples become available.
I've reviewed the relevant IOKit and IOUSBHost documentation, but haven't established whether USBDriverKit supports an equivalent approach.
Basically, "yes". More specifically, USBDriverKit and the IOUSBHost framework are actually quite similar, in that what both actually "do” is control a USB device by sending commands to the in-kernel USB driver which ACTUALLY controls the device. The main difference/benefit DriverKit provides is the ability to inject services back INTO the kernel, not in the mechanics of how it actually interacts with its provider.
Does USBDriverKit permit modifying portions of a CreateIOBuffer OUT buffer while an IsochIO request is still pending, specifically to supply audio for future USB frames?
Yes, as this is basically the entire "point" of our Isochronous APIs. More specifically, the basic idea here is that what's actually slow/high latency here is:
-
Setting up the chain of commands that you're doing to send.
-
Receiving the completion for any given event.
...while the actual transmission of any given packet happens at interrupt time, basically make it as fast as it's possible to be.
SO, the idea here is that you transfer data by:
-
Scheduling a long series of transfers to occur at specific times "in the future".
-
Backfilling the data into each of those transfers "before" they're actually sent.
-
...then looping back to #1 by scheduling more transfers before #1 finishes, allowing the transfer process to continue indefinitely.
If so, what synchronization requirements and controller-prefetch guarantees determine the latest safe time to update those regions?
I'm not sure what you mean here. The memory you're writing to has already been wired, so it can be modified any time "before" the actual transfer starts. Roughly when that transfer starts can be determined mathematically, but part of the "point" of this architecture is to avoid explicit synchronization between the data producer and the sending driver.
__
Kevin Elliott
DTS Engineer, CoreOS/Hardware