Make capture work + add full-duplex (record+playback) on modern kernels#8
Open
ljadach wants to merge 1 commit into
Open
Make capture work + add full-duplex (record+playback) on modern kernels#8ljadach wants to merge 1 commit into
ljadach wants to merge 1 commit into
Conversation
Capture never produced data (issues mmm444#1, mmm444#3, mmm444#7): arecord blocked and wrote a 44-byte header-only file. Fixing it uncovered a chain of issues: - Capture URBs were allocated but never submitted (no submit_init_cap_urbs). - Capture URBs lacked URB_NO_TRANSFER_DMA_MAP, so on kernels with the vmalloc-DMA-map check usb_submit_urb fails with -EAGAIN. - cap_urb_complete writes 16 bytes/frame but the stream advertised S24_3LE (12 bytes/frame) via the shared hw, overrunning the buffer -> panic. - In full duplex reset_eie ran from both prepares concurrently and re-submitted active URBs (-EBUSY). - The 24-bit sample was written low-aligned/unsigned, so S32->S16 left noise. Changes: - Add submit_init_cap_urbs(); submit cap URBs from the capture trigger and gate resubmission on CAPTURE_RUNNING so they drain on stop. - Set URB_NO_TRANSFER_DMA_MAP on the capture URBs. - Capture advertises its own S32_LE format (16 bytes/frame) in eie_cpcm_open. - Serialize the reset decision in both prepares under devices_mutex. - Sign-align the decoded 24-bit sample into S32_LE (<< 8). Tested on Linux Mint 22.3, kernel 6.17.0-29-generic: simultaneous playback and 4-channel capture (recorded an overdub), dmesg clean, no crash. cap_urb_complete still lacks the spinlock noted in the TODO; only tested at 48 kHz. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This makes capture/recording work on the Akai EIE Pro - long reported broken (#1, #3, #7) - and enables simultaneous playback + capture (full duplex) on current kernels. Tested on Linux Mint 22.3 / kernel 6.17.0-29-generic: recorded a 4-channel overdub while playing back, no crashes.
The playback path is left unchanged; all changes are on the capture side plus one shared-reset serialization.
Background
Capture never produced data:
arecordblocked forever and wrote a 44-byte (header-only) file. Investigating revealed a chain of issues, each hidden behind the previous one.Root causes & fixes
Capture URBs were never submitted.
init_cap_urbs()allocates them, but nothing ever calledusb_submit_urb()on them - there wassubmit_init_sync_urbs()/submit_init_play_urbs()but no capture equivalent, so no completion ever fired. Addedsubmit_init_cap_urbs()and call it from the capture triggerSTART(by thenreset_eie()from prepare has the sync + playback streams flowing, so the device is clocked and the bulk-in endpoint is live). Resubmission incap_urb_complete()is now gated onCAPTURE_RUNNINGso the URBs drain cleanly on stop.Missing
URB_NO_TRANSFER_DMA_MAPon capture URBs. They useusb_alloc_coherentbuffers but didn't set the flag (the playback/sync URBs do). On kernels with the vmalloc-DMA-map hardening the core then tries to DMA-map the buffer and the submit fails with-EAGAIN(xhci_hcd: rejecting DMA map of vmalloc memory). Set the flag.Capture buffer overflow -> kernel panic.
cap_urb_complete()writesBYTES_PER_FRAME_CAP(16, i.e. 4x__le32) per frame, but capture shared the playbackhwadvertisingS24_3LE= 12 bytes/frame, so ALSA sized the runtime buffer at 12. Once the URBs actually flowed, the 16-byte writes overran the buffer -> memory corruption -> panic. Capture now advertises its ownSNDRV_PCM_FMTBIT_S32_LE(16 bytes/frame) ineie_cpcm_open().Full-duplex reset race. Opening playback and capture near-simultaneously ran
reset_eie()from both*_preparecallbacks concurrently, re-submitting already-active URBs (-EBUSY, "URB submitted while active"). Serialized theif (rate != eie->rate) reset_eie()decision underdevices_mutexin both prepares, so the second stream sees the device already streaming and skips the redundant reset.Capture decoded to noise. The 24-bit sample was written low-aligned and unsigned, so
S32 -> S16downconversion kept only ~8 noisy bits. Sign-align the value intoS32_LE(ch << 8).Testing
6.17.0-29-generic, Akai EIE Pro09e8:0010.dmesgclean, no crash.Known remaining work (honest)
cap_urb_complete()still updatescap_buf_pos/cap_frameswithout the spinlock (the existingTODO); fine in light testing, should be hardened for heavy DAW load.This was debugged with a lot of
dmesg-driven iteration; the commit message lists the same chain. Happy to split into smaller commits if preferred.