fix recording while using different playback device

This commit is contained in:
Šerif Rami 2026-01-19 12:21:42 +01:00
parent 7f10e1254e
commit 2f53fd05bc
3 changed files with 92 additions and 23 deletions

View File

@ -128,6 +128,7 @@ enum tascam_register {
* @freq_q16: current frequency for the playback PLL in Q16.16 format
* @feedback_synced: flag indicating if feedback is synced
* @feedback_urb_skip_count: number of feedback URBs to skip at startup
* @running_ghost_playback: flag indicating if implicit playback is running
* @stop_work: work struct for stopping all streams
* @stop_pcm_work: work struct for stopping PCM streams
*/
@ -183,6 +184,7 @@ struct tascam_card {
u32 freq_q16;
bool feedback_synced;
unsigned int feedback_urb_skip_count;
bool running_ghost_playback;
struct work_struct stop_work;
struct work_struct stop_pcm_work;

View File

@ -73,7 +73,7 @@ static snd_pcm_uframes_t tascam_capture_pointer(struct snd_pcm_substream *substr
static int tascam_capture_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct tascam_card *tascam = snd_pcm_substream_chip(substream);
int i, ret = 0;
int i, u, ret = 0;
bool start = false;
bool stop = false;
unsigned long flags;
@ -105,6 +105,20 @@ static int tascam_capture_trigger(struct snd_pcm_substream *substream, int cmd)
if (tascam->capture_urbs[i])
usb_unlink_urb(tascam->capture_urbs[i]);
}
if (tascam->running_ghost_playback) {
atomic_set(&tascam->playback_active, 0);
tascam->running_ghost_playback = false;
for (i = 0; i < NUM_PLAYBACK_URBS; i++) {
if (tascam->playback_urbs[i])
usb_unlink_urb(tascam->playback_urbs[i]);
}
for (i = 0; i < NUM_FEEDBACK_URBS; i++) {
if (tascam->feedback_urbs[i])
usb_unlink_urb(tascam->feedback_urbs[i]);
}
}
}
if (start) {
@ -121,6 +135,54 @@ static int tascam_capture_trigger(struct snd_pcm_substream *substream, int cmd)
}
atomic_inc(&tascam->active_urbs);
}
if (!atomic_read(&tascam->playback_active)) {
atomic_set(&tascam->playback_active, 1);
tascam->running_ghost_playback = true;
tascam->phase_accum = 0;
tascam->freq_q16 = div_u64(((u64)tascam->current_rate << 16), 8000);
tascam->feedback_urb_skip_count = 4;
tascam->feedback_synced = false;
for (i = 0; i < NUM_FEEDBACK_URBS; i++) {
struct urb *f_urb = tascam->feedback_urbs[i];
f_urb->number_of_packets = FEEDBACK_URB_PACKETS;
f_urb->transfer_buffer_length = FEEDBACK_URB_PACKETS * FEEDBACK_PACKET_SIZE;
for (u = 0; u < FEEDBACK_URB_PACKETS; u++) {
f_urb->iso_frame_desc[u].offset = u * FEEDBACK_PACKET_SIZE;
f_urb->iso_frame_desc[u].length = FEEDBACK_PACKET_SIZE;
}
usb_anchor_urb(f_urb, &tascam->feedback_anchor);
if (usb_submit_urb(f_urb, GFP_ATOMIC) < 0) {
usb_unanchor_urb(f_urb);
atomic_dec(&tascam->active_urbs);
} else {
atomic_inc(&tascam->active_urbs);
}
}
size_t nominal_bytes = (tascam->current_rate / 8000) * PLAYBACK_FRAME_SIZE;
for (u = 0; u < NUM_PLAYBACK_URBS; u++) {
struct urb *urb = tascam->playback_urbs[u];
size_t total_bytes = 0;
urb->number_of_packets = PLAYBACK_URB_PACKETS;
for (i = 0; i < PLAYBACK_URB_PACKETS; i++) {
urb->iso_frame_desc[i].offset = i * nominal_bytes;
urb->iso_frame_desc[i].length = nominal_bytes;
total_bytes += nominal_bytes;
}
urb->transfer_buffer_length = total_bytes;
memset(urb->transfer_buffer, 0, total_bytes);
usb_anchor_urb(urb, &tascam->playback_anchor);
if (usb_submit_urb(urb, GFP_ATOMIC) < 0) {
usb_unanchor_urb(urb);
atomic_dec(&tascam->active_urbs);
} else {
atomic_inc(&tascam->active_urbs);
}
}
}
}
return ret;
}

View File

@ -59,6 +59,7 @@ static int tascam_playback_prepare(struct snd_pcm_substream *substream)
tascam->playback_frames_consumed = 0;
tascam->last_pb_period_pos = 0;
tascam->feedback_synced = false;
tascam->running_ghost_playback = false;
tascam->feedback_urb_skip_count = 4;
@ -204,7 +205,6 @@ void playback_urb_complete(struct urb *urb)
goto exit_clear;
substream = tascam->playback_substream;
runtime = substream->runtime;
spin_lock_irqsave(&tascam->lock, flags);
@ -225,34 +225,39 @@ void playback_urb_complete(struct urb *urb)
urb->transfer_buffer_length = total_bytes_for_urb;
if (total_bytes_for_urb > 0) {
u8 *dst_buf = urb->transfer_buffer;
size_t ptr_bytes = frames_to_bytes(runtime, tascam->driver_playback_pos);
frames_to_copy = bytes_to_frames(runtime, total_bytes_for_urb);
if (substream) {
runtime = substream->runtime;
u8 *dst_buf = urb->transfer_buffer;
size_t ptr_bytes = frames_to_bytes(runtime, tascam->driver_playback_pos);
frames_to_copy = bytes_to_frames(runtime, total_bytes_for_urb);
if (tascam->driver_playback_pos + frames_to_copy > runtime->buffer_size) {
size_t part1 = runtime->buffer_size - tascam->driver_playback_pos;
size_t part1_bytes = frames_to_bytes(runtime, part1);
if (tascam->driver_playback_pos + frames_to_copy > runtime->buffer_size) {
size_t part1 = runtime->buffer_size - tascam->driver_playback_pos;
size_t part1_bytes = frames_to_bytes(runtime, part1);
memcpy(dst_buf, runtime->dma_area + ptr_bytes, part1_bytes);
memcpy(dst_buf + part1_bytes, runtime->dma_area, total_bytes_for_urb - part1_bytes);
memcpy(dst_buf, runtime->dma_area + ptr_bytes, part1_bytes);
memcpy(dst_buf + part1_bytes, runtime->dma_area, total_bytes_for_urb - part1_bytes);
} else {
memcpy(dst_buf, runtime->dma_area + ptr_bytes, total_bytes_for_urb);
}
tascam->driver_playback_pos += frames_to_copy;
if (tascam->driver_playback_pos >= runtime->buffer_size)
tascam->driver_playback_pos -= runtime->buffer_size;
tascam->playback_frames_consumed += frames_to_copy;
if (div_u64(tascam->playback_frames_consumed, runtime->period_size) > tascam->last_pb_period_pos) {
tascam->last_pb_period_pos = div_u64(tascam->playback_frames_consumed, runtime->period_size);
need_period_elapsed = true;
}
} else {
memcpy(dst_buf, runtime->dma_area + ptr_bytes, total_bytes_for_urb);
}
tascam->driver_playback_pos += frames_to_copy;
if (tascam->driver_playback_pos >= runtime->buffer_size)
tascam->driver_playback_pos -= runtime->buffer_size;
tascam->playback_frames_consumed += frames_to_copy;
if (div_u64(tascam->playback_frames_consumed, runtime->period_size) > tascam->last_pb_period_pos) {
tascam->last_pb_period_pos = div_u64(tascam->playback_frames_consumed, runtime->period_size);
need_period_elapsed = true;
memset(urb->transfer_buffer, 0, total_bytes_for_urb);
}
}
spin_unlock_irqrestore(&tascam->lock, flags);
if (need_period_elapsed)
if (need_period_elapsed && substream)
snd_pcm_period_elapsed(substream);
usb_anchor_urb(urb, &tascam->playback_anchor);