more playback/feedback sync up to spec changes

This commit is contained in:
serifpersia 2025-07-22 12:34:38 +02:00
parent 3fc054f4c3
commit 60b0430678
1 changed files with 65 additions and 36 deletions

View File

@ -17,7 +17,7 @@ MODULE_DESCRIPTION("ALSA Driver for TASCAM US-144MKII");
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");
#define DRIVER_NAME "us144mkii" #define DRIVER_NAME "us144mkii"
#define DRIVER_VERSION "1.7.1" #define DRIVER_VERSION "1.7.2"
/* --- Module Parameters --- */ /* --- Module Parameters --- */
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
@ -93,7 +93,7 @@ enum tascam_register {
#define MIDI_OUT_BUF_SIZE 64 #define MIDI_OUT_BUF_SIZE 64
#define NUM_MIDI_OUT_URBS 4 #define NUM_MIDI_OUT_URBS 4
#define USB_CTRL_TIMEOUT_MS 1000 #define USB_CTRL_TIMEOUT_MS 1000
#define FEEDBACK_SYNC_LOSS_THRESHOLD 10 #define FEEDBACK_SYNC_LOSS_THRESHOLD 41
/* --- Audio Format Configuration --- */ /* --- Audio Format Configuration --- */
#define BYTES_PER_SAMPLE 3 #define BYTES_PER_SAMPLE 3
@ -143,6 +143,7 @@ struct tascam_card {
s32 *capture_decode_dst_block; s32 *capture_decode_dst_block;
s32 *capture_routing_buffer; s32 *capture_routing_buffer;
struct work_struct capture_work; struct work_struct capture_work;
struct work_struct start_work;
struct work_struct stop_work; struct work_struct stop_work;
/* MIDI streams */ /* MIDI streams */
@ -194,6 +195,7 @@ static void playback_urb_complete(struct urb *urb);
static void feedback_urb_complete(struct urb *urb); static void feedback_urb_complete(struct urb *urb);
static void capture_urb_complete(struct urb *urb); static void capture_urb_complete(struct urb *urb);
static void tascam_capture_work_handler(struct work_struct *work); static void tascam_capture_work_handler(struct work_struct *work);
static void tascam_start_work_handler(struct work_struct *work);
static void tascam_midi_in_urb_complete(struct urb *urb); static void tascam_midi_in_urb_complete(struct urb *urb);
static void tascam_midi_out_urb_complete(struct urb *urb); static void tascam_midi_out_urb_complete(struct urb *urb);
static void tascam_midi_out_work_handler(struct work_struct *work); static void tascam_midi_out_work_handler(struct work_struct *work);
@ -463,24 +465,32 @@ static void process_capture_routing_us144mkii(struct tascam_card *tascam,
/* --- Rate-to-Packet Fixing Data (Verified) --- */ /* --- Rate-to-Packet Fixing Data (Verified) --- */
static const unsigned int patterns_48khz[5][8] = { static const unsigned int patterns_48khz[5][8] = {
{5, 6, 6, 6, 5, 6, 6, 6}, {5, 6, 6, 6, 6, 6, 6, 6}, {5, 6, 6, 6, 6, 6, 6, 6},
{6, 6, 6, 6, 6, 6, 6, 6}, {7, 6, 6, 6, 6, 6, 6, 6}, {6, 6, 6, 6, 6, 6, 6, 6},
{7, 6, 6, 6, 7, 6, 6, 6} {6, 6, 6, 6, 6, 6, 6, 6},
{6, 6, 6, 7, 6, 6, 6, 6},
{7, 6, 6, 7, 6, 6, 7, 6}
}; };
static const unsigned int patterns_96khz[5][8] = { static const unsigned int patterns_96khz[5][8] = {
{11, 12, 12, 12, 11, 12, 12, 12}, {11, 12, 12, 12, 12, 12, 12, 12}, {11, 12, 12, 12, 12, 12, 12, 12},
{12, 12, 12, 12, 12, 12, 12, 12}, {13, 12, 12, 12, 12, 12, 12, 12}, {12, 12, 12, 12, 12, 12, 12, 12},
{13, 12, 12, 12, 13, 12, 12, 12} {12, 12, 12, 12, 12, 12, 12, 12},
{12, 12, 13, 12, 12, 12, 12, 12},
{13, 12, 12, 13, 12, 12, 13, 12}
}; };
static const unsigned int patterns_88khz[5][8] = { static const unsigned int patterns_88khz[5][8] = {
{10, 11, 11, 11, 10, 11, 11, 11}, {10, 11, 11, 11, 11, 11, 11, 11}, {10, 11, 11, 11, 11, 11, 11, 11},
{11, 11, 11, 11, 11, 11, 11, 11}, {12, 11, 11, 11, 11, 11, 11, 11}, {11, 11, 11, 11, 11, 11, 11, 11},
{12, 11, 11, 11, 12, 11, 11, 11} {11, 11, 11, 11, 11, 11, 11, 11},
{11, 11, 12, 11, 11, 11, 11, 11},
{12, 11, 11, 12, 11, 11, 12, 11}
}; };
static const unsigned int patterns_44khz[5][8] = { static const unsigned int patterns_44khz[5][8] = {
{5, 5, 5, 6, 5, 5, 5, 6}, {5, 5, 6, 5, 5, 6, 5, 6}, {5, 5, 5, 5, 5, 5, 5, 6},
{5, 6, 5, 6, 5, 6, 5, 6}, {6, 5, 6, 6, 5, 6, 5, 6}, {5, 5, 5, 6, 5, 5, 5, 6},
{6, 6, 6, 5, 6, 6, 6, 5} {5, 5, 6, 5, 6, 5, 5, 6},
{5, 6, 5, 6, 5, 6, 5, 6},
{6, 6, 6, 6, 6, 6, 6, 5}
}; };
static const struct snd_pcm_hardware tascam_pcm_hw = { static const struct snd_pcm_hardware tascam_pcm_hw = {
@ -864,23 +874,23 @@ static int tascam_pcm_hw_params(struct snd_pcm_substream *substream,
switch (rate) { switch (rate) {
case 44100: case 44100:
tascam->feedback_patterns = patterns_44khz; tascam->feedback_patterns = patterns_44khz;
tascam->feedback_base_value = 42; tascam->feedback_base_value = 43;
tascam->feedback_max_value = 46; tascam->feedback_max_value = 45;
break; break;
case 48000: case 48000:
tascam->feedback_patterns = patterns_48khz; tascam->feedback_patterns = patterns_48khz;
tascam->feedback_base_value = 46; tascam->feedback_base_value = 47;
tascam->feedback_max_value = 50; tascam->feedback_max_value = 49;
break; break;
case 88200: case 88200:
tascam->feedback_patterns = patterns_88khz; tascam->feedback_patterns = patterns_88khz;
tascam->feedback_base_value = 86; tascam->feedback_base_value = 87;
tascam->feedback_max_value = 90; tascam->feedback_max_value = 89;
break; break;
case 96000: case 96000:
tascam->feedback_patterns = patterns_96khz; tascam->feedback_patterns = patterns_96khz;
tascam->feedback_base_value = 94; tascam->feedback_base_value = 95;
tascam->feedback_max_value = 98; tascam->feedback_max_value = 97;
break; break;
default: default:
return -EINVAL; return -EINVAL;
@ -904,6 +914,7 @@ static int tascam_pcm_hw_free(struct snd_pcm_substream *substream)
return snd_pcm_lib_free_pages(substream); return snd_pcm_lib_free_pages(substream);
} }
static int tascam_playback_prepare(struct snd_pcm_substream *substream) static int tascam_playback_prepare(struct snd_pcm_substream *substream)
{ {
struct tascam_card *tascam = snd_pcm_substream_chip(substream); struct tascam_card *tascam = snd_pcm_substream_chip(substream);
@ -982,6 +993,18 @@ static void tascam_stop_work_handler(struct work_struct *work)
cancel_work_sync(&tascam->capture_work); cancel_work_sync(&tascam->capture_work);
} }
static void tascam_start_work_handler(struct work_struct *work)
{
struct tascam_card *tascam = container_of(work, struct tascam_card, start_work);
int err;
err = usb_control_msg(tascam->dev, usb_sndctrlpipe(tascam->dev, 0),
VENDOR_REQ_MODE_CONTROL, RT_H2D_VENDOR_DEV,
0x1f00, 0x0001, NULL, 0, USB_CTRL_TIMEOUT_MS);
if (err < 0)
dev_err(tascam->card->dev, "Failed to send start streaming command: %d\n", err);
}
static int tascam_pcm_trigger(struct snd_pcm_substream *substream, int cmd) static 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);
@ -1055,6 +1078,9 @@ static int tascam_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
} }
atomic_inc(&tascam->active_urbs); atomic_inc(&tascam->active_urbs);
} }
schedule_work(&tascam->start_work);
return 0; return 0;
start_rollback: start_rollback:
dev_err(tascam->card->dev, "Failed to submit URBs to start stream: %d\n", err); dev_err(tascam->card->dev, "Failed to submit URBs to start stream: %d\n", err);
@ -1146,7 +1172,8 @@ static void playback_urb_complete(struct urb *urb)
int ret, i; int ret, i;
if (urb->status) { if (urb->status) {
if (urb->status != -ENOENT && urb->status != -ECONNRESET && urb->status != -ESHUTDOWN) if (urb->status != -ENOENT && urb->status != -ECONNRESET && urb->status != -ESHUTDOWN &&
urb->status != -ENODEV)
dev_err_ratelimited(tascam->card->dev, "Playback URB failed: %d\n", urb->status); dev_err_ratelimited(tascam->card->dev, "Playback URB failed: %d\n", urb->status);
goto out; goto out;
} }
@ -1241,7 +1268,8 @@ static void feedback_urb_complete(struct urb *urb)
bool capture_period_elapsed = false; bool capture_period_elapsed = false;
if (urb->status) { if (urb->status) {
if (urb->status != -ENOENT && urb->status != -ECONNRESET && urb->status != -ESHUTDOWN) if (urb->status != -ENOENT && urb->status != -ECONNRESET && urb->status != -ESHUTDOWN &&
urb->status != -ENODEV)
dev_err_ratelimited(tascam->card->dev, "Feedback URB failed: %d\n", urb->status); dev_err_ratelimited(tascam->card->dev, "Feedback URB failed: %d\n", urb->status);
goto out; goto out;
} }
@ -1293,9 +1321,13 @@ static void feedback_urb_complete(struct urb *urb)
if (tascam->feedback_synced) { if (tascam->feedback_synced) {
tascam->feedback_consecutive_errors++; tascam->feedback_consecutive_errors++;
if (tascam->feedback_consecutive_errors > FEEDBACK_SYNC_LOSS_THRESHOLD) { if (tascam->feedback_consecutive_errors > FEEDBACK_SYNC_LOSS_THRESHOLD) {
dev_warn_ratelimited(tascam->card->dev, "Feedback sync lost! (value: %u, errors: %u)\n", dev_err(tascam->card->dev, "Fatal: Feedback sync lost. Stopping stream.\n");
feedback_value, tascam->feedback_consecutive_errors); 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; tascam->feedback_synced = false;
goto unlock_and_continue;
} }
} }
for (i = 0; i < 8; i++) { for (i = 0; i < 8; i++) {
@ -1501,7 +1533,8 @@ static void capture_urb_complete(struct urb *urb)
unsigned long flags; unsigned long flags;
if (urb->status) { if (urb->status) {
if (urb->status != -ENOENT && urb->status != -ECONNRESET && urb->status != -ESHUTDOWN) if (urb->status != -ENOENT && urb->status != -ECONNRESET && urb->status != -ESHUTDOWN &&
urb->status != -ENODEV && urb->status != -EPROTO)
dev_err_ratelimited(tascam->card->dev, "Capture URB failed: %d\n", urb->status); dev_err_ratelimited(tascam->card->dev, "Capture URB failed: %d\n", urb->status);
goto out; goto out;
} }
@ -1989,6 +2022,7 @@ static int tascam_probe(struct usb_interface *intf, const struct usb_device_id *
spin_lock_init(&tascam->lock); spin_lock_init(&tascam->lock);
atomic_set(&tascam->active_urbs, 0); atomic_set(&tascam->active_urbs, 0);
INIT_WORK(&tascam->capture_work, tascam_capture_work_handler); INIT_WORK(&tascam->capture_work, tascam_capture_work_handler);
INIT_WORK(&tascam->start_work, tascam_start_work_handler);
INIT_WORK(&tascam->stop_work, tascam_stop_work_handler); INIT_WORK(&tascam->stop_work, tascam_stop_work_handler);
tascam->line_out_source = 0; tascam->line_out_source = 0;
tascam->digital_out_source = 1; tascam->digital_out_source = 1;
@ -2049,16 +2083,11 @@ static int tascam_probe(struct usb_interface *intf, const struct usb_device_id *
err = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), VENDOR_REQ_MODE_CONTROL, err = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), VENDOR_REQ_MODE_CONTROL,
RT_D2H_VENDOR_DEV, MODE_VAL_HANDSHAKE_READ, 0x0000, RT_D2H_VENDOR_DEV, MODE_VAL_HANDSHAKE_READ, 0x0000,
handshake_buf, 1, USB_CTRL_TIMEOUT_MS); handshake_buf, 1, USB_CTRL_TIMEOUT_MS);
if (err == 1 && handshake_buf[0] == HANDSHAKE_SUCCESS_VAL) { if (err < 0 || (handshake_buf[0] != HANDSHAKE_SUCCESS_VAL && handshake_buf[0] != 0x00)) {
dev_info(&intf->dev, "Handshake successful.\n");
} else {
if (handshake_buf[0] != HANDSHAKE_SUCCESS_VAL &&
handshake_buf[0] != MODE_VAL_STREAM_START &&
handshake_buf[0] != 0x32) {
dev_warn(&intf->dev, "Handshake failed with unexpected value (err %d, val 0x%02x), continuing anyway.\n", dev_warn(&intf->dev, "Handshake failed with unexpected value (err %d, val 0x%02x), continuing anyway.\n",
err, (unsigned int)(err > 0 ? handshake_buf[0] : 0)); err, (unsigned int)(err > 0 ? handshake_buf[0] : 0));
} }
}
kfree(handshake_buf); kfree(handshake_buf);
/* /*