On Mon, May 17, 2021 at 10:19:59PM +0200, Patrick Wildt wrote: > The thing though is that I thought xhci(4) was already calling syncmem > on that buffer. I mean, it's happening in xhci_device_isoc_start(), > isn't it? It's doing > > usb_syncmem(&xfer->dmabuf, 0, xfer->length, > usbd_xfer_isread(xfer) ? > BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE); > > and unless the usbd_xfer_isread() is wrong, it should be passing > PREWRITE as well. >
AFAICS, the usb_syncmem() call in xhci_device_isoc_start() is correct. The problem is that it's called too early, before the data to transfer is stored to memory. Indeed, xhci_device_isoc_start() doesn't start the transfer, it just queues xfer's frames descriptors on the HC ring; each descriptor is a pointer to 1 ms of audio data that's initialiy zero-filled by uaudio(4). The HC sends on the wire a single frame every millisecond from its ring. After frames are queued, userland can start filling the data pointed by the frame descriptors, so that when the HC dequeues them, the data is ready. If userland misses the deadline (fills the memory after frame is sent), the HC sends the initial frame content (zeros, i.e. silencs), playback "stutters". This is equivalent to how PCI audio hardware works. > Unless xfer->length is different than xfer->size? But then the whole > isoc code in xhci(4) would be wrong, since it relies on xfer->length. > > Oh, maybe that's it. The isoc code mostly works on xfer->frlengths[], > and maybe xfer->length is not what we need to use there. > > Maybe we need to sync each individual frame with the corresponding > frlength? The easier is to sync just after userland stops touching the memory block; FWIW, a block contains multiple frames (10 with default sndiod settings).