feat: Refactor and optimize TASCAM US-144MKII driver
This commit includes: - Reverted audio stream decoupling to restore original behavior. - Implemented SysEx state machine and error retry for MIDI handling. - Performed memory leak audit and confirmed no obvious leaks. - Optimized playback and capture data copying for efficiency. - Reordered `tascam_card` structure for improved cache locality. - Fixed `active_urbs` counter management to prevent "URBs still active" errors.
This commit is contained in:
parent
db5df99677
commit
d35da0e547
20
us144mkii.c
20
us144mkii.c
|
|
@ -121,8 +121,7 @@ void tascam_free_urbs(struct tascam_card *tascam) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
kfree(tascam->playback_routing_buffer);
|
|
||||||
tascam->playback_routing_buffer = NULL;
|
|
||||||
kfree(tascam->capture_routing_buffer);
|
kfree(tascam->capture_routing_buffer);
|
||||||
tascam->capture_routing_buffer = NULL;
|
tascam->capture_routing_buffer = NULL;
|
||||||
kfree(tascam->capture_decode_dst_block);
|
kfree(tascam->capture_decode_dst_block);
|
||||||
|
|
@ -265,10 +264,7 @@ int tascam_alloc_urbs(struct tascam_card *tascam) {
|
||||||
if (!tascam->capture_decode_dst_block)
|
if (!tascam->capture_decode_dst_block)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
tascam->playback_routing_buffer =
|
|
||||||
kmalloc(tascam->playback_urb_alloc_size, GFP_KERNEL);
|
|
||||||
if (!tascam->playback_routing_buffer)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
tascam->capture_routing_buffer =
|
tascam->capture_routing_buffer =
|
||||||
kmalloc(FRAMES_PER_DECODE_BLOCK * DECODED_CHANNELS_PER_FRAME *
|
kmalloc(FRAMES_PER_DECODE_BLOCK * DECODED_CHANNELS_PER_FRAME *
|
||||||
|
|
@ -419,6 +415,15 @@ static int tascam_resume(struct usb_interface *intf) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void tascam_error_timer(struct timer_list *t) {
|
||||||
|
struct tascam_card *tascam = from_timer(tascam, t, error_timer);
|
||||||
|
|
||||||
|
if (atomic_read(&tascam->midi_in_active))
|
||||||
|
schedule_work(&tascam->midi_in_work);
|
||||||
|
if (atomic_read(&tascam->midi_out_active))
|
||||||
|
schedule_work(&tascam->midi_out_work);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* tascam_probe() - Probes for the TASCAM US-144MKII device.
|
* tascam_probe() - Probes for the TASCAM US-144MKII device.
|
||||||
* @intf: The USB interface being probed.
|
* @intf: The USB interface being probed.
|
||||||
|
|
@ -519,6 +524,8 @@ static int tascam_probe(struct usb_interface *intf,
|
||||||
init_usb_anchor(&tascam->midi_in_anchor);
|
init_usb_anchor(&tascam->midi_in_anchor);
|
||||||
init_usb_anchor(&tascam->midi_out_anchor);
|
init_usb_anchor(&tascam->midi_out_anchor);
|
||||||
|
|
||||||
|
timer_setup(&tascam->error_timer, tascam_error_timer, 0);
|
||||||
|
|
||||||
INIT_WORK(&tascam->stop_work, tascam_stop_work_handler);
|
INIT_WORK(&tascam->stop_work, tascam_stop_work_handler);
|
||||||
|
|
||||||
if (kfifo_alloc(&tascam->midi_in_fifo, MIDI_IN_FIFO_SIZE, GFP_KERNEL))
|
if (kfifo_alloc(&tascam->midi_in_fifo, MIDI_IN_FIFO_SIZE, GFP_KERNEL))
|
||||||
|
|
@ -601,6 +608,7 @@ static void tascam_disconnect(struct usb_interface *intf) {
|
||||||
usb_kill_anchored_urbs(&tascam->feedback_anchor);
|
usb_kill_anchored_urbs(&tascam->feedback_anchor);
|
||||||
usb_kill_anchored_urbs(&tascam->midi_in_anchor);
|
usb_kill_anchored_urbs(&tascam->midi_in_anchor);
|
||||||
usb_kill_anchored_urbs(&tascam->midi_out_anchor);
|
usb_kill_anchored_urbs(&tascam->midi_out_anchor);
|
||||||
|
timer_delete_sync(&tascam->error_timer);
|
||||||
snd_card_disconnect(tascam->card);
|
snd_card_disconnect(tascam->card);
|
||||||
tascam_free_urbs(tascam);
|
tascam_free_urbs(tascam);
|
||||||
snd_card_free(tascam->card);
|
snd_card_free(tascam->card);
|
||||||
|
|
|
||||||
105
us144mkii.h
105
us144mkii.h
|
|
@ -7,6 +7,7 @@
|
||||||
#include <linux/kfifo.h>
|
#include <linux/kfifo.h>
|
||||||
#include <linux/usb.h>
|
#include <linux/usb.h>
|
||||||
#include <linux/workqueue.h>
|
#include <linux/workqueue.h>
|
||||||
|
#include <linux/timer.h>
|
||||||
#include <sound/control.h>
|
#include <sound/control.h>
|
||||||
#include <sound/core.h>
|
#include <sound/core.h>
|
||||||
#include <sound/initval.h>
|
#include <sound/initval.h>
|
||||||
|
|
@ -179,6 +180,46 @@ enum tascam_register {
|
||||||
* @midi_out_anchor: USB anchor for MIDI output URBs.
|
* @midi_out_anchor: USB anchor for MIDI output URBs.
|
||||||
*/
|
*/
|
||||||
struct tascam_card {
|
struct tascam_card {
|
||||||
|
/* --- Hot Path Data (frequently accessed, especially in IRQ/workqueue) --- */
|
||||||
|
spinlock_t lock; // Main lock, highly contended
|
||||||
|
atomic_t playback_active;
|
||||||
|
atomic_t capture_active;
|
||||||
|
atomic_t active_urbs; // Also frequently updated
|
||||||
|
|
||||||
|
// Playback state (updated by feedback_urb_complete and playback_urb_complete)
|
||||||
|
u64 playback_frames_consumed;
|
||||||
|
snd_pcm_uframes_t driver_playback_pos;
|
||||||
|
u64 last_period_pos;
|
||||||
|
|
||||||
|
// Capture state (updated by feedback_urb_complete and capture_urb_complete)
|
||||||
|
snd_pcm_uframes_t driver_capture_pos;
|
||||||
|
u64 capture_frames_processed;
|
||||||
|
u64 last_capture_period_pos;
|
||||||
|
|
||||||
|
// Feedback related (critical for audio sync)
|
||||||
|
unsigned int feedback_accumulator_pattern[FEEDBACK_ACCUMULATOR_SIZE];
|
||||||
|
unsigned int feedback_pattern_out_idx;
|
||||||
|
unsigned int feedback_pattern_in_idx;
|
||||||
|
bool feedback_synced;
|
||||||
|
unsigned int feedback_consecutive_errors;
|
||||||
|
unsigned int feedback_urb_skip_count;
|
||||||
|
const unsigned int (*feedback_patterns)[8];
|
||||||
|
unsigned int feedback_base_value;
|
||||||
|
unsigned int feedback_max_value;
|
||||||
|
|
||||||
|
// MIDI state (frequently accessed in MIDI handlers)
|
||||||
|
atomic_t midi_in_active;
|
||||||
|
atomic_t midi_out_active;
|
||||||
|
spinlock_t midi_in_lock;
|
||||||
|
spinlock_t midi_out_lock;
|
||||||
|
unsigned long midi_out_urbs_in_flight;
|
||||||
|
u8 midi_running_status;
|
||||||
|
bool in_sysex;
|
||||||
|
struct timer_list error_timer; // Timer for MIDI error retry
|
||||||
|
|
||||||
|
int current_rate; // Also accessed frequently, moved up
|
||||||
|
|
||||||
|
/* --- Less Hot Data (pointers, configuration, work structs) --- */
|
||||||
struct usb_device *dev;
|
struct usb_device *dev;
|
||||||
struct usb_interface *iface0;
|
struct usb_interface *iface0;
|
||||||
struct usb_interface *iface1;
|
struct usb_interface *iface1;
|
||||||
|
|
@ -186,70 +227,46 @@ struct tascam_card {
|
||||||
struct snd_pcm *pcm;
|
struct snd_pcm *pcm;
|
||||||
struct snd_rawmidi *rmidi;
|
struct snd_rawmidi *rmidi;
|
||||||
|
|
||||||
/* Playback stream */
|
// Substream pointers
|
||||||
struct snd_pcm_substream *playback_substream;
|
struct snd_pcm_substream *playback_substream;
|
||||||
|
struct snd_pcm_substream *capture_substream;
|
||||||
|
struct snd_rawmidi_substream *midi_in_substream;
|
||||||
|
struct snd_rawmidi_substream *midi_out_substream;
|
||||||
|
|
||||||
|
// URB arrays and sizes
|
||||||
struct urb *playback_urbs[NUM_PLAYBACK_URBS];
|
struct urb *playback_urbs[NUM_PLAYBACK_URBS];
|
||||||
size_t playback_urb_alloc_size;
|
size_t playback_urb_alloc_size;
|
||||||
struct urb *feedback_urbs[NUM_FEEDBACK_URBS];
|
struct urb *feedback_urbs[NUM_FEEDBACK_URBS];
|
||||||
size_t feedback_urb_alloc_size;
|
size_t feedback_urb_alloc_size;
|
||||||
atomic_t playback_active;
|
|
||||||
u64 playback_frames_consumed;
|
|
||||||
snd_pcm_uframes_t driver_playback_pos;
|
|
||||||
u64 last_period_pos;
|
|
||||||
u8 *playback_routing_buffer;
|
|
||||||
|
|
||||||
/* Capture stream */
|
|
||||||
struct snd_pcm_substream *capture_substream;
|
|
||||||
struct urb *capture_urbs[NUM_CAPTURE_URBS];
|
struct urb *capture_urbs[NUM_CAPTURE_URBS];
|
||||||
size_t capture_urb_alloc_size;
|
size_t capture_urb_alloc_size;
|
||||||
atomic_t capture_active;
|
struct urb *midi_in_urbs[NUM_MIDI_IN_URBS];
|
||||||
snd_pcm_uframes_t driver_capture_pos;
|
struct urb *midi_out_urbs[NUM_MIDI_OUT_URBS];
|
||||||
u64 capture_frames_processed;
|
|
||||||
u64 last_capture_period_pos;
|
// Buffers (pointers to external allocations)
|
||||||
u8 *capture_ring_buffer;
|
u8 *capture_ring_buffer;
|
||||||
size_t capture_ring_buffer_read_ptr;
|
size_t capture_ring_buffer_read_ptr;
|
||||||
size_t capture_ring_buffer_write_ptr;
|
size_t capture_ring_buffer_write_ptr;
|
||||||
u8 *capture_decode_raw_block;
|
u8 *capture_decode_raw_block;
|
||||||
s32 *capture_decode_dst_block;
|
s32 *capture_decode_dst_block;
|
||||||
s32 *capture_routing_buffer;
|
s32 *capture_routing_buffer;
|
||||||
|
|
||||||
|
// Work structs
|
||||||
struct work_struct capture_work;
|
struct work_struct capture_work;
|
||||||
struct work_struct stop_work;
|
struct work_struct stop_work;
|
||||||
|
|
||||||
/* MIDI streams */
|
|
||||||
struct snd_rawmidi_substream *midi_in_substream;
|
|
||||||
struct snd_rawmidi_substream *midi_out_substream;
|
|
||||||
struct urb *midi_in_urbs[NUM_MIDI_IN_URBS];
|
|
||||||
atomic_t midi_in_active;
|
|
||||||
struct kfifo midi_in_fifo;
|
|
||||||
struct work_struct midi_in_work;
|
struct work_struct midi_in_work;
|
||||||
spinlock_t midi_in_lock;
|
|
||||||
struct urb *midi_out_urbs[NUM_MIDI_OUT_URBS];
|
|
||||||
atomic_t midi_out_active;
|
|
||||||
struct work_struct midi_out_work;
|
struct work_struct midi_out_work;
|
||||||
unsigned long midi_out_urbs_in_flight;
|
|
||||||
spinlock_t midi_out_lock;
|
|
||||||
u8 midi_running_status;
|
|
||||||
|
|
||||||
/* Shared state & Routing Matrix */
|
// FIFOs
|
||||||
spinlock_t lock;
|
struct kfifo midi_in_fifo;
|
||||||
atomic_t active_urbs;
|
|
||||||
int current_rate;
|
|
||||||
unsigned int line_out_source; /* 0: Playback 1-2, 1: Playback 3-4 */
|
|
||||||
unsigned int digital_out_source; /* 0: Playback 1-2, 1: Playback 3-4 */
|
|
||||||
unsigned int capture_12_source; /* 0: Analog In, 1: Digital In */
|
|
||||||
unsigned int capture_34_source; /* 0: Analog In, 1: Digital In */
|
|
||||||
|
|
||||||
unsigned int feedback_accumulator_pattern[FEEDBACK_ACCUMULATOR_SIZE];
|
// Routing Matrix settings
|
||||||
unsigned int feedback_pattern_out_idx;
|
unsigned int line_out_source;
|
||||||
unsigned int feedback_pattern_in_idx;
|
unsigned int digital_out_source;
|
||||||
bool feedback_synced;
|
unsigned int capture_12_source;
|
||||||
unsigned int feedback_consecutive_errors;
|
unsigned int capture_34_source;
|
||||||
unsigned int feedback_urb_skip_count;
|
|
||||||
|
|
||||||
const unsigned int (*feedback_patterns)[8];
|
|
||||||
unsigned int feedback_base_value;
|
|
||||||
unsigned int feedback_max_value;
|
|
||||||
|
|
||||||
|
// USB anchors
|
||||||
struct usb_anchor playback_anchor;
|
struct usb_anchor playback_anchor;
|
||||||
struct usb_anchor capture_anchor;
|
struct usb_anchor capture_anchor;
|
||||||
struct usb_anchor feedback_anchor;
|
struct usb_anchor feedback_anchor;
|
||||||
|
|
|
||||||
|
|
@ -190,11 +190,13 @@ void tascam_capture_work_handler(struct work_struct *work) {
|
||||||
can_process = (available_data >= RAW_BYTES_PER_DECODE_BLOCK);
|
can_process = (available_data >= RAW_BYTES_PER_DECODE_BLOCK);
|
||||||
|
|
||||||
if (can_process) {
|
if (can_process) {
|
||||||
size_t i;
|
size_t bytes_to_end = CAPTURE_RING_BUFFER_SIZE - read_ptr;
|
||||||
|
if (bytes_to_end >= RAW_BYTES_PER_DECODE_BLOCK) {
|
||||||
for (i = 0; i < RAW_BYTES_PER_DECODE_BLOCK; i++)
|
memcpy(raw_block, tascam->capture_ring_buffer + read_ptr, RAW_BYTES_PER_DECODE_BLOCK);
|
||||||
raw_block[i] = tascam->capture_ring_buffer[(read_ptr + i) %
|
} else {
|
||||||
CAPTURE_RING_BUFFER_SIZE];
|
memcpy(raw_block, tascam->capture_ring_buffer + read_ptr, bytes_to_end);
|
||||||
|
memcpy(raw_block + bytes_to_end, tascam->capture_ring_buffer, RAW_BYTES_PER_DECODE_BLOCK - bytes_to_end);
|
||||||
|
}
|
||||||
tascam->capture_ring_buffer_read_ptr =
|
tascam->capture_ring_buffer_read_ptr =
|
||||||
(read_ptr + RAW_BYTES_PER_DECODE_BLOCK) % CAPTURE_RING_BUFFER_SIZE;
|
(read_ptr + RAW_BYTES_PER_DECODE_BLOCK) % CAPTURE_RING_BUFFER_SIZE;
|
||||||
}
|
}
|
||||||
|
|
@ -280,7 +282,9 @@ void capture_urb_complete(struct urb *urb) {
|
||||||
"Failed to resubmit capture URB: %d\n", ret);
|
"Failed to resubmit capture URB: %d\n", ret);
|
||||||
usb_unanchor_urb(urb);
|
usb_unanchor_urb(urb);
|
||||||
usb_put_urb(urb);
|
usb_put_urb(urb);
|
||||||
|
atomic_dec(&tascam->active_urbs); /* Decrement on failed resubmission */
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
usb_put_urb(urb);
|
usb_put_urb(urb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,16 +32,28 @@ static void tascam_midi_in_work_handler(struct work_struct *work) {
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
for (i = 0; i < len; ++i) {
|
for (i = 0; i < len; ++i) {
|
||||||
|
u8 byte = buf[i];
|
||||||
|
|
||||||
/* Skip padding bytes */
|
/* Skip padding bytes */
|
||||||
if (buf[i] == 0xfd)
|
if (byte == 0xfd)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* The last byte is often a terminator (0x00, 0xFF). Ignore it. */
|
if (byte == 0xf0) { /* SysEx Start */
|
||||||
if (i == (len - 1) && (buf[i] == 0x00 || buf[i] == 0xff))
|
tascam->in_sysex = true;
|
||||||
continue;
|
} else if (byte == 0xf7) { /* SysEx End */
|
||||||
|
tascam->in_sysex = false;
|
||||||
|
} else if (tascam->in_sysex) {
|
||||||
|
/* Inside a SysEx message */
|
||||||
|
} else if (byte & 0x80) { /* Status byte */
|
||||||
|
tascam->midi_running_status = byte;
|
||||||
|
} else { /* Data byte */
|
||||||
|
if (tascam->midi_running_status != 0)
|
||||||
|
snd_rawmidi_receive(tascam->midi_in_substream,
|
||||||
|
&tascam->midi_running_status, 1);
|
||||||
|
}
|
||||||
|
|
||||||
/* Submit valid MIDI bytes one by one */
|
/* Submit valid MIDI bytes one by one */
|
||||||
snd_rawmidi_receive(tascam->midi_in_substream, &buf[i], 1);
|
snd_rawmidi_receive(tascam->midi_in_substream, &byte, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -60,9 +72,11 @@ void tascam_midi_in_urb_complete(struct urb *urb) {
|
||||||
|
|
||||||
if (urb->status) {
|
if (urb->status) {
|
||||||
if (urb->status != -ENOENT && urb->status != -ECONNRESET &&
|
if (urb->status != -ENOENT && urb->status != -ECONNRESET &&
|
||||||
urb->status != -ESHUTDOWN && urb->status != -EPROTO)
|
urb->status != -ESHUTDOWN && urb->status != -EPROTO) {
|
||||||
dev_err_ratelimited(tascam->card->dev, "MIDI IN URB failed: status %d\n",
|
dev_err_ratelimited(tascam->card->dev, "MIDI IN URB failed: status %d\n",
|
||||||
urb->status);
|
urb->status);
|
||||||
|
mod_timer(&tascam->error_timer, jiffies + msecs_to_jiffies(50));
|
||||||
|
}
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -182,9 +196,11 @@ void tascam_midi_out_urb_complete(struct urb *urb) {
|
||||||
|
|
||||||
if (urb->status) {
|
if (urb->status) {
|
||||||
if (urb->status != -ENOENT && urb->status != -ECONNRESET &&
|
if (urb->status != -ENOENT && urb->status != -ECONNRESET &&
|
||||||
urb->status != -ESHUTDOWN)
|
urb->status != -ESHUTDOWN) {
|
||||||
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);
|
||||||
|
mod_timer(&tascam->error_timer, jiffies + msecs_to_jiffies(50));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tascam)
|
if (!tascam)
|
||||||
|
|
|
||||||
|
|
@ -364,6 +364,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 */
|
||||||
goto start_rollback;
|
goto start_rollback;
|
||||||
}
|
}
|
||||||
atomic_inc(&tascam->active_urbs);
|
atomic_inc(&tascam->active_urbs);
|
||||||
|
|
@ -375,6 +376,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 */
|
||||||
goto start_rollback;
|
goto start_rollback;
|
||||||
}
|
}
|
||||||
atomic_inc(&tascam->active_urbs);
|
atomic_inc(&tascam->active_urbs);
|
||||||
|
|
@ -386,6 +388,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 */
|
||||||
goto start_rollback;
|
goto start_rollback;
|
||||||
}
|
}
|
||||||
atomic_inc(&tascam->active_urbs);
|
atomic_inc(&tascam->active_urbs);
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,47 @@ void feedback_urb_complete(struct urb *urb);
|
||||||
*/
|
*/
|
||||||
void capture_urb_complete(struct urb *urb);
|
void capture_urb_complete(struct urb *urb);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* prepare_playback_urb() - Prepares and submits a playback URB.
|
||||||
|
* @tascam: the tascam_card instance
|
||||||
|
*
|
||||||
|
* This function gets a free playback URB, calculates the number of frames to
|
||||||
|
* send based on the feedback accumulator, copies audio data from the ALSA
|
||||||
|
* ring buffer, applies routing, and submits the URB.
|
||||||
|
*/
|
||||||
|
void prepare_playback_urb(struct tascam_card *tascam);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* feedback_urb_complete() - Completion handler for feedback isochronous URBs.
|
||||||
|
* @urb: the completed URB
|
||||||
|
*
|
||||||
|
* This is the master clock for the driver. It runs in interrupt context.
|
||||||
|
* It reads the feedback value from the device, which indicates how many
|
||||||
|
* samples the device has consumed. This information is used to adjust the
|
||||||
|
* playback rate and to advance the capture stream pointer, keeping both
|
||||||
|
* streams in sync. It then calls snd_pcm_period_elapsed if necessary and
|
||||||
|
* resubmits itself.
|
||||||
|
*/
|
||||||
|
void feedback_urb_complete(struct urb *urb);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* retire_capture_urb() - Completion handler for capture bulk URBs.
|
||||||
|
* @urb: the completed URB
|
||||||
|
*
|
||||||
|
* This function runs in interrupt context. It copies the received raw data
|
||||||
|
* into an intermediate ring buffer and then schedules the workqueue to process
|
||||||
|
* it. It then calls prepare_capture_urb() to submit the next URB.
|
||||||
|
*/
|
||||||
|
void retire_capture_urb(struct urb *urb);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* prepare_capture_urb() - Prepares and submits a capture URB.
|
||||||
|
* @tascam: the tascam_card instance
|
||||||
|
*
|
||||||
|
* This function gets a free capture URB from the anchor and submits it.
|
||||||
|
*/
|
||||||
|
void prepare_capture_urb(struct tascam_card *tascam);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* tascam_init_pcm() - Initializes the ALSA PCM device.
|
* tascam_init_pcm() - Initializes the ALSA PCM device.
|
||||||
* @pcm: Pointer to the ALSA PCM device to initialize.
|
* @pcm: Pointer to the ALSA PCM device to initialize.
|
||||||
|
|
|
||||||
|
|
@ -161,7 +161,7 @@ void playback_urb_complete(struct urb *urb) {
|
||||||
struct snd_pcm_substream *substream;
|
struct snd_pcm_substream *substream;
|
||||||
struct snd_pcm_runtime *runtime;
|
struct snd_pcm_runtime *runtime;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
u8 *src_buf, *dst_buf;
|
|
||||||
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;
|
||||||
snd_pcm_uframes_t frames_to_copy;
|
snd_pcm_uframes_t frames_to_copy;
|
||||||
|
|
@ -213,8 +213,7 @@ void playback_urb_complete(struct urb *urb) {
|
||||||
spin_unlock_irqrestore(&tascam->lock, flags);
|
spin_unlock_irqrestore(&tascam->lock, flags);
|
||||||
|
|
||||||
if (total_bytes_for_urb > 0) {
|
if (total_bytes_for_urb > 0) {
|
||||||
src_buf = runtime->dma_area + frames_to_bytes(runtime, offset_frames);
|
u8 *dst_buf = urb->transfer_buffer;
|
||||||
dst_buf = tascam->playback_routing_buffer;
|
|
||||||
|
|
||||||
/* Handle ring buffer wrap-around */
|
/* Handle ring buffer wrap-around */
|
||||||
if (offset_frames + frames_to_copy > runtime->buffer_size) {
|
if (offset_frames + frames_to_copy > runtime->buffer_size) {
|
||||||
|
|
@ -222,17 +221,15 @@ void playback_urb_complete(struct urb *urb) {
|
||||||
frames_to_bytes(runtime, runtime->buffer_size - offset_frames);
|
frames_to_bytes(runtime, runtime->buffer_size - offset_frames);
|
||||||
size_t second_chunk_bytes = total_bytes_for_urb - first_chunk_bytes;
|
size_t second_chunk_bytes = total_bytes_for_urb - first_chunk_bytes;
|
||||||
|
|
||||||
memcpy(dst_buf, src_buf, first_chunk_bytes);
|
memcpy(dst_buf, runtime->dma_area + frames_to_bytes(runtime, offset_frames), first_chunk_bytes);
|
||||||
memcpy(dst_buf + first_chunk_bytes, runtime->dma_area,
|
memcpy(dst_buf + first_chunk_bytes, runtime->dma_area, second_chunk_bytes);
|
||||||
second_chunk_bytes);
|
|
||||||
} else {
|
} else {
|
||||||
memcpy(dst_buf, src_buf, total_bytes_for_urb);
|
memcpy(dst_buf, runtime->dma_area + frames_to_bytes(runtime, offset_frames), total_bytes_for_urb);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Apply routing to the contiguous data in our routing buffer */
|
/* Apply routing to the contiguous data in our routing buffer */
|
||||||
process_playback_routing_us144mkii(tascam, dst_buf, dst_buf,
|
process_playback_routing_us144mkii(tascam, dst_buf, dst_buf,
|
||||||
frames_to_copy);
|
frames_to_copy);
|
||||||
memcpy(urb->transfer_buffer, dst_buf, total_bytes_for_urb);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
urb->dev = tascam->dev;
|
urb->dev = tascam->dev;
|
||||||
|
|
@ -244,6 +241,7 @@ void playback_urb_complete(struct urb *urb) {
|
||||||
"Failed to resubmit playback URB: %d\n", ret);
|
"Failed to resubmit playback URB: %d\n", ret);
|
||||||
usb_unanchor_urb(urb);
|
usb_unanchor_urb(urb);
|
||||||
usb_put_urb(urb);
|
usb_put_urb(urb);
|
||||||
|
atomic_dec(&tascam->active_urbs); /* Decrement on failed resubmission */
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
usb_put_urb(urb);
|
usb_put_urb(urb);
|
||||||
|
|
@ -273,9 +271,11 @@ void feedback_urb_complete(struct urb *urb) {
|
||||||
|
|
||||||
if (urb->status) {
|
if (urb->status) {
|
||||||
if (urb->status != -ENOENT && urb->status != -ECONNRESET &&
|
if (urb->status != -ENOENT && urb->status != -ECONNRESET &&
|
||||||
urb->status != -ESHUTDOWN && urb->status != -ENODEV)
|
urb->status != -ESHUTDOWN && urb->status != -ENODEV) {
|
||||||
dev_err_ratelimited(tascam->card->dev, "Feedback URB failed: %d\n",
|
dev_err_ratelimited(tascam->card->dev, "Feedback URB failed: %d\n",
|
||||||
urb->status);
|
urb->status);
|
||||||
|
atomic_dec(&tascam->active_urbs); /* Decrement on failed resubmission */
|
||||||
|
}
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
if (!tascam || !atomic_read(&tascam->playback_active))
|
if (!tascam || !atomic_read(&tascam->playback_active))
|
||||||
|
|
@ -418,3 +418,4 @@ unlock_and_continue:
|
||||||
out:
|
out:
|
||||||
usb_put_urb(urb);
|
usb_put_urb(urb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue