diff --git a/us144mkii.h b/us144mkii.h index 6a57e72..c0c62f4 100644 --- a/us144mkii.h +++ b/us144mkii.h @@ -172,6 +172,10 @@ struct tascam_card { unsigned int feedback_urb_skip_count; bool running_ghost_playback; + u8 capture_remainder_buf[63]; + unsigned int capture_remainder_len; + u8 capture_combined_buf[CAPTURE_PACKET_SIZE + 63]; + struct work_struct stop_work; struct work_struct stop_pcm_work; }; diff --git a/us144mkii_capture.c b/us144mkii_capture.c index 035af49..d1c08c1 100644 --- a/us144mkii_capture.c +++ b/us144mkii_capture.c @@ -46,6 +46,7 @@ static int tascam_capture_prepare(struct snd_pcm_substream *substream) tascam->driver_capture_pos = 0; tascam->capture_frames_processed = 0; tascam->last_cap_period_pos = 0; + tascam->capture_remainder_len = 0; return 0; } @@ -144,40 +145,64 @@ static void tascam_decode_capture_chunk(const u8 *src, u32 *dst, int frames) * @urb: the completed URB * * Decodes audio data, updates ring buffer, and handles period elapsed. + * Handles partial 64-byte frames by buffering remainder across URB completions. + * Distinguishes transient USB errors from fatal ones for robustness. */ void capture_urb_complete(struct urb *urb) { struct tascam_card *tascam = urb->context; struct snd_pcm_runtime *runtime; - int frames, part1; + int frames, part1, total_available, new_remainder; unsigned long flags; bool need_period_elapsed = false; + u8 *combined = tascam->capture_combined_buf; - if (urb->status || !tascam || !tascam->dev) + if (!tascam || !tascam->dev) + return; + + if (urb->status) { + if (urb->status == -EOVERFLOW || urb->status == -ENOENT || urb->status == -EPIPE) { + usb_anchor_urb(urb, &tascam->capture_anchor); + if (usb_submit_urb(urb, GFP_ATOMIC) < 0) { + usb_unanchor_urb(urb); + atomic_dec(&tascam->active_urbs); + } + return; + } goto exit; + } if (!tascam->capture_substream || !tascam->capture_substream->runtime) goto exit; runtime = tascam->capture_substream->runtime; - frames = urb->actual_length / 64; + + memcpy(combined, tascam->capture_remainder_buf, tascam->capture_remainder_len); + memcpy(combined + tascam->capture_remainder_len, urb->transfer_buffer, urb->actual_length); + total_available = tascam->capture_remainder_len + urb->actual_length; + + frames = total_available / 64; + new_remainder = total_available % 64; if (frames > 0) { spin_lock_irqsave(&tascam->lock, flags); if (!atomic_read(&tascam->capture_active)) { spin_unlock_irqrestore(&tascam->lock, flags); - goto exit; + tascam->capture_remainder_len = new_remainder; + if (new_remainder > 0) + memcpy(tascam->capture_remainder_buf, combined + (frames * 64), new_remainder); + goto resubmit; } u32 *dma = (u32 *)(runtime->dma_area + frames_to_bytes(runtime, tascam->driver_capture_pos)); if (tascam->driver_capture_pos + frames <= runtime->buffer_size) { - tascam_decode_capture_chunk(urb->transfer_buffer, dma, frames); + tascam_decode_capture_chunk(combined, dma, frames); } else { part1 = runtime->buffer_size - tascam->driver_capture_pos; - tascam_decode_capture_chunk(urb->transfer_buffer, dma, part1); - tascam_decode_capture_chunk(urb->transfer_buffer + (part1 * 64), + tascam_decode_capture_chunk(combined, dma, part1); + tascam_decode_capture_chunk(combined + (part1 * 64), (u32 *)runtime->dma_area, frames - part1); } @@ -191,6 +216,11 @@ void capture_urb_complete(struct urb *urb) spin_unlock_irqrestore(&tascam->lock, flags); } + tascam->capture_remainder_len = new_remainder; + if (new_remainder > 0) + memcpy(tascam->capture_remainder_buf, combined + (frames * 64), new_remainder); + +resubmit: usb_anchor_urb(urb, &tascam->capture_anchor); if (usb_submit_urb(urb, GFP_ATOMIC) < 0) { usb_unanchor_urb(urb); @@ -203,7 +233,7 @@ void capture_urb_complete(struct urb *urb) return; - exit: +exit: usb_unanchor_urb(urb); atomic_dec(&tascam->active_urbs); } diff --git a/us144mkii_midi.c b/us144mkii_midi.c index 134e19e..1a5af7e 100644 --- a/us144mkii_midi.c +++ b/us144mkii_midi.c @@ -14,6 +14,7 @@ static void tascam_midi_out_complete(struct urb *urb) if (urb->status || !sub || !tascam->midi_out_active) { tascam->midi_out_active = false; + usb_unanchor_urb(urb); spin_unlock_irqrestore(&tascam->midi_lock, flags); return; } diff --git a/us144mkii_playback.c b/us144mkii_playback.c index c15368a..e69eca0 100644 --- a/us144mkii_playback.c +++ b/us144mkii_playback.c @@ -207,8 +207,15 @@ static snd_pcm_uframes_t tascam_playback_pointer(struct snd_pcm_substream *subst unsigned long flags; u64 pos; - if (!atomic_read(&tascam->playback_active)) + if (!atomic_read(&tascam->playback_active)) { + if (tascam->running_ghost_playback) { + spin_lock_irqsave(&tascam->lock, flags); + pos = tascam->playback_frames_consumed; + spin_unlock_irqrestore(&tascam->lock, flags); + return (snd_pcm_uframes_t)pos; + } return 0; + } spin_lock_irqsave(&tascam->lock, flags); pos = tascam->playback_frames_consumed;