Simple math time. 10ms timeslice. 30ms frames of data. full duplex plus out-of-band events. 480 byte buffer. 8kHz, 16-bit audio. Why did I think that signal-driven async I/O was a good idea for this phone server? Live and learn, I guess. Anyway, I need to be able to deal with audio channels in my select loop anyway, and all the queueing code can simply stay, sans locking cruft.
In better news, as it turns out, the Telex headset Just Works. Limited to 22kHz, apparently, but otherwise very nice (it does full-duplex wonderfully, and speech recognition performance is better than any other input device I've tried).
Unfortunately, my D-Link USB hub with built-in PS/2 adaptor Just Does Not Work (though Win98 has no problems with it). Control messages sent to it appear to result in timeout/CRC errors, which means that over-current changes for its built-in devices can't be cleared, which results in a never-ending flood of console messages and no actual data from said devices getting through. The Linux-USB mailing lists have been most unresponsive. So I guess I will have to bite the bullet, read the USB spec, and play with a sniffer after all.