diff --git a/us144mkii.c b/us144mkii.c index 5e5f2a3..1d5fca7 100644 --- a/us144mkii.c +++ b/us144mkii.c @@ -116,7 +116,7 @@ int tascam_alloc_urbs(struct tascam_card *tascam) tascam->feedback_urbs[i] = urb; urb->transfer_buffer = usb_alloc_coherent(tascam->dev, tascam->feedback_urb_alloc_size, - GFP_KERNEL, &urb->transfer_dma); + GFP_KERNEL, &urb->transfer_dma); if (!urb->transfer_buffer) return -ENOMEM; urb->dev = tascam->dev; @@ -226,9 +226,6 @@ static int tascam_probe(struct usb_interface *intf, const struct usb_device_id * int err; int idx; - char *handshake_buf __free(kfree) = NULL; - - if (intf->cur_altsetting->desc.bInterfaceNumber == 1) return 0; @@ -242,25 +239,6 @@ static int tascam_probe(struct usb_interface *intf, const struct usb_device_id * return -ENOENT; } - handshake_buf = kmalloc(1, GFP_KERNEL); - if (!handshake_buf) { - atomic_dec(&dev_idx); - return -ENOMEM; - } - - err = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - VENDOR_REQ_MODE_CONTROL, RT_D2H_VENDOR_DEV, - MODE_VAL_HANDSHAKE_READ, 0x0000, handshake_buf, 1, - USB_CTRL_TIMEOUT_MS); - if (err < 0) { - dev_err(&dev->dev, "Handshake failed: %d\n", err); - atomic_dec(&dev_idx); - return err; - } - - usb_set_interface(dev, 0, 1); - usb_set_interface(dev, 1, 1); - err = snd_card_new(&dev->dev, index[idx], id[idx], THIS_MODULE, sizeof(struct tascam_card), &card); if (err < 0) { @@ -312,6 +290,33 @@ static int tascam_probe(struct usb_interface *intf, const struct usb_device_id * if (err < 0) goto free_card; + tascam->scratch_buf = devm_kzalloc(&dev->dev, 4, GFP_KERNEL); + if (!tascam->scratch_buf) { + err = -ENOMEM; + goto free_card; + } + + err = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + VENDOR_REQ_MODE_CONTROL, RT_D2H_VENDOR_DEV, + MODE_VAL_HANDSHAKE_READ, 0x0000, tascam->scratch_buf, 1, + USB_CTRL_TIMEOUT_MS); + if (err < 0) { + dev_err(&dev->dev, "Handshake failed: %d\n", err); + goto free_card; + } + + err = usb_set_interface(dev, 0, 1); + if (err < 0) { + dev_err(&dev->dev, "Failed to set interface 0: %d\n", err); + goto free_card; + } + + err = usb_set_interface(dev, 1, 1); + if (err < 0) { + dev_err(&dev->dev, "Failed to set interface 1: %d\n", err); + goto free_card; + } + if (us144mkii_configure_device_for_rate(tascam, 48000) < 0) dev_warn(&dev->dev, "Failed to initialize device at 48khz\n"); else diff --git a/us144mkii.h b/us144mkii.h index f600be6..ead811c 100644 --- a/us144mkii.h +++ b/us144mkii.h @@ -64,12 +64,12 @@ enum tascam_register { #define REG_VAL_ENABLE 0x0101 -#define NUM_PLAYBACK_URBS 8 -#define PLAYBACK_URB_PACKETS 2 +#define NUM_PLAYBACK_URBS 4 +#define PLAYBACK_URB_PACKETS 4 #define NUM_FEEDBACK_URBS 2 #define FEEDBACK_URB_PACKETS 1 #define FEEDBACK_PACKET_SIZE 3 -#define NUM_CAPTURE_URBS 4 +#define NUM_CAPTURE_URBS 8 #define CAPTURE_PACKET_SIZE 512 #define MIDI_PACKET_SIZE 9 @@ -92,6 +92,7 @@ enum tascam_register { * @card: pointer to the ALSA sound card * @pcm: pointer to the ALSA PCM device * @rmidi: pointer to the ALSA raw MIDI device + * @scratch_buf: temporary buffer for control messages * @playback_substream: pointer to the PCM playback substream * @capture_substream: pointer to the PCM capture substream * @playback_urbs: array of URBs for PCM playback @@ -136,6 +137,8 @@ struct tascam_card { struct snd_pcm *pcm; struct snd_rawmidi *rmidi; + u8 *scratch_buf; + struct snd_pcm_substream *playback_substream; struct snd_pcm_substream *capture_substream; diff --git a/us144mkii_capture.c b/us144mkii_capture.c index ae6f974..f56eb14 100644 --- a/us144mkii_capture.c +++ b/us144mkii_capture.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only // Copyright (c) 2025 Ĺ erif Rami +#include #include "us144mkii_pcm.h" const struct snd_pcm_hardware tascam_capture_hw = { @@ -35,6 +36,8 @@ static int tascam_capture_close(struct snd_pcm_substream *substream) { struct tascam_card *tascam = snd_pcm_substream_chip(substream); + atomic_set(&tascam->capture_active, 0); + usb_kill_anchored_urbs(&tascam->capture_anchor); tascam->capture_substream = NULL; return 0; } @@ -97,7 +100,7 @@ static int tascam_capture_trigger(struct snd_pcm_substream *substream, int cmd) spin_unlock_irqrestore(&tascam->lock, flags); if (stop) { - /* Ensure capture_active is updated before unlinking URBs */ + /* Ensure capture_active is updated before unlinking URBs. */ smp_mb(); for (i = 0; i < NUM_CAPTURE_URBS; i++) { if (tascam->capture_urbs[i]) @@ -111,7 +114,7 @@ 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 cleared before unlinking URBs */ + /* 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]); @@ -124,46 +127,44 @@ static int tascam_capture_trigger(struct snd_pcm_substream *substream, int cmd) return ret; } -static inline u8 tascam_pack_byte(const u8 *src, int bit_offset) +static inline void tascam_unpack_8bytes(const u8 *src, u8 *out_bit0, u8 *out_bit1) { - return (((src[0] >> bit_offset) & 1) << 7) | - (((src[1] >> bit_offset) & 1) << 6) | - (((src[2] >> bit_offset) & 1) << 5) | - (((src[3] >> bit_offset) & 1) << 4) | - (((src[4] >> bit_offset) & 1) << 3) | - (((src[5] >> bit_offset) & 1) << 2) | - (((src[6] >> bit_offset) & 1) << 1) | - (((src[7] >> bit_offset) & 1)); + 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 = b0; + *out_bit1 = b1; } static void tascam_decode_capture_chunk(const u8 *src, u32 *dst, int frames_to_decode) { - int frame; - u8 h, m, l; + int i; + u8 h[4], m[4], l[4]; - for (frame = 0; frame < frames_to_decode; frame++) { - const u8 *p_src_a = src + (frame * 64); - const u8 *p_src_b = src + (frame * 64) + 32; + for (i = 0; i < frames_to_decode; i++) { + const u8 *p_src_a = src + (i * 64); + const u8 *p_src_b = src + (i * 64) + 32; - h = tascam_pack_byte(p_src_a, 0); - m = tascam_pack_byte(p_src_a + 8, 0); - l = tascam_pack_byte(p_src_a + 16, 0); - *dst++ = (h << 24) | (m << 16) | (l << 8); + /* 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]); - h = tascam_pack_byte(p_src_b, 0); - m = tascam_pack_byte(p_src_b + 8, 0); - l = tascam_pack_byte(p_src_b + 16, 0); - *dst++ = (h << 24) | (m << 16) | (l << 8); + /* 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]); - h = tascam_pack_byte(p_src_a, 1); - m = tascam_pack_byte(p_src_a + 8, 1); - l = tascam_pack_byte(p_src_a + 16, 1); - *dst++ = (h << 24) | (m << 16) | (l << 8); - - h = tascam_pack_byte(p_src_b, 1); - m = tascam_pack_byte(p_src_b + 8, 1); - l = tascam_pack_byte(p_src_b + 16, 1); - *dst++ = (h << 24) | (m << 16) | (l << 8); + *dst++ = (h[0] << 24) | (m[0] << 16) | (l[0] << 8); + *dst++ = (h[1] << 24) | (m[1] << 16) | (l[1] << 8); + *dst++ = (h[2] << 24) | (m[2] << 16) | (l[2] << 8); + *dst++ = (h[3] << 24) | (m[3] << 16) | (l[3] << 8); } } @@ -200,18 +201,21 @@ void capture_urb_complete(struct urb *urb) } substream = tascam->capture_substream; - if (!substream) { + if (!substream || !substream->runtime) { atomic_dec(&tascam->active_urbs); return; } runtime = substream->runtime; - if (!runtime) { + if (!runtime->dma_area) { atomic_dec(&tascam->active_urbs); return; } buffer_size = runtime->buffer_size; period_size = runtime->period_size; + + if (urb->actual_length % 64 != 0) + dev_warn_ratelimited(&tascam->dev->dev, "Unaligned capture packet size: %d\n", urb->actual_length); frames_received = urb->actual_length / 64; if (frames_received > 0) { @@ -252,8 +256,11 @@ void capture_urb_complete(struct urb *urb) snd_pcm_period_elapsed(substream); } - if (usb_submit_urb(urb, GFP_ATOMIC) < 0) + usb_anchor_urb(urb, &tascam->capture_anchor); + if (usb_submit_urb(urb, GFP_ATOMIC) < 0) { + usb_unanchor_urb(urb); atomic_dec(&tascam->active_urbs); + } } const struct snd_pcm_ops tascam_capture_ops = { diff --git a/us144mkii_midi.c b/us144mkii_midi.c index a732a34..005b301 100644 --- a/us144mkii_midi.c +++ b/us144mkii_midi.c @@ -40,7 +40,9 @@ static void tascam_midi_out_complete(struct urb *urb) } 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); @@ -111,6 +113,7 @@ static void tascam_midi_in_complete(struct urb *urb) } } + usb_anchor_urb(urb, &tascam->midi_anchor); if (usb_submit_urb(urb, GFP_ATOMIC) < 0) usb_unanchor_urb(urb); } diff --git a/us144mkii_playback.c b/us144mkii_playback.c index 5c037c2..f2ed42c 100644 --- a/us144mkii_playback.c +++ b/us144mkii_playback.c @@ -35,7 +35,10 @@ static int tascam_playback_close(struct snd_pcm_substream *substream) { struct tascam_card *tascam = snd_pcm_substream_chip(substream); + atomic_set(&tascam->playback_active, 0); cancel_work_sync(&tascam->stop_pcm_work); + usb_kill_anchored_urbs(&tascam->playback_anchor); + usb_kill_anchored_urbs(&tascam->feedback_anchor); tascam->playback_substream = NULL; return 0; } @@ -130,7 +133,7 @@ static int tascam_playback_trigger(struct snd_pcm_substream *substream, int cmd) spin_unlock_irqrestore(&tascam->lock, flags); if (stop) { - /* Ensure playback_active is updated before unlinking URBs */ + /* Ensure playback_active is updated before unlinking URBs. */ smp_mb(); for (i = 0; i < NUM_FEEDBACK_URBS; i++) { if (tascam->feedback_urbs[i]) @@ -149,7 +152,7 @@ static int tascam_playback_trigger(struct snd_pcm_substream *substream, int cmd) 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 cleared before unlinking URBs */ + /* 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]); @@ -164,7 +167,7 @@ static int tascam_playback_trigger(struct snd_pcm_substream *substream, int cmd) 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 cleared before unlinking URBs */ + /* 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]); @@ -217,12 +220,12 @@ void playback_urb_complete(struct urb *urb) } substream = tascam->playback_substream; - if (!substream) { + if (!substream || !substream->runtime) { atomic_dec(&tascam->active_urbs); return; } runtime = substream->runtime; - if (!runtime) { + if (!runtime->dma_area) { atomic_dec(&tascam->active_urbs); return; } @@ -278,8 +281,11 @@ void playback_urb_complete(struct urb *urb) if (need_period_elapsed) snd_pcm_period_elapsed(substream); - if (usb_submit_urb(urb, GFP_ATOMIC) < 0) + usb_anchor_urb(urb, &tascam->playback_anchor); + if (usb_submit_urb(urb, GFP_ATOMIC) < 0) { + usb_unanchor_urb(urb); atomic_dec(&tascam->active_urbs); + } } /** @@ -331,6 +337,7 @@ void feedback_urb_complete(struct urb *urb) spin_unlock_irqrestore(&tascam->lock, flags); resubmit: + usb_anchor_urb(urb, &tascam->feedback_anchor); ret = usb_submit_urb(urb, GFP_ATOMIC); if (ret < 0) { usb_unanchor_urb(urb);