us144mkii: fix moderate bugs - capture robustness, ghost pointer, MIDI cleanup
BUG-09: Buffer partial 64-byte frame remainders across URB completions to prevent silent data loss when USB bulk transfers return incomplete chunks. Adds remainder and combined buffers to tascam_card struct. BUG-10: Distinguish transient USB errors (-EOVERFLOW, -ENOENT, -EPIPE) from fatal ones in capture completion. Resubmit URB on transient errors instead of dropping it, preventing capture stream death on transient USB bandwidth contention or controller glitches. BUG-11: Return playback_frames_consumed from pointer callback during ghost playback instead of always returning 0, providing correct semantics when the callback is queried indirectly. BUG-12: Unanchor MIDI output URB before returning on error path in tascam_midi_out_complete to prevent the URB from remaining stuck in the anchor's pending list.
This commit is contained in:
parent
f6c59c4f9f
commit
ef73de30ec
|
|
@ -172,6 +172,10 @@ struct tascam_card {
|
||||||
unsigned int feedback_urb_skip_count;
|
unsigned int feedback_urb_skip_count;
|
||||||
bool running_ghost_playback;
|
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_work;
|
||||||
struct work_struct stop_pcm_work;
|
struct work_struct stop_pcm_work;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ static int tascam_capture_prepare(struct snd_pcm_substream *substream)
|
||||||
tascam->driver_capture_pos = 0;
|
tascam->driver_capture_pos = 0;
|
||||||
tascam->capture_frames_processed = 0;
|
tascam->capture_frames_processed = 0;
|
||||||
tascam->last_cap_period_pos = 0;
|
tascam->last_cap_period_pos = 0;
|
||||||
|
tascam->capture_remainder_len = 0;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -144,40 +145,64 @@ static void tascam_decode_capture_chunk(const u8 *src, u32 *dst, int frames)
|
||||||
* @urb: the completed URB
|
* @urb: the completed URB
|
||||||
*
|
*
|
||||||
* Decodes audio data, updates ring buffer, and handles period elapsed.
|
* 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)
|
void capture_urb_complete(struct urb *urb)
|
||||||
{
|
{
|
||||||
struct tascam_card *tascam = urb->context;
|
struct tascam_card *tascam = urb->context;
|
||||||
struct snd_pcm_runtime *runtime;
|
struct snd_pcm_runtime *runtime;
|
||||||
int frames, part1;
|
int frames, part1, total_available, new_remainder;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
bool need_period_elapsed = false;
|
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;
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
if (!tascam->capture_substream || !tascam->capture_substream->runtime)
|
if (!tascam->capture_substream || !tascam->capture_substream->runtime)
|
||||||
goto exit;
|
goto exit;
|
||||||
|
|
||||||
runtime = tascam->capture_substream->runtime;
|
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) {
|
if (frames > 0) {
|
||||||
spin_lock_irqsave(&tascam->lock, flags);
|
spin_lock_irqsave(&tascam->lock, flags);
|
||||||
|
|
||||||
if (!atomic_read(&tascam->capture_active)) {
|
if (!atomic_read(&tascam->capture_active)) {
|
||||||
spin_unlock_irqrestore(&tascam->lock, flags);
|
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));
|
u32 *dma = (u32 *)(runtime->dma_area + frames_to_bytes(runtime, tascam->driver_capture_pos));
|
||||||
|
|
||||||
if (tascam->driver_capture_pos + frames <= runtime->buffer_size) {
|
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 {
|
} else {
|
||||||
part1 = runtime->buffer_size - tascam->driver_capture_pos;
|
part1 = runtime->buffer_size - tascam->driver_capture_pos;
|
||||||
tascam_decode_capture_chunk(urb->transfer_buffer, dma, part1);
|
tascam_decode_capture_chunk(combined, dma, part1);
|
||||||
tascam_decode_capture_chunk(urb->transfer_buffer + (part1 * 64),
|
tascam_decode_capture_chunk(combined + (part1 * 64),
|
||||||
(u32 *)runtime->dma_area, frames - part1);
|
(u32 *)runtime->dma_area, frames - part1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -191,6 +216,11 @@ void capture_urb_complete(struct urb *urb)
|
||||||
spin_unlock_irqrestore(&tascam->lock, flags);
|
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);
|
usb_anchor_urb(urb, &tascam->capture_anchor);
|
||||||
if (usb_submit_urb(urb, GFP_ATOMIC) < 0) {
|
if (usb_submit_urb(urb, GFP_ATOMIC) < 0) {
|
||||||
usb_unanchor_urb(urb);
|
usb_unanchor_urb(urb);
|
||||||
|
|
@ -203,7 +233,7 @@ void capture_urb_complete(struct urb *urb)
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
usb_unanchor_urb(urb);
|
usb_unanchor_urb(urb);
|
||||||
atomic_dec(&tascam->active_urbs);
|
atomic_dec(&tascam->active_urbs);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ static void tascam_midi_out_complete(struct urb *urb)
|
||||||
|
|
||||||
if (urb->status || !sub || !tascam->midi_out_active) {
|
if (urb->status || !sub || !tascam->midi_out_active) {
|
||||||
tascam->midi_out_active = false;
|
tascam->midi_out_active = false;
|
||||||
|
usb_unanchor_urb(urb);
|
||||||
spin_unlock_irqrestore(&tascam->midi_lock, flags);
|
spin_unlock_irqrestore(&tascam->midi_lock, flags);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -207,8 +207,15 @@ static snd_pcm_uframes_t tascam_playback_pointer(struct snd_pcm_substream *subst
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
u64 pos;
|
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;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
spin_lock_irqsave(&tascam->lock, flags);
|
spin_lock_irqsave(&tascam->lock, flags);
|
||||||
pos = tascam->playback_frames_consumed;
|
pos = tascam->playback_frames_consumed;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue