improvements

This commit is contained in:
Šerif Rami 2026-01-06 20:01:07 +01:00
parent 7716f17b21
commit c379dc65c1
6 changed files with 202 additions and 161 deletions

View File

@ -87,7 +87,8 @@ int tascam_alloc_urbs(struct tascam_card *tascam)
{
int i;
tascam->playback_urb_alloc_size = PLAYBACK_URB_PACKETS * (12 + 2) * BYTES_PER_FRAME;
tascam->playback_urb_alloc_size = PLAYBACK_URB_PACKETS * 156;
for (i = 0; i < NUM_PLAYBACK_URBS; i++) {
struct urb *urb = usb_alloc_urb(PLAYBACK_URB_PACKETS, GFP_KERNEL);
@ -129,12 +130,13 @@ int tascam_alloc_urbs(struct tascam_card *tascam)
for (i = 0; i < NUM_CAPTURE_URBS; i++) {
struct urb *urb = usb_alloc_urb(0, GFP_KERNEL);
void *buf;
if (!urb)
return -ENOMEM;
tascam->capture_urbs[i] = urb;
void *buf = usb_alloc_coherent(tascam->dev, CAPTURE_PACKET_SIZE,
GFP_KERNEL, &urb->transfer_dma);
buf = usb_alloc_coherent(tascam->dev, CAPTURE_PACKET_SIZE,
GFP_KERNEL, &urb->transfer_dma);
if (!buf)
return -ENOMEM;
usb_fill_bulk_urb(urb, tascam->dev,
@ -188,9 +190,6 @@ static int tascam_suspend(struct usb_interface *intf, pm_message_t message)
usb_kill_anchored_urbs(&tascam->capture_anchor);
usb_kill_anchored_urbs(&tascam->midi_anchor);
usb_control_msg(tascam->dev, usb_sndctrlpipe(tascam->dev, 0),
VENDOR_REQ_DEEP_SLEEP, RT_H2D_VENDOR_DEV,
0x0000, 0x0000, NULL, 0, USB_CTRL_TIMEOUT_MS);
return 0;
}
@ -198,6 +197,8 @@ static int tascam_resume(struct usb_interface *intf)
{
struct tascam_card *tascam = usb_get_intfdata(intf);
int err;
unsigned long flags;
int current_rate;
if (!tascam)
return 0;
@ -205,12 +206,17 @@ static int tascam_resume(struct usb_interface *intf)
err = usb_set_interface(tascam->dev, 0, 1);
if (err < 0)
return err;
err = usb_set_interface(tascam->dev, 1, 1);
if (err < 0)
return err;
if (tascam->current_rate > 0)
us144mkii_configure_device_for_rate(tascam, tascam->current_rate);
spin_lock_irqsave(&tascam->lock, flags);
current_rate = tascam->current_rate;
spin_unlock_irqrestore(&tascam->lock, flags);
if (current_rate > 0)
us144mkii_configure_device_for_rate(tascam, current_rate);
return 0;
}
@ -249,6 +255,8 @@ static int tascam_probe(struct usb_interface *intf, const struct usb_device_id *
tascam->card = card;
tascam->iface0 = intf;
tascam->dev_id = le16_to_cpu(dev->descriptor.idProduct);
spin_lock_init(&tascam->lock);
init_usb_anchor(&tascam->playback_anchor);
init_usb_anchor(&tascam->feedback_anchor);
@ -259,7 +267,7 @@ static int tascam_probe(struct usb_interface *intf, const struct usb_device_id *
INIT_WORK(&tascam->stop_pcm_work, tascam_stop_pcm_work_handler);
strscpy(card->driver, DRIVER_NAME, sizeof(card->driver));
if (le16_to_cpu(dev->descriptor.idProduct) == USB_PID_TASCAM_US144)
if (tascam->dev_id == USB_PID_TASCAM_US144)
strscpy(card->shortname, "US-144", sizeof(card->shortname));
else
strscpy(card->shortname, "US-144MKII", sizeof(card->shortname));
@ -326,7 +334,7 @@ static int tascam_probe(struct usb_interface *intf, const struct usb_device_id *
usb_set_intfdata(intf, tascam);
return 0;
free_card:
free_card:
snd_card_free(card);
atomic_dec(&dev_idx);
return err;

View File

@ -41,7 +41,6 @@ enum uac_control_selector {
enum tascam_vendor_request {
VENDOR_REQ_REGISTER_WRITE = 0x41,
VENDOR_REQ_DEEP_SLEEP = 0x44,
VENDOR_REQ_MODE_CONTROL = 0x49,
};
@ -52,25 +51,25 @@ enum tascam_mode_value {
};
enum tascam_register {
REG_ADDR_UNKNOWN_0D = 0x0d04,
REG_ADDR_UNKNOWN_0E = 0x0e00,
REG_ADDR_UNKNOWN_0F = 0x0f00,
REG_ADDR_INIT_0D = 0x0d04,
REG_ADDR_INIT_0E = 0x0e00,
REG_ADDR_INIT_0F = 0x0f00,
REG_ADDR_RATE_44100 = 0x1000,
REG_ADDR_RATE_48000 = 0x1002,
REG_ADDR_RATE_88200 = 0x1008,
REG_ADDR_RATE_96000 = 0x100a,
REG_ADDR_UNKNOWN_11 = 0x110b,
REG_ADDR_INIT_11 = 0x110b,
};
#define REG_VAL_ENABLE 0x0101
#define NUM_PLAYBACK_URBS 4
#define PLAYBACK_URB_PACKETS 4
#define NUM_FEEDBACK_URBS 2
#define NUM_PLAYBACK_URBS 2
#define PLAYBACK_URB_PACKETS 8
#define NUM_FEEDBACK_URBS 4
#define FEEDBACK_URB_PACKETS 1
#define FEEDBACK_PACKET_SIZE 3
#define NUM_CAPTURE_URBS 8
#define CAPTURE_PACKET_SIZE 512
#define CAPTURE_PACKET_SIZE 4096
#define MIDI_PACKET_SIZE 9
#define MIDI_PAYLOAD_SIZE 8
@ -137,6 +136,8 @@ struct tascam_card {
struct snd_pcm *pcm;
struct snd_rawmidi *rmidi;
u16 dev_id;
u8 *scratch_buf;
struct snd_pcm_substream *playback_substream;

View File

@ -16,7 +16,7 @@ const struct snd_pcm_hardware tascam_capture_hw = {
.channels_min = NUM_CHANNELS,
.channels_max = NUM_CHANNELS,
.buffer_bytes_max = 1024 * 1024,
.period_bytes_min = 32 * BYTES_PER_FRAME,
.period_bytes_min = 48 * BYTES_PER_FRAME,
.period_bytes_max = 1024 * BYTES_PER_FRAME,
.periods_min = 2,
.periods_max = 1024,
@ -80,27 +80,26 @@ static int tascam_capture_trigger(struct snd_pcm_substream *substream, int cmd)
spin_lock_irqsave(&tascam->lock, flags);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
if (!atomic_read(&tascam->capture_active)) {
atomic_set(&tascam->capture_active, 1);
start = true;
}
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
atomic_set(&tascam->capture_active, 0);
stop = true;
break;
default:
ret = -EINVAL;
break;
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
if (!atomic_read(&tascam->capture_active)) {
atomic_set(&tascam->capture_active, 1);
start = true;
}
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
atomic_set(&tascam->capture_active, 0);
stop = true;
break;
default:
ret = -EINVAL;
break;
}
spin_unlock_irqrestore(&tascam->lock, flags);
if (stop) {
/* Ensure capture_active is updated before unlinking URBs. */
smp_mb();
for (i = 0; i < NUM_CAPTURE_URBS; i++) {
if (tascam->capture_urbs[i])
@ -114,7 +113,6 @@ static int tascam_capture_trigger(struct snd_pcm_substream *substream, int cmd)
if (usb_submit_urb(tascam->capture_urbs[i], GFP_ATOMIC) < 0) {
usb_unanchor_urb(tascam->capture_urbs[i]);
atomic_set(&tascam->capture_active, 0);
/* Ensure capture_active is updated before unlinking URBs due to submission error. */
smp_mb();
for (int j = 0; j < i; j++)
usb_unlink_urb(tascam->capture_urbs[j]);
@ -130,16 +128,26 @@ static int tascam_capture_trigger(struct snd_pcm_substream *substream, int cmd)
static inline void tascam_unpack_8bytes(const u8 *src, u8 *out_bit0, u8 *out_bit1)
{
u64 val = get_unaligned_le64(src);
u8 b0 = 0, b1 = 0;
int i;
for (i = 0; i < 8; i++) {
b0 |= ((val >> (i * 8)) & 1) << (7 - i);
b1 |= ((val >> (i * 8 + 1)) & 1) << (7 - i);
}
*out_bit0 =
(((val >> 0) & 1) << 7) |
(((val >> 8) & 1) << 6) |
(((val >> 16) & 1) << 5) |
(((val >> 24) & 1) << 4) |
(((val >> 32) & 1) << 3) |
(((val >> 40) & 1) << 2) |
(((val >> 48) & 1) << 1) |
(((val >> 56) & 1) << 0);
*out_bit0 = b0;
*out_bit1 = b1;
*out_bit1 =
(((val >> 1) & 1) << 7) |
(((val >> 9) & 1) << 6) |
(((val >> 17) & 1) << 5) |
(((val >> 25) & 1) << 4) |
(((val >> 33) & 1) << 3) |
(((val >> 41) & 1) << 2) |
(((val >> 49) & 1) << 1) |
(((val >> 57) & 1) << 0);
}
static void tascam_decode_capture_chunk(const u8 *src, u32 *dst, int frames_to_decode)
@ -151,12 +159,10 @@ static void tascam_decode_capture_chunk(const u8 *src, u32 *dst, int frames_to_d
const u8 *p_src_a = src + (i * 64);
const u8 *p_src_b = src + (i * 64) + 32;
/* Channel 1 (h0) and Channel 3 (h2) */
tascam_unpack_8bytes(p_src_a, &h[0], &h[2]);
tascam_unpack_8bytes(p_src_a + 8, &m[0], &m[2]);
tascam_unpack_8bytes(p_src_a + 16, &l[0], &l[2]);
/* Channel 2 (h1) and Channel 4 (h3) */
tascam_unpack_8bytes(p_src_b, &h[1], &h[3]);
tascam_unpack_8bytes(p_src_b + 8, &m[1], &m[3]);
tascam_unpack_8bytes(p_src_b + 16, &l[1], &l[3]);
@ -168,14 +174,6 @@ static void tascam_decode_capture_chunk(const u8 *src, u32 *dst, int frames_to_d
}
}
/**
* capture_urb_complete() - 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 resubmits the URB to receive more data.
*/
void capture_urb_complete(struct urb *urb)
{
struct tascam_card *tascam = urb->context;
@ -190,18 +188,27 @@ void capture_urb_complete(struct urb *urb)
if (!tascam)
return;
if (!tascam->dev) {
usb_unanchor_urb(urb);
atomic_dec(&tascam->active_urbs);
return;
}
if (urb->status) {
usb_unanchor_urb(urb);
atomic_dec(&tascam->active_urbs);
return;
}
substream = tascam->capture_substream;
if (!substream || !substream->runtime) {
usb_unanchor_urb(urb);
atomic_dec(&tascam->active_urbs);
return;
}
runtime = substream->runtime;
if (!runtime->dma_area) {
usb_unanchor_urb(urb);
atomic_dec(&tascam->active_urbs);
return;
}
@ -223,7 +230,6 @@ void capture_urb_complete(struct urb *urb)
}
write_pos = tascam->driver_capture_pos;
u32 *dma_ptr = (u32 *)(runtime->dma_area + frames_to_bytes(runtime, write_pos));
if (write_pos + frames_received <= buffer_size) {
@ -260,6 +266,7 @@ void capture_urb_complete(struct urb *urb)
if (usb_submit_urb(urb, GFP_ATOMIC) < 0) {
usb_unanchor_urb(urb);
atomic_dec(&tascam->active_urbs);
return;
}
}

View File

@ -22,10 +22,11 @@ static void tascam_midi_out_complete(struct urb *urb)
spin_unlock_irqrestore(&tascam->midi_lock, flags);
return;
}
spin_unlock_irqrestore(&tascam->midi_lock, flags);
if (!active)
if (!active) {
spin_unlock_irqrestore(&tascam->midi_lock, flags);
return;
}
count = snd_rawmidi_transmit(substream, tascam->midi_out_buf, MIDI_PAYLOAD_SIZE);
if (count > 0) {
@ -36,20 +37,18 @@ static void tascam_midi_out_complete(struct urb *urb)
urb->transfer_buffer_length = MIDI_PACKET_SIZE;
submit = true;
} else {
spin_lock_irqsave(&tascam->midi_lock, flags);
tascam->midi_out_active = false;
spin_unlock_irqrestore(&tascam->midi_lock, flags);
}
if (submit) {
usb_anchor_urb(urb, &tascam->midi_anchor);
if (usb_submit_urb(urb, GFP_ATOMIC) < 0) {
usb_unanchor_urb(urb);
spin_lock_irqsave(&tascam->midi_lock, flags);
tascam->midi_out_active = false;
spin_unlock_irqrestore(&tascam->midi_lock, flags);
}
}
spin_unlock_irqrestore(&tascam->midi_lock, flags);
}
static void tascam_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
@ -57,18 +56,14 @@ static void tascam_midi_output_trigger(struct snd_rawmidi_substream *substream,
struct tascam_card *tascam = substream->rmidi->private_data;
unsigned long flags;
int count;
bool do_submit = false;
spin_lock_irqsave(&tascam->midi_lock, flags);
if (up) {
spin_lock_irqsave(&tascam->midi_lock, flags);
tascam->midi_output = substream;
if (!tascam->midi_out_active) {
tascam->midi_out_active = true;
do_submit = true;
}
spin_unlock_irqrestore(&tascam->midi_lock, flags);
if (do_submit) {
count = snd_rawmidi_transmit(substream, tascam->midi_out_buf, MIDI_PAYLOAD_SIZE);
if (count > 0) {
if (count < MIDI_PAYLOAD_SIZE)
@ -80,21 +75,17 @@ static void tascam_midi_output_trigger(struct snd_rawmidi_substream *substream,
usb_anchor_urb(tascam->midi_out_urb, &tascam->midi_anchor);
if (usb_submit_urb(tascam->midi_out_urb, GFP_ATOMIC) < 0) {
usb_unanchor_urb(tascam->midi_out_urb);
spin_lock_irqsave(&tascam->midi_lock, flags);
tascam->midi_out_active = false;
spin_unlock_irqrestore(&tascam->midi_lock, flags);
}
} else {
spin_lock_irqsave(&tascam->midi_lock, flags);
tascam->midi_out_active = false;
spin_unlock_irqrestore(&tascam->midi_lock, flags);
}
}
} else {
spin_lock_irqsave(&tascam->midi_lock, flags);
tascam->midi_output = NULL;
spin_unlock_irqrestore(&tascam->midi_lock, flags);
}
spin_unlock_irqrestore(&tascam->midi_lock, flags);
}
static void tascam_midi_in_complete(struct urb *urb)

View File

@ -11,7 +11,7 @@ static int tascam_write_regs(struct tascam_card *tascam, const u16 *regs, size_t
for (i = 0; i < count; i++) {
err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
VENDOR_REQ_REGISTER_WRITE, RT_H2D_VENDOR_DEV,
regs[i], REG_VAL_ENABLE, NULL, 0, USB_CTRL_TIMEOUT_MS);
regs[i], REG_VAL_ENABLE, NULL, 0, USB_CTRL_TIMEOUT_MS);
if (err < 0)
return err;
}
@ -31,74 +31,77 @@ static int tascam_write_regs(struct tascam_card *tascam, const u16 *regs, size_t
int us144mkii_configure_device_for_rate(struct tascam_card *tascam, int rate)
{
struct usb_device *dev = tascam->dev;
u8 *rate_payload_buf;
u8 *rate_payload;
int err = 0;
const u8 *current_payload_src;
u16 rate_reg;
static const u8 payload_44100[] = { 0x44, 0xac, 0x00 };
static const u8 payload_48000[] = { 0x80, 0xbb, 0x00 };
static const u8 payload_88200[] = { 0x88, 0x58, 0x01 };
static const u8 payload_96000[] = { 0x00, 0x77, 0x01 };
if (!dev)
return -ENODEV;
switch (rate) {
case 44100:
current_payload_src = payload_44100;
rate_reg = REG_ADDR_RATE_44100;
break;
case 48000:
current_payload_src = payload_48000;
rate_reg = REG_ADDR_RATE_48000;
break;
case 88200:
current_payload_src = payload_88200;
rate_reg = REG_ADDR_RATE_88200;
break;
case 96000:
current_payload_src = payload_96000;
rate_reg = REG_ADDR_RATE_96000;
break;
default:
return -EINVAL;
case 44100:
current_payload_src = payload_44100;
rate_reg = REG_ADDR_RATE_44100;
break;
case 48000:
current_payload_src = payload_48000;
rate_reg = REG_ADDR_RATE_48000;
break;
case 88200:
current_payload_src = payload_88200;
rate_reg = REG_ADDR_RATE_88200;
break;
case 96000:
current_payload_src = payload_96000;
rate_reg = REG_ADDR_RATE_96000;
break;
default:
return -EINVAL;
}
rate_payload_buf = kmemdup(current_payload_src, 3, GFP_KERNEL);
if (!rate_payload_buf)
rate_payload = kmemdup(current_payload_src, 3, GFP_KERNEL);
if (!rate_payload)
return -ENOMEM;
err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
VENDOR_REQ_MODE_CONTROL, RT_H2D_VENDOR_DEV,
MODE_VAL_CONFIG, 0x0000, NULL, 0, USB_CTRL_TIMEOUT_MS);
if (err < 0)
goto fail;
goto out;
usb_control_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR,
RT_H2D_CLASS_EP, UAC_SAMPLING_FREQ_CONTROL,
EP_AUDIO_IN, rate_payload_buf, 3, USB_CTRL_TIMEOUT_MS);
usb_control_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR,
RT_H2D_CLASS_EP, UAC_SAMPLING_FREQ_CONTROL,
EP_AUDIO_OUT, rate_payload_buf, 3, USB_CTRL_TIMEOUT_MS);
err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR,
RT_H2D_CLASS_EP, UAC_SAMPLING_FREQ_CONTROL,
EP_AUDIO_IN, rate_payload, 3, USB_CTRL_TIMEOUT_MS);
if (err < 0)
goto out;
err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR,
RT_H2D_CLASS_EP, UAC_SAMPLING_FREQ_CONTROL,
EP_AUDIO_OUT, rate_payload, 3, USB_CTRL_TIMEOUT_MS);
if (err < 0)
goto out;
{
const u16 regs_to_write[] = {
REG_ADDR_UNKNOWN_0D, REG_ADDR_UNKNOWN_0E,
REG_ADDR_UNKNOWN_0F, rate_reg, REG_ADDR_UNKNOWN_11
REG_ADDR_INIT_0D, REG_ADDR_INIT_0E,
REG_ADDR_INIT_0F, rate_reg, REG_ADDR_INIT_11
};
err = tascam_write_regs(tascam, regs_to_write, ARRAY_SIZE(regs_to_write));
if (err < 0)
goto fail;
goto out;
}
err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
VENDOR_REQ_MODE_CONTROL, RT_H2D_VENDOR_DEV,
MODE_VAL_STREAM_START, 0x0000, NULL, 0, USB_CTRL_TIMEOUT_MS);
if (err < 0)
goto fail;
kfree(rate_payload_buf);
return 0;
fail:
kfree(rate_payload_buf);
out:
kfree(rate_payload);
return err;
}
@ -120,12 +123,20 @@ int tascam_pcm_hw_params(struct snd_pcm_substream *substream,
struct tascam_card *tascam = snd_pcm_substream_chip(substream);
unsigned int rate = params_rate(params);
int err;
unsigned long flags;
if (tascam->current_rate != rate) {
if (atomic_read(&tascam->playback_active) ||
atomic_read(&tascam->capture_active)) {
return -EBUSY;
}
spin_lock_irqsave(&tascam->lock, flags);
if (tascam->current_rate == rate) {
spin_unlock_irqrestore(&tascam->lock, flags);
return 0;
}
if (atomic_read(&tascam->playback_active) ||
atomic_read(&tascam->capture_active)) {
spin_unlock_irqrestore(&tascam->lock, flags);
return -EBUSY;
}
spin_unlock_irqrestore(&tascam->lock, flags);
usb_kill_anchored_urbs(&tascam->playback_anchor);
usb_kill_anchored_urbs(&tascam->feedback_anchor);
@ -135,12 +146,17 @@ int tascam_pcm_hw_params(struct snd_pcm_substream *substream,
err = us144mkii_configure_device_for_rate(tascam, rate);
if (err < 0) {
spin_lock_irqsave(&tascam->lock, flags);
tascam->current_rate = 0;
spin_unlock_irqrestore(&tascam->lock, flags);
return err;
}
spin_lock_irqsave(&tascam->lock, flags);
tascam->current_rate = rate;
}
return 0;
spin_unlock_irqrestore(&tascam->lock, flags);
return 0;
}
/**
@ -151,6 +167,9 @@ void tascam_stop_pcm_work_handler(struct work_struct *work)
{
struct tascam_card *tascam = container_of(work, struct tascam_card, stop_pcm_work);
if (!tascam->dev)
return;
if (tascam->playback_substream)
snd_pcm_stop(tascam->playback_substream, SNDRV_PCM_STATE_XRUN);
}

View File

@ -15,7 +15,7 @@ const struct snd_pcm_hardware tascam_playback_hw = {
.channels_min = NUM_CHANNELS,
.channels_max = NUM_CHANNELS,
.buffer_bytes_max = 1024 * 1024,
.period_bytes_min = 32 * BYTES_PER_FRAME,
.period_bytes_min = 48 * BYTES_PER_FRAME,
.period_bytes_max = 1024 * BYTES_PER_FRAME,
.periods_min = 2,
.periods_max = 1024,
@ -26,6 +26,7 @@ static int tascam_playback_open(struct snd_pcm_substream *substream)
struct tascam_card *tascam = snd_pcm_substream_chip(substream);
substream->runtime->hw = tascam_playback_hw;
tascam->playback_substream = substream;
atomic_set(&tascam->playback_active, 0);
return 0;
@ -48,7 +49,6 @@ static int tascam_playback_prepare(struct snd_pcm_substream *substream)
struct tascam_card *tascam = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
int i, u;
u32 nominal_q16 = (runtime->rate << 16) / 8000;
size_t nominal_bytes = (runtime->rate / 8000) * BYTES_PER_FRAME;
usb_kill_anchored_urbs(&tascam->playback_anchor);
@ -60,7 +60,7 @@ static int tascam_playback_prepare(struct snd_pcm_substream *substream)
tascam->feedback_synced = false;
tascam->feedback_urb_skip_count = NUM_FEEDBACK_URBS;
tascam->phase_accum = 0;
tascam->freq_q16 = nominal_q16;
tascam->freq_q16 = div_u64(((u64)runtime->rate << 16), 8000);
for (i = 0; i < NUM_FEEDBACK_URBS; i++) {
struct urb *f_urb = tascam->feedback_urbs[i];
@ -75,11 +75,11 @@ static int tascam_playback_prepare(struct snd_pcm_substream *substream)
for (u = 0; u < NUM_PLAYBACK_URBS; u++) {
struct urb *urb = tascam->playback_urbs[u];
int num_packets = PLAYBACK_URB_PACKETS;
memset(urb->transfer_buffer, 0, tascam->playback_urb_alloc_size);
urb->number_of_packets = PLAYBACK_URB_PACKETS;
urb->transfer_buffer_length = PLAYBACK_URB_PACKETS * nominal_bytes;
for (i = 0; i < PLAYBACK_URB_PACKETS; i++) {
urb->number_of_packets = num_packets;
urb->transfer_buffer_length = num_packets * nominal_bytes;
for (i = 0; i < num_packets; i++) {
urb->iso_frame_desc[i].offset = i * nominal_bytes;
urb->iso_frame_desc[i].length = nominal_bytes;
}
@ -113,27 +113,28 @@ static int tascam_playback_trigger(struct snd_pcm_substream *substream, int cmd)
spin_lock_irqsave(&tascam->lock, flags);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
if (!atomic_read(&tascam->playback_active)) {
atomic_set(&tascam->playback_active, 1);
start = true;
}
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
atomic_set(&tascam->playback_active, 0);
stop = true;
break;
default:
ret = -EINVAL;
break;
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
if (!atomic_read(&tascam->playback_active)) {
atomic_set(&tascam->playback_active, 1);
tascam->feedback_synced = false;
tascam->feedback_urb_skip_count = NUM_FEEDBACK_URBS;
start = true;
}
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
atomic_set(&tascam->playback_active, 0);
stop = true;
break;
default:
ret = -EINVAL;
break;
}
spin_unlock_irqrestore(&tascam->lock, flags);
if (stop) {
/* Ensure playback_active is updated before unlinking URBs. */
smp_mb();
for (i = 0; i < NUM_FEEDBACK_URBS; i++) {
if (tascam->feedback_urbs[i])
@ -150,9 +151,7 @@ static int tascam_playback_trigger(struct snd_pcm_substream *substream, int cmd)
usb_anchor_urb(tascam->feedback_urbs[i], &tascam->feedback_anchor);
if (usb_submit_urb(tascam->feedback_urbs[i], GFP_ATOMIC) < 0) {
usb_unanchor_urb(tascam->feedback_urbs[i]);
dev_err(&tascam->dev->dev, "Failed to submit feedback URB %d\n", i);
atomic_set(&tascam->playback_active, 0);
/* Ensure playback_active is updated before unlinking feedback URBs due to submission error. */
smp_mb();
for (int j = 0; j < i; j++)
usb_unlink_urb(tascam->feedback_urbs[j]);
@ -165,9 +164,7 @@ static int tascam_playback_trigger(struct snd_pcm_substream *substream, int cmd)
usb_anchor_urb(tascam->playback_urbs[i], &tascam->playback_anchor);
if (usb_submit_urb(tascam->playback_urbs[i], GFP_ATOMIC) < 0) {
usb_unanchor_urb(tascam->playback_urbs[i]);
dev_err(&tascam->dev->dev, "Failed to submit playback URB %d\n", i);
atomic_set(&tascam->playback_active, 0);
/* Ensure playback_active is updated before unlinking playback URBs due to submission error. */
smp_mb();
for (int j = 0; j < NUM_FEEDBACK_URBS; j++)
usb_unlink_urb(tascam->feedback_urbs[j]);
@ -182,7 +179,7 @@ static int tascam_playback_trigger(struct snd_pcm_substream *substream, int cmd)
schedule_work(&tascam->stop_work);
}
error:
error:
return ret;
}
@ -211,6 +208,7 @@ void playback_urb_complete(struct urb *urb)
return;
if (urb->status) {
usb_unanchor_urb(urb);
atomic_dec(&tascam->active_urbs);
return;
}
@ -243,6 +241,10 @@ void playback_urb_complete(struct urb *urb)
tascam->phase_accum += tascam->freq_q16;
frames_for_packet = tascam->phase_accum >> 16;
tascam->phase_accum &= 0xFFFF;
if (frames_for_packet > 13)
frames_for_packet = 13;
urb->iso_frame_desc[i].offset = total_bytes_for_urb;
urb->iso_frame_desc[i].length = frames_for_packet * BYTES_PER_FRAME;
total_bytes_for_urb += urb->iso_frame_desc[i].length;
@ -251,6 +253,7 @@ void playback_urb_complete(struct urb *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) % buffer_size;
if (total_bytes_for_urb > 0) {
@ -259,9 +262,10 @@ void playback_urb_complete(struct urb *urb)
if (offset_frames + frames_to_copy > buffer_size) {
size_t part1 = buffer_size - offset_frames;
size_t part1_bytes = frames_to_bytes(runtime, part1);
memcpy(dst_buf, runtime->dma_area + ptr_bytes, frames_to_bytes(runtime, part1));
memcpy(dst_buf + frames_to_bytes(runtime, part1), runtime->dma_area, total_bytes_for_urb - 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);
} else {
memcpy(dst_buf, runtime->dma_area + ptr_bytes, total_bytes_for_urb);
}
@ -286,6 +290,7 @@ void playback_urb_complete(struct urb *urb)
if (usb_submit_urb(urb, GFP_ATOMIC) < 0) {
usb_unanchor_urb(urb);
atomic_dec(&tascam->active_urbs);
return;
}
}
@ -305,20 +310,26 @@ void feedback_urb_complete(struct urb *urb)
struct tascam_card *tascam = urb->context;
int ret, p;
unsigned long flags;
bool playback_active;
if (!tascam)
return;
if (urb->status) {
atomic_dec(&tascam->active_urbs);
return;
}
if (!atomic_read(&tascam->playback_active)) {
usb_unanchor_urb(urb);
atomic_dec(&tascam->active_urbs);
return;
}
spin_lock_irqsave(&tascam->lock, flags);
playback_active = atomic_read(&tascam->playback_active);
if (!playback_active) {
spin_unlock_irqrestore(&tascam->lock, flags);
usb_unanchor_urb(urb);
atomic_dec(&tascam->active_urbs);
return;
}
if (tascam->feedback_urb_skip_count > 0) {
tascam->feedback_urb_skip_count--;
spin_unlock_irqrestore(&tascam->lock, flags);
@ -327,17 +338,21 @@ void feedback_urb_complete(struct urb *urb)
for (p = 0; p < urb->number_of_packets; p++) {
if (urb->iso_frame_desc[p].status == 0 && urb->iso_frame_desc[p].actual_length >= 3) {
u8 *data = (u8 *)urb->transfer_buffer + urb->iso_frame_desc[p].offset;
u32 sum = data[0] + data[1] + data[2];
u32 target_freq_q16 = ((sum * 65536) / 3) / 8;
tascam->freq_q16 = (tascam->freq_q16 * PLL_FILTER_OLD_WEIGHT + target_freq_q16 * PLL_FILTER_NEW_WEIGHT) / PLL_FILTER_DIVISOR;
u8 *data = (u8 *)urb->transfer_buffer + urb->iso_frame_desc[p].offset;
u32 sum_frames_3ms = data[0] + data[1] + data[2];
u32 target_freq_q16 = (sum_frames_3ms * 65536) / 24;
tascam->freq_q16 = (tascam->freq_q16 * PLL_FILTER_OLD_WEIGHT +
target_freq_q16 * PLL_FILTER_NEW_WEIGHT +
(PLL_FILTER_DIVISOR >> 1)) / PLL_FILTER_DIVISOR;
tascam->feedback_synced = true;
}
}
spin_unlock_irqrestore(&tascam->lock, flags);
resubmit:
resubmit:
usb_anchor_urb(urb, &tascam->feedback_anchor);
ret = usb_submit_urb(urb, GFP_ATOMIC);
if (ret < 0) {