code safety improvements

This commit is contained in:
Šerif Rami 2025-08-08 15:57:06 +02:00
parent 87ac941e35
commit f4b17d24c8
6 changed files with 259 additions and 292 deletions

View File

@ -415,7 +415,7 @@ static int tascam_probe(struct usb_interface *intf,
struct snd_card *card; struct snd_card *card;
struct tascam_card *tascam; struct tascam_card *tascam;
int err; int err;
char *handshake_buf; char *handshake_buf __free(kfree);
if (dev->speed != USB_SPEED_HIGH) if (dev->speed != USB_SPEED_HIGH)
dev_info(&dev->dev, dev_info(&dev->dev,
@ -449,17 +449,15 @@ static int tascam_probe(struct usb_interface *intf,
handshake_buf, 1, USB_CTRL_TIMEOUT_MS); handshake_buf, 1, USB_CTRL_TIMEOUT_MS);
if (err < 0) { if (err < 0) {
dev_err(&dev->dev, "Handshake read failed with %d\n", err); dev_err(&dev->dev, "Handshake read failed with %d\n", err);
kfree(handshake_buf);
return err; return err;
} }
if (handshake_buf[0] != 0x12 && handshake_buf[0] != 0x16 && if (handshake_buf[0] != 0x12 && handshake_buf[0] != 0x16 &&
handshake_buf[0] != 0x30) { handshake_buf[0] != 0x30) {
dev_err(&dev->dev, "Unexpected handshake value: 0x%x\n", handshake_buf[0]); dev_err(&dev->dev, "Unexpected handshake value: 0x%x\n", handshake_buf[0]);
kfree(handshake_buf);
return -ENODEV; return -ENODEV;
} }
kfree(handshake_buf);
err = usb_set_interface(dev, 0, 1); err = usb_set_interface(dev, 0, 1);
if (err < 0) { if (err < 0) {

View File

@ -73,14 +73,12 @@ tascam_capture_pointer(struct snd_pcm_substream *substream) {
struct tascam_card *tascam = snd_pcm_substream_chip(substream); struct tascam_card *tascam = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
u64 pos; u64 pos;
unsigned long flags;
if (!atomic_read(&tascam->capture_active)) if (!atomic_read(&tascam->capture_active))
return 0; return 0;
spin_lock_irqsave(&tascam->lock, flags); guard(spinlock_irqsave)(&tascam->lock);
pos = tascam->capture_frames_processed; pos = tascam->capture_frames_processed;
spin_unlock_irqrestore(&tascam->lock, flags);
if (runtime->buffer_size == 0) if (runtime->buffer_size == 0)
return 0; return 0;
@ -165,7 +163,6 @@ void tascam_capture_work_handler(struct work_struct *work) {
container_of(work, struct tascam_card, capture_work); container_of(work, struct tascam_card, capture_work);
struct snd_pcm_substream *substream = tascam->capture_substream; struct snd_pcm_substream *substream = tascam->capture_substream;
struct snd_pcm_runtime *runtime; struct snd_pcm_runtime *runtime;
unsigned long flags;
u8 *raw_block = tascam->capture_decode_raw_block; u8 *raw_block = tascam->capture_decode_raw_block;
s32 *decoded_block = tascam->capture_decode_dst_block; s32 *decoded_block = tascam->capture_decode_dst_block;
s32 *routed_block = tascam->capture_routing_buffer; s32 *routed_block = tascam->capture_routing_buffer;
@ -184,28 +181,29 @@ void tascam_capture_work_handler(struct work_struct *work) {
size_t write_ptr, read_ptr, available_data; size_t write_ptr, read_ptr, available_data;
bool can_process; bool can_process;
spin_lock_irqsave(&tascam->lock, flags); {
write_ptr = tascam->capture_ring_buffer_write_ptr; guard(spinlock_irqsave)(&tascam->lock);
read_ptr = tascam->capture_ring_buffer_read_ptr; write_ptr = tascam->capture_ring_buffer_write_ptr;
available_data = (write_ptr >= read_ptr) read_ptr = tascam->capture_ring_buffer_read_ptr;
? (write_ptr - read_ptr) available_data = (write_ptr >= read_ptr)
: (CAPTURE_RING_BUFFER_SIZE - read_ptr + write_ptr); ? (write_ptr - read_ptr)
can_process = (available_data >= RAW_BYTES_PER_DECODE_BLOCK); : (CAPTURE_RING_BUFFER_SIZE - read_ptr + write_ptr);
can_process = (available_data >= RAW_BYTES_PER_DECODE_BLOCK);
if (can_process) { if (can_process) {
size_t bytes_to_end = CAPTURE_RING_BUFFER_SIZE - read_ptr; size_t bytes_to_end = CAPTURE_RING_BUFFER_SIZE - read_ptr;
if (bytes_to_end >= RAW_BYTES_PER_DECODE_BLOCK) { if (bytes_to_end >= RAW_BYTES_PER_DECODE_BLOCK) {
memcpy(raw_block, tascam->capture_ring_buffer + read_ptr, memcpy(raw_block, tascam->capture_ring_buffer + read_ptr,
RAW_BYTES_PER_DECODE_BLOCK); RAW_BYTES_PER_DECODE_BLOCK);
} else { } else {
memcpy(raw_block, tascam->capture_ring_buffer + read_ptr, bytes_to_end); memcpy(raw_block, tascam->capture_ring_buffer + read_ptr, bytes_to_end);
memcpy(raw_block + bytes_to_end, tascam->capture_ring_buffer, memcpy(raw_block + bytes_to_end, tascam->capture_ring_buffer,
RAW_BYTES_PER_DECODE_BLOCK - bytes_to_end); RAW_BYTES_PER_DECODE_BLOCK - bytes_to_end);
}
tascam->capture_ring_buffer_read_ptr =
(read_ptr + RAW_BYTES_PER_DECODE_BLOCK) % CAPTURE_RING_BUFFER_SIZE;
} }
tascam->capture_ring_buffer_read_ptr =
(read_ptr + RAW_BYTES_PER_DECODE_BLOCK) % CAPTURE_RING_BUFFER_SIZE;
} }
spin_unlock_irqrestore(&tascam->lock, flags);
if (!can_process) if (!can_process)
break; break;
@ -213,29 +211,30 @@ void tascam_capture_work_handler(struct work_struct *work) {
decode_tascam_capture_block(raw_block, decoded_block); decode_tascam_capture_block(raw_block, decoded_block);
process_capture_routing_us144mkii(tascam, decoded_block, routed_block); process_capture_routing_us144mkii(tascam, decoded_block, routed_block);
spin_lock_irqsave(&tascam->lock, flags); {
if (atomic_read(&tascam->capture_active)) { guard(spinlock_irqsave)(&tascam->lock);
int f; if (atomic_read(&tascam->capture_active)) {
int f;
for (f = 0; f < FRAMES_PER_DECODE_BLOCK; ++f) { for (f = 0; f < FRAMES_PER_DECODE_BLOCK; ++f) {
u8 *dst_frame_start = u8 *dst_frame_start =
runtime->dma_area + runtime->dma_area +
frames_to_bytes(runtime, tascam->driver_capture_pos); frames_to_bytes(runtime, tascam->driver_capture_pos);
s32 *routed_frame_start = routed_block + (f * NUM_CHANNELS); s32 *routed_frame_start = routed_block + (f * NUM_CHANNELS);
int c; int c;
for (c = 0; c < NUM_CHANNELS; c++) { for (c = 0; c < NUM_CHANNELS; c++) {
u8 *dst_channel = dst_frame_start + (c * BYTES_PER_SAMPLE); u8 *dst_channel = dst_frame_start + (c * BYTES_PER_SAMPLE);
s32 *src_channel_s32 = routed_frame_start + c; s32 *src_channel_s32 = routed_frame_start + c;
memcpy(dst_channel, ((char *)src_channel_s32) + 1, 3); memcpy(dst_channel, ((char *)src_channel_s32) + 1, 3);
}
tascam->driver_capture_pos =
(tascam->driver_capture_pos + 1) % runtime->buffer_size;
} }
tascam->driver_capture_pos =
(tascam->driver_capture_pos + 1) % runtime->buffer_size;
} }
} }
spin_unlock_irqrestore(&tascam->lock, flags);
} }
} }
@ -250,7 +249,6 @@ void tascam_capture_work_handler(struct work_struct *work) {
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;
int ret; int ret;
unsigned long flags;
if (urb->status) { if (urb->status) {
if (urb->status != -ENOENT && urb->status != -ECONNRESET && if (urb->status != -ENOENT && urb->status != -ECONNRESET &&
@ -267,20 +265,25 @@ void capture_urb_complete(struct urb *urb) {
size_t write_ptr; size_t write_ptr;
size_t bytes_to_end; size_t bytes_to_end;
spin_lock_irqsave(&tascam->lock, flags); {
write_ptr = tascam->capture_ring_buffer_write_ptr; guard(spinlock_irqsave)(&tascam->lock);
bytes_to_end = CAPTURE_RING_BUFFER_SIZE - write_ptr; write_ptr = tascam->capture_ring_buffer_write_ptr;
bytes_to_end = CAPTURE_RING_BUFFER_SIZE - write_ptr;
if (urb->actual_length > bytes_to_end) { if (urb->actual_length > bytes_to_end) {
memcpy(tascam->capture_ring_buffer + write_ptr, urb->transfer_buffer, bytes_to_end); memcpy(tascam->capture_ring_buffer + write_ptr, urb->transfer_buffer,
memcpy(tascam->capture_ring_buffer, urb->transfer_buffer + bytes_to_end, urb->actual_length - bytes_to_end); bytes_to_end);
} else { memcpy(tascam->capture_ring_buffer, urb->transfer_buffer + bytes_to_end,
memcpy(tascam->capture_ring_buffer + write_ptr, urb->transfer_buffer, urb->actual_length); urb->actual_length - bytes_to_end);
} else {
memcpy(tascam->capture_ring_buffer + write_ptr, urb->transfer_buffer,
urb->actual_length);
}
tascam->capture_ring_buffer_write_ptr =
(write_ptr + urb->actual_length) % CAPTURE_RING_BUFFER_SIZE;
} }
tascam->capture_ring_buffer_write_ptr = (write_ptr + urb->actual_length) % CAPTURE_RING_BUFFER_SIZE;
spin_unlock_irqrestore(&tascam->lock, flags);
schedule_work(&tascam->capture_work); schedule_work(&tascam->capture_work);
} }

View File

@ -34,15 +34,7 @@ static const char *const capture_source_texts[] = {"Analog In", "Digital In"};
*/ */
static int tascam_playback_source_info(struct snd_kcontrol *kcontrol, static int tascam_playback_source_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo) { struct snd_ctl_elem_info *uinfo) {
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; return snd_ctl_enum_info(uinfo, 1, 2, playback_source_texts);
uinfo->count = 1;
uinfo->value.enumerated.items = 2;
if (uinfo->value.enumerated.item >= 2)
uinfo->value.enumerated.item = 1;
strscpy(uinfo->value.enumerated.name,
playback_source_texts[uinfo->value.enumerated.item],
sizeof(uinfo->value.enumerated.name));
return 0;
} }
/** /**
@ -179,15 +171,7 @@ static const struct snd_kcontrol_new tascam_digital_out_control = {
*/ */
static int tascam_capture_source_info(struct snd_kcontrol *kcontrol, static int tascam_capture_source_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo) { struct snd_ctl_elem_info *uinfo) {
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; return snd_ctl_enum_info(uinfo, 1, 2, capture_source_texts);
uinfo->count = 1;
uinfo->value.enumerated.items = 2;
if (uinfo->value.enumerated.item >= 2)
uinfo->value.enumerated.item = 1;
strscpy(uinfo->value.enumerated.name,
capture_source_texts[uinfo->value.enumerated.item],
sizeof(uinfo->value.enumerated.name));
return 0;
} }
/** /**
@ -349,7 +333,7 @@ static int tascam_samplerate_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) { struct snd_ctl_elem_value *ucontrol) {
struct tascam_card *tascam = struct tascam_card *tascam =
(struct tascam_card *)snd_kcontrol_chip(kcontrol); (struct tascam_card *)snd_kcontrol_chip(kcontrol);
u8 *buf; u8 *buf __free(kfree);
int err; int err;
u32 rate = 0; u32 rate = 0;
@ -370,7 +354,6 @@ static int tascam_samplerate_get(struct snd_kcontrol *kcontrol,
rate = buf[0] | (buf[1] << 8) | (buf[2] << 16); rate = buf[0] | (buf[1] << 8) | (buf[2] << 16);
ucontrol->value.integer.value[0] = rate; ucontrol->value.integer.value[0] = rate;
kfree(buf);
return 0; return 0;
} }

View File

@ -124,13 +124,13 @@ static void tascam_midi_in_trigger(struct snd_rawmidi_substream *substream,
int up) { int up) {
struct tascam_card *tascam = substream->rmidi->private_data; struct tascam_card *tascam = substream->rmidi->private_data;
int i, err; int i, err;
unsigned long flags;
if (up) { if (up) {
if (atomic_xchg(&tascam->midi_in_active, 1) == 0) { if (atomic_xchg(&tascam->midi_in_active, 1) == 0) {
spin_lock_irqsave(&tascam->midi_in_lock, flags); {
kfifo_reset(&tascam->midi_in_fifo); guard(spinlock_irqsave)(&tascam->midi_in_lock);
spin_unlock_irqrestore(&tascam->midi_in_lock, flags); kfifo_reset(&tascam->midi_in_fifo);
}
for (i = 0; i < NUM_MIDI_IN_URBS; i++) { for (i = 0; i < NUM_MIDI_IN_URBS; i++) {
usb_get_urb(tascam->midi_in_urbs[i]); usb_get_urb(tascam->midi_in_urbs[i]);
@ -175,7 +175,6 @@ static const struct snd_rawmidi_ops tascam_midi_in_ops = {
*/ */
void tascam_midi_out_urb_complete(struct urb *urb) { void tascam_midi_out_urb_complete(struct urb *urb) {
struct tascam_card *tascam = urb->context; struct tascam_card *tascam = urb->context;
unsigned long flags;
int i, urb_index = -1; int i, urb_index = -1;
if (urb->status) { if (urb->status) {
@ -184,6 +183,7 @@ void tascam_midi_out_urb_complete(struct urb *urb) {
dev_err_ratelimited(tascam->card->dev, "MIDI OUT URB failed: %d\n", dev_err_ratelimited(tascam->card->dev, "MIDI OUT URB failed: %d\n",
urb->status); urb->status);
} }
goto out;
} }
if (!tascam) if (!tascam)
@ -201,12 +201,14 @@ void tascam_midi_out_urb_complete(struct urb *urb) {
goto out; goto out;
} }
spin_lock_irqsave(&tascam->midi_out_lock, flags); {
clear_bit(urb_index, &tascam->midi_out_urbs_in_flight); guard(spinlock_irqsave)(&tascam->midi_out_lock);
spin_unlock_irqrestore(&tascam->midi_out_lock, flags); clear_bit(urb_index, &tascam->midi_out_urbs_in_flight);
}
if (atomic_read(&tascam->midi_out_active)) if (atomic_read(&tascam->midi_out_active))
schedule_work(&tascam->midi_out_work); schedule_work(&tascam->midi_out_work);
out: out:
usb_put_urb(urb); usb_put_urb(urb);
} }
@ -231,52 +233,51 @@ static void tascam_midi_out_work_handler(struct work_struct *work) {
return; return;
while (snd_rawmidi_transmit_peek(substream, (u8[]){0}, 1) == 1) { while (snd_rawmidi_transmit_peek(substream, (u8[]){0}, 1) == 1) {
unsigned long flags;
int urb_index; int urb_index;
struct urb *urb; struct urb *urb;
u8 *buf; u8 *buf;
int bytes_to_send; int bytes_to_send;
spin_lock_irqsave(&tascam->midi_out_lock, flags); {
guard(spinlock_irqsave)(&tascam->midi_out_lock);
urb_index = -1; urb_index = -1;
for (i = 0; i < NUM_MIDI_OUT_URBS; i++) { for (i = 0; i < NUM_MIDI_OUT_URBS; i++) {
if (!test_bit(i, &tascam->midi_out_urbs_in_flight)) { if (!test_bit(i, &tascam->midi_out_urbs_in_flight)) {
urb_index = i; urb_index = i;
break; break;
}
} }
if (urb_index < 0) {
return; /* No free URBs, will be rescheduled by completion handler */
}
urb = tascam->midi_out_urbs[urb_index];
buf = urb->transfer_buffer;
bytes_to_send = snd_rawmidi_transmit(substream, buf, 8);
if (bytes_to_send <= 0) {
break; /* No more data */
}
if (bytes_to_send < 9)
memset(buf + bytes_to_send, 0xfd, 9 - bytes_to_send);
buf[8] = 0x00;
set_bit(urb_index, &tascam->midi_out_urbs_in_flight);
urb->transfer_buffer_length = 9;
} }
if (urb_index < 0) {
spin_unlock_irqrestore(&tascam->midi_out_lock, flags);
return; /* No free URBs, will be rescheduled by completion handler */
}
urb = tascam->midi_out_urbs[urb_index];
buf = urb->transfer_buffer;
bytes_to_send = snd_rawmidi_transmit(substream, buf, 8);
if (bytes_to_send <= 0) {
spin_unlock_irqrestore(&tascam->midi_out_lock, flags);
break; /* No more data */
}
if (bytes_to_send < 9)
memset(buf + bytes_to_send, 0xfd, 9 - bytes_to_send);
buf[8] = 0x00;
set_bit(urb_index, &tascam->midi_out_urbs_in_flight);
urb->transfer_buffer_length = 9;
spin_unlock_irqrestore(&tascam->midi_out_lock, flags);
usb_get_urb(urb); usb_get_urb(urb);
usb_anchor_urb(urb, &tascam->midi_out_anchor); usb_anchor_urb(urb, &tascam->midi_out_anchor);
if (usb_submit_urb(urb, GFP_KERNEL) < 0) { if (usb_submit_urb(urb, GFP_KERNEL) < 0) {
dev_err_ratelimited(tascam->card->dev, dev_err_ratelimited(tascam->card->dev,
"Failed to submit MIDI OUT URB %d\n", urb_index); "Failed to submit MIDI OUT URB %d\n", urb_index);
spin_lock_irqsave(&tascam->midi_out_lock, flags); {
clear_bit(urb_index, &tascam->midi_out_urbs_in_flight); guard(spinlock_irqsave)(&tascam->midi_out_lock);
spin_unlock_irqrestore(&tascam->midi_out_lock, flags); clear_bit(urb_index, &tascam->midi_out_urbs_in_flight);
}
usb_unanchor_urb(urb); usb_unanchor_urb(urb);
usb_put_urb(urb); usb_put_urb(urb);
break; /* Stop on error */ break; /* Stop on error */
@ -402,4 +403,4 @@ int tascam_create_midi(struct tascam_card *tascam) {
INIT_WORK(&tascam->midi_out_work, tascam_midi_out_work_handler); INIT_WORK(&tascam->midi_out_work, tascam_midi_out_work_handler);
return 0; return 0;
} }

View File

@ -259,10 +259,6 @@ int tascam_pcm_hw_params(struct snd_pcm_substream *substream,
int err; int err;
unsigned int rate = params_rate(params); unsigned int rate = params_rate(params);
err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
if (err < 0)
return err;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
tascam->fpo.sample_rate_khz = rate / 1000; tascam->fpo.sample_rate_khz = rate / 1000;
tascam->fpo.base_feedback_value = tascam->fpo.sample_rate_khz; tascam->fpo.base_feedback_value = tascam->fpo.sample_rate_khz;
@ -300,9 +296,7 @@ int tascam_pcm_hw_params(struct snd_pcm_substream *substream,
* *
* Return: 0 on success. * Return: 0 on success.
*/ */
int tascam_pcm_hw_free(struct snd_pcm_substream *substream) { int tascam_pcm_hw_free(struct snd_pcm_substream *substream) { return 0; }
return snd_pcm_lib_free_pages(substream);
}
/** /**
* tascam_pcm_trigger() - Triggers the start or stop of PCM streams. * tascam_pcm_trigger() - Triggers the start or stop of PCM streams.
@ -318,36 +312,36 @@ int tascam_pcm_hw_free(struct snd_pcm_substream *substream) {
*/ */
int tascam_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { int tascam_pcm_trigger(struct snd_pcm_substream *substream, int cmd) {
struct tascam_card *tascam = snd_pcm_substream_chip(substream); struct tascam_card *tascam = snd_pcm_substream_chip(substream);
unsigned long flags;
int err = 0; int err = 0;
int i; int i;
bool do_start = false; bool do_start = false;
bool do_stop = false; bool do_stop = false;
spin_lock_irqsave(&tascam->lock, flags); {
switch (cmd) { guard(spinlock_irqsave)(&tascam->lock);
case SNDRV_PCM_TRIGGER_START: switch (cmd) {
case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_START:
if (!atomic_read(&tascam->playback_active)) { case SNDRV_PCM_TRIGGER_RESUME:
atomic_set(&tascam->playback_active, 1); if (!atomic_read(&tascam->playback_active)) {
atomic_set(&tascam->capture_active, 1); atomic_set(&tascam->playback_active, 1);
do_start = true; atomic_set(&tascam->capture_active, 1);
do_start = true;
}
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (atomic_read(&tascam->playback_active)) {
atomic_set(&tascam->playback_active, 0);
atomic_set(&tascam->capture_active, 0);
do_stop = true;
}
break;
default:
err = -EINVAL;
break;
} }
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (atomic_read(&tascam->playback_active)) {
atomic_set(&tascam->playback_active, 0);
atomic_set(&tascam->capture_active, 0);
do_stop = true;
}
break;
default:
err = -EINVAL;
break;
} }
spin_unlock_irqrestore(&tascam->lock, flags);
if (do_start) { if (do_start) {
if (atomic_read(&tascam->active_urbs) > 0) { if (atomic_read(&tascam->active_urbs) > 0) {
@ -362,7 +356,7 @@ int tascam_pcm_trigger(struct snd_pcm_substream *substream, int cmd) {
if (err < 0) { if (err < 0) {
usb_unanchor_urb(tascam->feedback_urbs[i]); usb_unanchor_urb(tascam->feedback_urbs[i]);
usb_put_urb(tascam->feedback_urbs[i]); usb_put_urb(tascam->feedback_urbs[i]);
atomic_dec(&tascam->active_urbs); /* Decrement on failed submission */ atomic_dec(&tascam->active_urbs);
goto start_rollback; goto start_rollback;
} }
atomic_inc(&tascam->active_urbs); atomic_inc(&tascam->active_urbs);
@ -374,7 +368,7 @@ int tascam_pcm_trigger(struct snd_pcm_substream *substream, int cmd) {
if (err < 0) { if (err < 0) {
usb_unanchor_urb(tascam->playback_urbs[i]); usb_unanchor_urb(tascam->playback_urbs[i]);
usb_put_urb(tascam->playback_urbs[i]); usb_put_urb(tascam->playback_urbs[i]);
atomic_dec(&tascam->active_urbs); /* Decrement on failed submission */ atomic_dec(&tascam->active_urbs);
goto start_rollback; goto start_rollback;
} }
atomic_inc(&tascam->active_urbs); atomic_inc(&tascam->active_urbs);
@ -386,7 +380,7 @@ int tascam_pcm_trigger(struct snd_pcm_substream *substream, int cmd) {
if (err < 0) { if (err < 0) {
usb_unanchor_urb(tascam->capture_urbs[i]); usb_unanchor_urb(tascam->capture_urbs[i]);
usb_put_urb(tascam->capture_urbs[i]); usb_put_urb(tascam->capture_urbs[i]);
atomic_dec(&tascam->active_urbs); /* Decrement on failed submission */ atomic_dec(&tascam->active_urbs);
goto start_rollback; goto start_rollback;
} }
atomic_inc(&tascam->active_urbs); atomic_inc(&tascam->active_urbs);
@ -405,26 +399,18 @@ int tascam_pcm_trigger(struct snd_pcm_substream *substream, int cmd) {
return err; return err;
} }
/**
* tascam_init_pcm() - Initializes the ALSA PCM device.
* @pcm: Pointer to the ALSA PCM device to initialize.
*
* This function sets up the PCM operations for playback and capture,
* preallocates pages for the PCM buffer, and initializes the workqueue
* for deferred capture processing.
*
* Return: 0 on success.
*/
int tascam_init_pcm(struct snd_pcm *pcm) { int tascam_init_pcm(struct snd_pcm *pcm) {
struct tascam_card *tascam = pcm->private_data; struct tascam_card *tascam = pcm->private_data;
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &tascam_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &tascam_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &tascam_capture_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &tascam_capture_ops);
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
tascam->dev->dev.parent, 64 * 1024, snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
tascam_pcm_hw.buffer_bytes_max); tascam->dev->dev.parent, 64 * 1024,
tascam_pcm_hw.buffer_bytes_max);
INIT_WORK(&tascam->capture_work, tascam_capture_work_handler); INIT_WORK(&tascam->capture_work, tascam_capture_work_handler);
return 0; return 0;
} }

View File

@ -113,14 +113,12 @@ tascam_playback_pointer(struct snd_pcm_substream *substream) {
struct tascam_card *tascam = snd_pcm_substream_chip(substream); struct tascam_card *tascam = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
u64 pos; u64 pos;
unsigned long flags;
if (!atomic_read(&tascam->playback_active)) if (!atomic_read(&tascam->playback_active))
return 0; return 0;
spin_lock_irqsave(&tascam->lock, flags); guard(spinlock_irqsave)(&tascam->lock);
pos = tascam->playback_frames_consumed; pos = tascam->playback_frames_consumed;
spin_unlock_irqrestore(&tascam->lock, flags);
if (runtime->buffer_size == 0) if (runtime->buffer_size == 0)
return 0; return 0;
@ -160,7 +158,6 @@ void playback_urb_complete(struct urb *urb) {
struct tascam_card *tascam = urb->context; struct tascam_card *tascam = urb->context;
struct snd_pcm_substream *substream; struct snd_pcm_substream *substream;
struct snd_pcm_runtime *runtime; struct snd_pcm_runtime *runtime;
unsigned long flags;
size_t total_bytes_for_urb = 0; size_t total_bytes_for_urb = 0;
snd_pcm_uframes_t offset_frames; snd_pcm_uframes_t offset_frames;
@ -182,35 +179,35 @@ void playback_urb_complete(struct urb *urb) {
goto out; goto out;
runtime = substream->runtime; runtime = substream->runtime;
spin_lock_irqsave(&tascam->lock, flags); {
guard(spinlock_irqsave)(&tascam->lock);
for (i = 0; i < urb->number_of_packets; i++) { for (i = 0; i < urb->number_of_packets; i++) {
unsigned int frames_for_packet; unsigned int frames_for_packet;
size_t bytes_for_packet; size_t bytes_for_packet;
if (tascam->feedback_synced) { if (tascam->feedback_synced) {
frames_for_packet = frames_for_packet =
tascam tascam->feedback_accumulator_pattern
->feedback_accumulator_pattern[tascam->feedback_pattern_out_idx]; [tascam->feedback_pattern_out_idx];
tascam->feedback_pattern_out_idx = tascam->feedback_pattern_out_idx =
(tascam->feedback_pattern_out_idx + 1) % FEEDBACK_ACCUMULATOR_SIZE; (tascam->feedback_pattern_out_idx + 1) % FEEDBACK_ACCUMULATOR_SIZE;
} else { } else {
frames_for_packet = runtime->rate / 8000; frames_for_packet = runtime->rate / 8000;
}
bytes_for_packet = frames_for_packet * BYTES_PER_FRAME;
urb->iso_frame_desc[i].offset = total_bytes_for_urb;
urb->iso_frame_desc[i].length = bytes_for_packet;
total_bytes_for_urb += bytes_for_packet;
} }
bytes_for_packet = frames_for_packet * BYTES_PER_FRAME; urb->transfer_buffer_length = total_bytes_for_urb;
urb->iso_frame_desc[i].offset = total_bytes_for_urb; offset_frames = tascam->driver_playback_pos;
urb->iso_frame_desc[i].length = bytes_for_packet; frames_to_copy = bytes_to_frames(runtime, total_bytes_for_urb);
total_bytes_for_urb += bytes_for_packet; tascam->driver_playback_pos =
(offset_frames + frames_to_copy) % runtime->buffer_size;
} }
urb->transfer_buffer_length = total_bytes_for_urb;
offset_frames = tascam->driver_playback_pos;
frames_to_copy = bytes_to_frames(runtime, total_bytes_for_urb);
tascam->driver_playback_pos =
(offset_frames + frames_to_copy) % runtime->buffer_size;
spin_unlock_irqrestore(&tascam->lock, flags);
if (total_bytes_for_urb > 0) { if (total_bytes_for_urb > 0) {
u8 *dst_buf = urb->transfer_buffer; u8 *dst_buf = urb->transfer_buffer;
@ -267,7 +264,6 @@ void feedback_urb_complete(struct urb *urb) {
struct tascam_card *tascam = urb->context; struct tascam_card *tascam = urb->context;
struct snd_pcm_substream *playback_ss, *capture_ss; struct snd_pcm_substream *playback_ss, *capture_ss;
struct snd_pcm_runtime *playback_rt, *capture_rt; struct snd_pcm_runtime *playback_rt, *capture_rt;
unsigned long flags;
u64 total_frames_in_urb = 0; u64 total_frames_in_urb = 0;
int ret, p; int ret, p;
unsigned int old_in_idx, new_in_idx; unsigned int old_in_idx, new_in_idx;
@ -294,126 +290,126 @@ void feedback_urb_complete(struct urb *urb) {
capture_ss = tascam->capture_substream; capture_ss = tascam->capture_substream;
capture_rt = capture_ss ? capture_ss->runtime : NULL; capture_rt = capture_ss ? capture_ss->runtime : NULL;
spin_lock_irqsave(&tascam->lock, flags); {
guard(spinlock_irqsave)(&tascam->lock);
if (tascam->feedback_urb_skip_count > 0) { if (tascam->feedback_urb_skip_count > 0) {
tascam->feedback_urb_skip_count--; tascam->feedback_urb_skip_count--;
goto unlock_and_continue; goto continue_unlock;
} }
old_in_idx = tascam->feedback_pattern_in_idx; old_in_idx = tascam->feedback_pattern_in_idx;
for (p = 0; p < urb->number_of_packets; p++) { for (p = 0; p < urb->number_of_packets; p++) {
u8 feedback_value = 0; u8 feedback_value = 0;
const unsigned int *pattern; const unsigned int *pattern;
bool packet_ok = (urb->iso_frame_desc[p].status == 0 && bool packet_ok = (urb->iso_frame_desc[p].status == 0 &&
urb->iso_frame_desc[p].actual_length >= 1); urb->iso_frame_desc[p].actual_length >= 1);
if (packet_ok) if (packet_ok)
feedback_value = feedback_value =
*((u8 *)urb->transfer_buffer + urb->iso_frame_desc[p].offset); *((u8 *)urb->transfer_buffer + urb->iso_frame_desc[p].offset);
if (packet_ok) { if (packet_ok) {
int delta = feedback_value - tascam->fpo.base_feedback_value + int delta = feedback_value - tascam->fpo.base_feedback_value +
tascam->fpo.feedback_offset; tascam->fpo.feedback_offset;
int pattern_idx; int pattern_idx;
if (delta < 0) { if (delta < 0) {
pattern_idx = 0; // Clamp to the lowest pattern pattern_idx = 0; // Clamp to the lowest pattern
} else if (delta >= 5) { } else if (delta >= 5) {
pattern_idx = 4; // Clamp to the highest pattern pattern_idx = 4; // Clamp to the highest pattern
} else {
pattern_idx = delta;
}
pattern = tascam->fpo.full_frame_patterns[pattern_idx];
tascam->feedback_consecutive_errors = 0;
int i;
for (i = 0; i < 8; i++) {
unsigned int in_idx = (tascam->feedback_pattern_in_idx + i) %
FEEDBACK_ACCUMULATOR_SIZE;
tascam->feedback_accumulator_pattern[in_idx] = pattern[i];
total_frames_in_urb += pattern[i];
}
} else { } else {
pattern_idx = delta; unsigned int nominal_frames = playback_rt->rate / 8000;
} int i;
pattern = tascam->fpo.full_frame_patterns[pattern_idx]; if (tascam->feedback_synced) {
tascam->feedback_consecutive_errors = 0; tascam->feedback_consecutive_errors++;
int i; if (tascam->feedback_consecutive_errors >
FEEDBACK_SYNC_LOSS_THRESHOLD) {
dev_err(tascam->card->dev,
"Fatal: Feedback sync lost. Stopping stream.\n");
if (playback_ss)
snd_pcm_stop(playback_ss, SNDRV_PCM_STATE_XRUN);
if (capture_ss)
snd_pcm_stop(capture_ss, SNDRV_PCM_STATE_XRUN);
tascam->feedback_synced = false;
goto continue_unlock;
}
}
for (i = 0; i < 8; i++) {
unsigned int in_idx = (tascam->feedback_pattern_in_idx + i) %
FEEDBACK_ACCUMULATOR_SIZE;
for (i = 0; i < 8; i++) { tascam->feedback_accumulator_pattern[in_idx] = nominal_frames;
unsigned int in_idx = total_frames_in_urb += nominal_frames;
(tascam->feedback_pattern_in_idx + i) % FEEDBACK_ACCUMULATOR_SIZE;
tascam->feedback_accumulator_pattern[in_idx] = pattern[i];
total_frames_in_urb += pattern[i];
}
} else {
unsigned int nominal_frames = playback_rt->rate / 8000;
int i;
if (tascam->feedback_synced) {
tascam->feedback_consecutive_errors++;
if (tascam->feedback_consecutive_errors >
FEEDBACK_SYNC_LOSS_THRESHOLD) {
dev_err(tascam->card->dev,
"Fatal: Feedback sync lost. Stopping stream.\n");
if (playback_ss)
snd_pcm_stop(playback_ss, SNDRV_PCM_STATE_XRUN);
if (capture_ss)
snd_pcm_stop(capture_ss, SNDRV_PCM_STATE_XRUN);
tascam->feedback_synced = false;
goto unlock_and_continue;
} }
} }
for (i = 0; i < 8; i++) { tascam->feedback_pattern_in_idx =
unsigned int in_idx = (tascam->feedback_pattern_in_idx + 8) % FEEDBACK_ACCUMULATOR_SIZE;
(tascam->feedback_pattern_in_idx + i) % FEEDBACK_ACCUMULATOR_SIZE; }
tascam->feedback_accumulator_pattern[in_idx] = nominal_frames; new_in_idx = tascam->feedback_pattern_in_idx;
total_frames_in_urb += nominal_frames;
if (!tascam->feedback_synced) {
unsigned int out_idx = tascam->feedback_pattern_out_idx;
bool is_ahead = (new_in_idx - out_idx) % FEEDBACK_ACCUMULATOR_SIZE <
(FEEDBACK_ACCUMULATOR_SIZE / 2);
bool was_behind = (old_in_idx - out_idx) % FEEDBACK_ACCUMULATOR_SIZE >=
(FEEDBACK_ACCUMULATOR_SIZE / 2);
if (is_ahead && was_behind) {
dev_dbg(tascam->card->dev, "Sync Acquired! (in: %u, out: %u)\n",
new_in_idx, out_idx);
tascam->feedback_synced = true;
tascam->feedback_consecutive_errors = 0;
} }
} }
tascam->feedback_pattern_in_idx =
(tascam->feedback_pattern_in_idx + 8) % FEEDBACK_ACCUMULATOR_SIZE;
}
new_in_idx = tascam->feedback_pattern_in_idx; if (total_frames_in_urb > 0) {
tascam->playback_frames_consumed += total_frames_in_urb;
if (atomic_read(&tascam->capture_active))
tascam->capture_frames_processed += total_frames_in_urb;
}
if (!tascam->feedback_synced) { if (playback_rt->period_size > 0) {
unsigned int out_idx = tascam->feedback_pattern_out_idx; u64 current_period = div_u64(tascam->playback_frames_consumed,
bool is_ahead = (new_in_idx - out_idx) % FEEDBACK_ACCUMULATOR_SIZE < playback_rt->period_size);
(FEEDBACK_ACCUMULATOR_SIZE / 2);
bool was_behind = (old_in_idx - out_idx) % FEEDBACK_ACCUMULATOR_SIZE >=
(FEEDBACK_ACCUMULATOR_SIZE / 2);
if (is_ahead && was_behind) { if (current_period > tascam->last_period_pos) {
dev_dbg(tascam->card->dev, "Sync Acquired! (in: %u, out: %u)\n", tascam->last_period_pos = current_period;
new_in_idx, out_idx); playback_period_elapsed = true;
tascam->feedback_synced = true; }
tascam->feedback_consecutive_errors = 0; }
if (atomic_read(&tascam->capture_active) && capture_rt &&
capture_rt->period_size > 0) {
u64 current_capture_period = div_u64(tascam->capture_frames_processed,
capture_rt->period_size);
if (current_capture_period > tascam->last_capture_period_pos) {
tascam->last_capture_period_pos = current_capture_period;
capture_period_elapsed = true;
}
} }
} }
if (total_frames_in_urb > 0) { continue_unlock:
tascam->playback_frames_consumed += total_frames_in_urb;
if (atomic_read(&tascam->capture_active))
tascam->capture_frames_processed += total_frames_in_urb;
}
if (playback_rt->period_size > 0) {
u64 current_period =
div_u64(tascam->playback_frames_consumed, playback_rt->period_size);
if (current_period > tascam->last_period_pos) {
tascam->last_period_pos = current_period;
playback_period_elapsed = true;
}
}
if (atomic_read(&tascam->capture_active) && capture_rt &&
capture_rt->period_size > 0) {
u64 current_capture_period =
div_u64(tascam->capture_frames_processed, capture_rt->period_size);
if (current_capture_period > tascam->last_capture_period_pos) {
tascam->last_capture_period_pos = current_capture_period;
capture_period_elapsed = true;
}
}
unlock_and_continue:
spin_unlock_irqrestore(&tascam->lock, flags);
if (playback_period_elapsed) if (playback_period_elapsed)
snd_pcm_period_elapsed(playback_ss); snd_pcm_period_elapsed(playback_ss);
if (capture_period_elapsed) if (capture_period_elapsed)