From ab377f904c3cd2f0c9f8130680540c14d3ca366b Mon Sep 17 00:00:00 2001 From: serifpersia Date: Mon, 30 Jun 2025 14:51:17 +0200 Subject: [PATCH] feat:stability and spec, new libusb tool for testing audio performance --- run_tascam_streamer.sh | 118 +++++++++ tascam_fifo_streamer.c | 530 +++++++++++++++++++++++++++++++++++++++++ tascam_streamer | Bin 0 -> 35424 bytes us144mkii.c | 278 ++++++++++++++------- us144mkii.ko | Bin 413680 -> 411432 bytes 5 files changed, 843 insertions(+), 83 deletions(-) create mode 100755 run_tascam_streamer.sh create mode 100644 tascam_fifo_streamer.c create mode 100755 tascam_streamer diff --git a/run_tascam_streamer.sh b/run_tascam_streamer.sh new file mode 100755 index 0000000..9971877 --- /dev/null +++ b/run_tascam_streamer.sh @@ -0,0 +1,118 @@ +#!/bin/bash + +# MIT License +# Copyright (c) 2025 serifpersia +# +# Interactive launcher for the TASCAM US-144MKII FIFO streamer. +# Prompts for sample rate, latency profile, and logging options, +# then configures PulseAudio and the C streamer binary accordingly. + +# --- Configuration --- +SINK_NAME="TASCAM-US144MKII-OUT" +FIFO_PLAYBACK_PATH="/tmp/tascam-audio-playback" +STREAMER_BINARY="./tascam_streamer" # Assumes the C program is in the same directory +CHANNELS="2" +FORMAT="s24le" + +# --- Cleanup Function --- +cleanup() { + echo "" + echo "--- Running cleanup... ---" + + pkill -f "$STREAMER_BINARY" 2>/dev/null + sleep 0.5 + + echo "Unloading PulseAudio module..." + pactl unload-module module-pipe-sink 2>/dev/null + + echo "Removing FIFO file..." + rm -f "$FIFO_PLAYBACK_PATH" + + echo "--- Cleanup complete. ---" + exit 0 +} + +# Trap signals to ensure cleanup runs +trap cleanup SIGINT TERM EXIT + +# --- Interactive Setup --- +echo "--- TASCAM Streamer Interactive Setup ---" + +# 1. Select Sample Rate +rates=("44100" "48000" "88200" "96000") +PS3="Please select a sample rate: " +select rate_choice in "${rates[@]}"; do + if [[ -n "$rate_choice" ]]; then + SELECTED_RATE="$rate_choice" + echo "Selected rate: $SELECTED_RATE Hz" + break + else + echo "Invalid selection. Please try again." + fi +done +echo "" + +# 2. Select Latency Profile +profiles=("0: Lowest" "1: Low" "2: Normal" "3: High" "4: Highest") +PS3="Please select a latency profile: " +select profile_choice in "${profiles[@]}"; do + if [[ -n "$profile_choice" ]]; then + SELECTED_PROFILE_INDEX=$((REPLY - 1)) + echo "Selected profile: $profile_choice" + break + else + echo "Invalid selection. Please try again." + fi +done +echo "" + +# 3. Select Logging Mode +LOG_MODE_FLAG="" +LOG_INTERVAL_FLAG="" +read -p "Use minimal logging instead of the live dashboard? (y/n) [default: n]: " minimal_choice +if [[ "$minimal_choice" == "y" || "$minimal_choice" == "Y" ]]; then + LOG_MODE_FLAG="--minimal-log" + read -p "Enter log interval in milliseconds [default: 1000]: " interval_ms + if [[ -z "$interval_ms" ]]; then + interval_ms=1000 # Set default if user enters nothing + fi + LOG_INTERVAL_FLAG="--log-interval $interval_ms" + LOG_MODE_SUMMARY="Minimal (updates every ${interval_ms}ms)" +else + LOG_MODE_SUMMARY="Live Dashboard (updates every 100ms)" +fi + +echo "---------------------------------------------" +echo "Configuration:" +echo " Rate: $SELECTED_RATE Hz" +echo " Profile: $SELECTED_PROFILE_INDEX ($profile_choice)" +echo " Logging: $LOG_MODE_SUMMARY" +echo "---------------------------------------------" + +# --- Main Execution --- +rm -f "$FIFO_PLAYBACK_PATH" +echo "Creating playback FIFO at $FIFO_PLAYBACK_PATH..." +mkfifo "$FIFO_PLAYBACK_PATH" + +echo "Loading PulseAudio pipe-sink module..." +SINK_MODULE_ID=$(pactl load-module module-pipe-sink file="$FIFO_PLAYBACK_PATH" sink_name="$SINK_NAME" format=$FORMAT rate=$SELECTED_RATE channels=$CHANNELS) +if [ -z "$SINK_MODULE_ID" ]; then + echo "Error: Failed to load PulseAudio pipe-sink module. Aborting." + exit 1 +fi +echo "Playback Sink ('$SINK_NAME') loaded with ID: $SINK_MODULE_ID" +echo "You can now select '$SINK_NAME' as an output device in your sound settings." +echo "---------------------------------------------" + +echo "Starting C streamer binary..." +# Launch the C program with all selected arguments. +# The log flags will be empty strings if not selected, which bash ignores. +sudo "$STREAMER_BINARY" \ + -r "$SELECTED_RATE" \ + -p "$SELECTED_PROFILE_INDEX" \ + --pipe "$FIFO_PLAYBACK_PATH" \ + $LOG_MODE_FLAG \ + $LOG_INTERVAL_FLAG + +echo "Streamer exited. Waiting for cleanup..." +wait diff --git a/tascam_fifo_streamer.c b/tascam_fifo_streamer.c new file mode 100644 index 0000000..3161273 --- /dev/null +++ b/tascam_fifo_streamer.c @@ -0,0 +1,530 @@ +// MIT License +// Copyright (c) 2025 serifpersia +// +// Final verification tool by an AI assistant. This version is a fully functional, +// multi-rate, multi-profile FIFO audio player with selectable logging modes for +// either deep diagnostics or minimal-overhead monitoring. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// --- Device and Endpoint Configuration --- +#define TASCAM_VID 0x0644 +#define TASCAM_PID 0x8020 +#define EP_AUDIO_OUT 0x02 +#define EP_PLAYBACK_FEEDBACK 0x81 +#define EP_CAPTURE_DATA 0x86 + +// --- USB Request Types --- +#define RT_H2D_CLASS_EP 0x22 +#define RT_D2H_VENDOR_DEV 0xc0 +#define RT_H2D_VENDOR_DEV 0x40 + +// --- UAC / Vendor Requests --- +#define UAC_SET_CUR 0x01 +#define UAC_SAMPLING_FREQ_CONTROL 0x0100 +#define VENDOR_REQ_REGISTER_WRITE 65 +#define VENDOR_REQ_MODE_CONTROL 73 + +// --- Streaming Configuration --- +#define BYTES_PER_SAMPLE 3 +#define DEVICE_CHANNELS 4 +#define PIPE_CHANNELS 2 +#define DEVICE_FRAME_SIZE (DEVICE_CHANNELS * BYTES_PER_SAMPLE) +#define PIPE_FRAME_SIZE (PIPE_CHANNELS * BYTES_PER_SAMPLE) +#define ISO_PLAYBACK_PACKETS_PER_TRANSFER 40 +#define NUM_PLAYBACK_TRANSFERS 4 +#define NUM_FEEDBACK_TRANSFERS 4 +#define FEEDBACK_PACKET_SIZE 3 +#define MAX_FEEDBACK_PACKETS_PER_URB 5 +#define USB_TIMEOUT 1000 + +// --- Feedback Synchronization Engine --- +#define FEEDBACK_ACCUMULATOR_SIZE 128 +#define WARMUP_THRESHOLD (ISO_PLAYBACK_PACKETS_PER_TRANSFER * 2) + +// --- Data Structures for Rate/Profile Configuration --- +struct latency_profile_config { + const char *name; + int feedback_packets_per_urb; + int asio_buffer_size_frames; + double expected_feedback_ms; +}; + +struct sample_rate_config { + int rate; + const unsigned char rate_data[3]; + uint16_t rate_vendor_wValue; + const unsigned int (*feedback_patterns)[8]; + unsigned int feedback_base_value; + unsigned int feedback_max_value; + const struct latency_profile_config profiles[5]; +}; + +// --- Pre-calculated Pattern Tables --- +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, 6, 5, 6, 5, 6, 5, 6}, {6, 5, 6, 6, 5, 6, 5, 6}, + {6, 6, 6, 5, 6, 6, 6, 5} +}; +static const unsigned int patterns_48khz[5][8] = { + {5, 6, 6, 6, 5, 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}, + {7, 6, 6, 6, 7, 6, 6, 6} +}; +static const unsigned int patterns_88khz[5][8] = { + {10, 11, 11, 11, 10, 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}, + {12, 11, 11, 11, 12, 11, 11, 11} +}; +static const unsigned int patterns_96khz[5][8] = { + {11, 12, 12, 12, 11, 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}, + {13, 12, 12, 12, 13, 12, 12, 12} +}; + +// --- Global Configuration Table --- +static const struct sample_rate_config g_rate_configs[] = { + { 44100, {0x44, 0xac, 0x00}, 0x1000, patterns_44khz, 42, 46, { {"Lowest",1,49,2.0}, {"Low",1,64,2.0}, {"Normal",2,128,2.0}, {"High",5,256,5.0}, {"Highest",5,512,5.0} } }, + { 48000, {0x80, 0xbb, 0x00}, 0x1002, patterns_48khz, 46, 50, { {"Lowest",1,48,1.0}, {"Low",1,64,2.0}, {"Normal",2,128,2.0}, {"High",5,256,5.0}, {"Highest",5,512,5.0} } }, + { 88200, {0x88, 0x58, 0x01}, 0x1008, patterns_88khz, 86, 90, { {"Lowest",1,98,1.0}, {"Low",1,128,2.0}, {"Normal",2,256,2.0}, {"High",5,512,5.0}, {"Highest",5,1024,5.0} } }, + { 96000, {0x00, 0x77, 0x01}, 0x100a, patterns_96khz, 94, 98, { {"Lowest",1,96,1.0}, {"Low",1,128,2.0}, {"Normal",2,256,2.0}, {"High",5,512,5.0}, {"Highest",5,1024,5.0} } } +}; +#define NUM_SUPPORTED_RATES (sizeof(g_rate_configs) / sizeof(g_rate_configs[0])) +#define NUM_PROFILES 5 + +// --- Global State --- +static volatile bool is_running = true; + +struct stream_state { + int fifo_fd; + pthread_mutex_t lock; + const struct sample_rate_config *rate_cfg; + const struct latency_profile_config *profile_cfg; + unsigned int feedback_accumulator_pattern[FEEDBACK_ACCUMULATOR_SIZE]; + unsigned int feedback_pattern_out_idx; + unsigned int feedback_pattern_in_idx; + bool feedback_synced; + bool feedback_warmed_up; + int last_feedback_value; + struct timeval last_feedback_completion_time; + double last_feedback_interval_ms; + double min_feedback_interval_ms; + double max_feedback_interval_ms; + double avg_feedback_interval_sum; + unsigned long feedback_interval_count; + unsigned long underrun_count; + unsigned long overrun_count; +}; + +struct logging_thread_args { + struct stream_state *state; + bool minimal_log; + int log_interval_ms; +}; + +// --- Function Prototypes --- +void print_usage(const char *prog_name); +int perform_initialization_sequence(libusb_device_handle *handle, const struct sample_rate_config *rate_config); +static void LIBUSB_CALL iso_playback_callback(struct libusb_transfer *transfer); +static void LIBUSB_CALL feedback_callback(struct libusb_transfer *transfer); +void *logging_thread_func(void *arg); +double timeval_diff_ms(struct timeval *start, struct timeval *end); + +void sigint_handler(int signum) { + if (is_running) { + printf("\n\n\n\n\nCtrl+C detected, stopping...\n"); + is_running = false; + } +} + +int main(int argc, char *argv[]) { + int sample_rate = 0; + int profile_index = -1; + const char *pipe_path = NULL; + bool minimal_log = false; + int log_interval_ms = 100; // Default to 100ms for dashboard + + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "-r") == 0 && i + 1 < argc) sample_rate = atoi(argv[++i]); + else if (strcmp(argv[i], "-p") == 0 && i + 1 < argc) profile_index = atoi(argv[++i]); + else if (strcmp(argv[i], "--pipe") == 0 && i + 1 < argc) pipe_path = argv[++i]; + else if (strcmp(argv[i], "--minimal-log") == 0) minimal_log = true; + else if (strcmp(argv[i], "--log-interval") == 0 && i + 1 < argc) log_interval_ms = atoi(argv[++i]); + } + + if (sample_rate == 0 || profile_index < 0 || !pipe_path) { + print_usage(argv[0]); + return 1; + } + + const struct sample_rate_config *rate_config = NULL; + for (unsigned int i = 0; i < NUM_SUPPORTED_RATES; i++) { + if (g_rate_configs[i].rate == sample_rate) { + rate_config = &g_rate_configs[i]; + break; + } + } + + if (!rate_config) { + fprintf(stderr, "Error: Sample rate %d is not supported.\n", sample_rate); + print_usage(argv[0]); + return 1; + } + if (profile_index >= NUM_PROFILES) { + fprintf(stderr, "Error: Invalid profile index %d.\n", profile_index); + print_usage(argv[0]); + return 1; + } + const struct latency_profile_config *profile_config = &rate_config->profiles[profile_index]; + + libusb_device_handle *handle = NULL; + struct libusb_transfer *playback_transfers[NUM_PLAYBACK_TRANSFERS] = {0}; + struct libusb_transfer *feedback_transfers[NUM_FEEDBACK_TRANSFERS] = {0}; + struct stream_state state = { .fifo_fd = -1 }; + struct logging_thread_args log_args = { &state, minimal_log, log_interval_ms }; + pthread_t logging_thread = 0; + bool kernel_driver_was_active[2] = {false, false}; + int r = 0; + + const int max_frames_per_packet = (rate_config->rate / 8000) + 2; + const int playback_packet_max_size = max_frames_per_packet * DEVICE_FRAME_SIZE; + const int playback_transfer_size = playback_packet_max_size * ISO_PLAYBACK_PACKETS_PER_TRANSFER; + const int feedback_transfer_size = FEEDBACK_PACKET_SIZE * MAX_FEEDBACK_PACKETS_PER_URB; + + printf("--- TASCAM US-144MKII FIFO Streamer ---\n"); + printf("Profile: %d, Rate: %d Hz, Latency: %s (%d-sample buffer)\n", + profile_index, rate_config->rate, profile_config->name, profile_config->asio_buffer_size_frames); + printf("Config: Feedback URB contains %d packet(s), expected interval %.1f ms.\n", + profile_config->feedback_packets_per_urb, profile_config->expected_feedback_ms); + printf("Pipe: Reading 24-bit stereo audio from %s\n", pipe_path); + + pthread_mutex_init(&state.lock, NULL); + state.rate_cfg = rate_config; + state.profile_cfg = profile_config; + state.min_feedback_interval_ms = DBL_MAX; + + state.fifo_fd = open(pipe_path, O_RDONLY | O_NONBLOCK); + if (state.fifo_fd < 0) { + perror("Error opening FIFO pipe"); + return 1; + } + + signal(SIGINT, sigint_handler); + if (libusb_init(NULL) < 0) { r = 1; goto cleanup; } + + handle = libusb_open_device_with_vid_pid(NULL, TASCAM_VID, TASCAM_PID); + if (!handle) { fprintf(stderr, "Device not found\n"); r = 1; goto cleanup; } + + for (int i = 0; i < 2; i++) { + if (libusb_kernel_driver_active(handle, i)) { + kernel_driver_was_active[i] = true; + if ((r = libusb_detach_kernel_driver(handle, i)) != 0) { + fprintf(stderr, "Could not detach kernel driver for interface %d: %s\n", i, libusb_error_name(r)); + r = 1; goto cleanup; + } + } + } + + if (perform_initialization_sequence(handle, rate_config) != 0) { + fprintf(stderr, "Device configuration failed.\n"); r = 1; goto cleanup; + } + + printf("Starting streams... (waiting for buffer warm-up)\n"); + for (int i = 0; i < NUM_PLAYBACK_TRANSFERS; i++) { + playback_transfers[i] = libusb_alloc_transfer(ISO_PLAYBACK_PACKETS_PER_TRANSFER); + unsigned char *buf = malloc(playback_transfer_size); + memset(buf, 0, playback_transfer_size); + libusb_fill_iso_transfer(playback_transfers[i], handle, EP_AUDIO_OUT, buf, playback_transfer_size, ISO_PLAYBACK_PACKETS_PER_TRANSFER, iso_playback_callback, &state, USB_TIMEOUT); + int nominal_packet_size = (rate_config->rate / 8000) * DEVICE_FRAME_SIZE; + libusb_set_iso_packet_lengths(playback_transfers[i], nominal_packet_size); + libusb_submit_transfer(playback_transfers[i]); + } + + for (int i = 0; i < NUM_FEEDBACK_TRANSFERS; i++) { + feedback_transfers[i] = libusb_alloc_transfer(profile_config->feedback_packets_per_urb); + unsigned char *buf = malloc(feedback_transfer_size); + libusb_fill_iso_transfer(feedback_transfers[i], handle, EP_PLAYBACK_FEEDBACK, buf, feedback_transfer_size, profile_config->feedback_packets_per_urb, feedback_callback, &state, USB_TIMEOUT); + libusb_set_iso_packet_lengths(feedback_transfers[i], FEEDBACK_PACKET_SIZE); + libusb_submit_transfer(feedback_transfers[i]); + } + + if (pthread_create(&logging_thread, NULL, logging_thread_func, &log_args) != 0) { + fprintf(stderr, "Failed to create logging thread.\n"); + is_running = false; + } + + printf("Draining stale data from FIFO pipe to ensure stream alignment...\n"); + char drain_buf[4096]; + while (read(state.fifo_fd, drain_buf, sizeof(drain_buf)) > 0); + + printf("\n--- Playback active. Press Ctrl+C to stop. ---\n"); + if (!minimal_log) printf("\n\n\n\n\n"); // Space for dashboard + + while (is_running) { + libusb_handle_events_timeout_completed(NULL, &(struct timeval){0, 100000}, NULL); + } + +cleanup: + is_running = false; + if (logging_thread) pthread_join(logging_thread, NULL); + for (int i = 0; i < NUM_PLAYBACK_TRANSFERS; i++) if (playback_transfers[i]) libusb_cancel_transfer(playback_transfers[i]); + for (int i = 0; i < NUM_FEEDBACK_TRANSFERS; i++) if (feedback_transfers[i]) libusb_cancel_transfer(feedback_transfers[i]); + if (handle) { + struct timeval tv = {0, 100000}; + libusb_handle_events_timeout_completed(NULL, &tv, NULL); + libusb_release_interface(handle, 1); + libusb_release_interface(handle, 0); + for(int i = 0; i < 2; i++) if (kernel_driver_was_active[i]) libusb_attach_kernel_driver(handle, i); + libusb_close(handle); + } + for (int i = 0; i < NUM_PLAYBACK_TRANSFERS; i++) if (playback_transfers[i]) { if (playback_transfers[i]->buffer) free(playback_transfers[i]->buffer); libusb_free_transfer(playback_transfers[i]); } + for (int i = 0; i < NUM_FEEDBACK_TRANSFERS; i++) if (feedback_transfers[i]) { if (feedback_transfers[i]->buffer) free(feedback_transfers[i]->buffer); libusb_free_transfer(feedback_transfers[i]); } + if (state.fifo_fd >= 0) close(state.fifo_fd); + pthread_mutex_destroy(&state.lock); + if (r != 1) libusb_exit(NULL); + printf("Cleanup complete.\n"); + return r; +} + +void print_usage(const char *prog_name) { + fprintf(stderr, "Usage: %s -r -p --pipe [options]\n", prog_name); + fprintf(stderr, "Required:\n"); + fprintf(stderr, " -r : 44100, 48000, 88200, 96000\n"); + fprintf(stderr, " -p : 0-4 (Lowest, Low, Normal, High, Highest)\n"); + fprintf(stderr, " --pipe : Path to the named pipe for audio input\n"); + fprintf(stderr, "Optional:\n"); + fprintf(stderr, " --minimal-log : Switch to a simple, single-line status summary.\n"); + fprintf(stderr, " --log-interval : Set summary update frequency (default: 100ms).\n"); +} + +double timeval_diff_ms(struct timeval *start, struct timeval *end) { + return (end->tv_sec - start->tv_sec) * 1000.0 + (end->tv_usec - start->tv_usec) / 1000.0; +} + +void *logging_thread_func(void *arg) { + struct logging_thread_args *args = (struct logging_thread_args *)arg; + struct stream_state *state = args->state; + const int bar_width = 20; + + while (is_running) { + usleep(args->log_interval_ms * 1000); + pthread_mutex_lock(&state->lock); + + const char *health = (state->underrun_count > 0 || state->overrun_count > 0) ? "\033[1;31mUNSTABLE\033[0m" : "\033[1;32mSTABLE\033[0m"; + const char *sync_status_str; + if (state->feedback_synced) { + sync_status_str = state->feedback_warmed_up ? "\033[1;32mACQUIRED\033[0m" : "\033[1;33mWARM-UP\033[0m"; + } else { + sync_status_str = "\033[1;31mLOST/OFF\033[0m"; + } + + double avg_interval = (state->feedback_interval_count > 0) ? state->avg_feedback_interval_sum / state->feedback_interval_count : 0.0; + + if (args->minimal_log) { + printf("Health: %s, Sync: %s, Avg Interval: %.2fms, Underruns: %lu, Overruns: %lu \r", + (state->underrun_count > 0 || state->overrun_count > 0) ? "UNSTABLE" : "STABLE", + state->feedback_warmed_up ? "ACQUIRED" : "WARMING", + avg_interval, state->underrun_count, state->overrun_count); + } else { + size_t fill = (state->feedback_pattern_in_idx - state->feedback_pattern_out_idx + FEEDBACK_ACCUMULATOR_SIZE) % FEEDBACK_ACCUMULATOR_SIZE; + int filled_chars = (int)((double)fill / FEEDBACK_ACCUMULATOR_SIZE * bar_width); + + printf("\033[5A\033[K\n\033[K\n\033[K\n\033[K\n\033[K\n\033[5A"); + printf("--- TASCAM US-144MKII Stream Health ---\n"); + printf(" Health: %-18s Sync: %-18s Feedback: %-3d\n", health, sync_status_str, state->last_feedback_value); + printf(" Buffer: ["); + for(int i=0; i Now: %4.2f Min: %4.2f Avg: %4.2f Max: %4.2f\n", + state->last_feedback_interval_ms, + state->min_feedback_interval_ms == DBL_MAX ? 0.0 : state->min_feedback_interval_ms, + avg_interval, state->max_feedback_interval_ms); + printf(" Errors -> Underruns: %-5lu Overruns: %lu\n", state->underrun_count, state->overrun_count); + } + fflush(stdout); + pthread_mutex_unlock(&state->lock); + } + return NULL; +} + +static void LIBUSB_CALL feedback_callback(struct libusb_transfer *transfer) { + if (!is_running) return; + struct stream_state *state = transfer->user_data; + struct timeval now; + gettimeofday(&now, NULL); + + if (transfer->status != LIBUSB_TRANSFER_COMPLETED) { + if (transfer->status != LIBUSB_TRANSFER_CANCELLED) { + pthread_mutex_lock(&state->lock); + if (state->feedback_synced) printf("\nSync Lost (URB Error: %s)!\n", libusb_error_name(transfer->status)); + state->feedback_synced = false; + state->feedback_warmed_up = false; + pthread_mutex_unlock(&state->lock); + } + goto resubmit; + } + + pthread_mutex_lock(&state->lock); + if (state->last_feedback_completion_time.tv_sec > 0) { + state->last_feedback_interval_ms = timeval_diff_ms(&state->last_feedback_completion_time, &now); + if (state->feedback_warmed_up) { + if (state->last_feedback_interval_ms < state->min_feedback_interval_ms) state->min_feedback_interval_ms = state->last_feedback_interval_ms; + if (state->last_feedback_interval_ms > state->max_feedback_interval_ms) state->max_feedback_interval_ms = state->last_feedback_interval_ms; + state->avg_feedback_interval_sum += state->last_feedback_interval_ms; + state->feedback_interval_count++; + } + } + state->last_feedback_completion_time = now; + + bool was_synced = state->feedback_synced; + bool sync_lost_this_urb = false; + + for (int p = 0; p < transfer->num_iso_packets; p++) { + struct libusb_iso_packet_descriptor *pack = &transfer->iso_packet_desc[p]; + if (pack->status != 0 || pack->actual_length < 1) { + sync_lost_this_urb = true; + continue; + } + size_t packet_offset = p * FEEDBACK_PACKET_SIZE; + uint8_t feedback_value = transfer->buffer[packet_offset]; + state->last_feedback_value = feedback_value; + + if (feedback_value >= state->rate_cfg->feedback_base_value && feedback_value <= state->rate_cfg->feedback_max_value) { + int pattern_index = feedback_value - state->rate_cfg->feedback_base_value; + const unsigned int *pattern = state->rate_cfg->feedback_patterns[pattern_index]; + size_t fill_level = (state->feedback_pattern_in_idx - state->feedback_pattern_out_idx + FEEDBACK_ACCUMULATOR_SIZE) % FEEDBACK_ACCUMULATOR_SIZE; + if (fill_level > (FEEDBACK_ACCUMULATOR_SIZE - 16)) state->overrun_count++; + for (int i = 0; i < 8; i++) { + unsigned int in_idx = (state->feedback_pattern_in_idx + i) % FEEDBACK_ACCUMULATOR_SIZE; + state->feedback_accumulator_pattern[in_idx] = pattern[i]; + } + state->feedback_pattern_in_idx = (state->feedback_pattern_in_idx + 8) % FEEDBACK_ACCUMULATOR_SIZE; + } else { + sync_lost_this_urb = true; + } + } + + if (sync_lost_this_urb) { + if (was_synced) printf("\nSync Lost (Bad Packet)!\n"); + state->feedback_synced = false; + state->feedback_warmed_up = false; + } else { + if (!was_synced) printf("\nSync Acquired!\n"); + state->feedback_synced = true; + size_t fill_level = (state->feedback_pattern_in_idx - state->feedback_pattern_out_idx + FEEDBACK_ACCUMULATOR_SIZE) % FEEDBACK_ACCUMULATOR_SIZE; + if (!state->feedback_warmed_up && fill_level >= WARMUP_THRESHOLD) { + state->feedback_warmed_up = true; + state->min_feedback_interval_ms = DBL_MAX; + state->max_feedback_interval_ms = 0.0; + state->avg_feedback_interval_sum = 0.0; + state->feedback_interval_count = 0; + printf("\nBuffer warmed up. Measuring steady-state performance.\n"); + } + } + pthread_mutex_unlock(&state->lock); + +resubmit: + if (is_running) libusb_submit_transfer(transfer); +} + +static void LIBUSB_CALL iso_playback_callback(struct libusb_transfer *transfer) { + if (!is_running) return; + struct stream_state *state = transfer->user_data; + + if (transfer->status != LIBUSB_TRANSFER_COMPLETED) { + if (transfer->status != LIBUSB_TRANSFER_CANCELLED) { + fprintf(stderr, "\nPlayback callback error: %s\n", libusb_error_name(transfer->status)); + is_running = false; + } + return; + } + + pthread_mutex_lock(&state->lock); + int nominal_frames = state->rate_cfg->rate / 8000; + + if (!state->feedback_warmed_up) { + libusb_set_iso_packet_lengths(transfer, nominal_frames * DEVICE_FRAME_SIZE); + memset(transfer->buffer, 0, transfer->length); + pthread_mutex_unlock(&state->lock); + goto resubmit_playback; + } + + unsigned char *buf_ptr = transfer->buffer; + size_t total_bytes_in_urb = 0; + + for (int i = 0; i < transfer->num_iso_packets; i++) { + unsigned int frames_for_packet; + if (state->feedback_pattern_out_idx == state->feedback_pattern_in_idx) { + state->underrun_count++; + frames_for_packet = nominal_frames; + } else { + frames_for_packet = state->feedback_accumulator_pattern[state->feedback_pattern_out_idx]; + state->feedback_pattern_out_idx = (state->feedback_pattern_out_idx + 1) % FEEDBACK_ACCUMULATOR_SIZE; + } + size_t bytes_for_packet = frames_for_packet * DEVICE_FRAME_SIZE; + size_t bytes_to_read_from_pipe = frames_for_packet * PIPE_FRAME_SIZE; + + ssize_t bytes_read = read(state->fifo_fd, buf_ptr, bytes_to_read_from_pipe); + + if (bytes_read > 0) { + int frames_read = bytes_read / PIPE_FRAME_SIZE; + for (int f = frames_read - 1; f >= 0; f--) { + unsigned char* src = buf_ptr + f * PIPE_FRAME_SIZE; + unsigned char* dst = buf_ptr + f * DEVICE_FRAME_SIZE; + memmove(dst, src, PIPE_FRAME_SIZE); + memset(dst + PIPE_FRAME_SIZE, 0, DEVICE_FRAME_SIZE - PIPE_FRAME_SIZE); + } + if ((size_t)bytes_read < bytes_to_read_from_pipe) { + memset(buf_ptr + (frames_read * DEVICE_FRAME_SIZE), 0, bytes_for_packet - (frames_read * DEVICE_FRAME_SIZE)); + } + } else { + memset(buf_ptr, 0, bytes_for_packet); + } + + buf_ptr += bytes_for_packet; + transfer->iso_packet_desc[i].length = bytes_for_packet; + total_bytes_in_urb += bytes_for_packet; + } + pthread_mutex_unlock(&state->lock); + + transfer->length = total_bytes_in_urb; + +resubmit_playback: + if (is_running && libusb_submit_transfer(transfer) < 0) { + fprintf(stderr, "\nError resubmitting playback transfer\n"); + is_running = false; + } +} + +int perform_initialization_sequence(libusb_device_handle *handle, const struct sample_rate_config *rate_config) { + unsigned char buf[64]; int r; + printf("\n--- STARTING DEVICE CONFIGURATION (per Spec v5.0) ---\n"); + #define CHECK(desc, call) r = (call); if (r < 0) { fprintf(stderr, " [FAIL] %s: %s\n", desc, libusb_error_name(r)); return -1; } else { printf(" [OK] %s (returned %d)\n", desc, r); } + printf(" [INFO] Step 1: Set Interfaces\n"); + r = libusb_set_configuration(handle, 1); if (r < 0 && r != LIBUSB_ERROR_BUSY) { fprintf(stderr, " [FAIL] Set Configuration 1: %s\n", libusb_error_name(r)); return -1; } + for (int i=0; i<=1; i++) { r = libusb_claim_interface(handle, i); if (r < 0) { fprintf(stderr, " [FAIL] Claim Interface %d: %s\n", i, libusb_error_name(r)); return -1; } r = libusb_set_interface_alt_setting(handle, i, 1); if (r < 0) { fprintf(stderr, " [FAIL] Set Alt Setting on Intf %d: %s\n", i, libusb_error_name(r)); return -1; } } + printf(" [OK] Step 1: Interfaces set and claimed.\n"); + printf("\n-- Step 2: Initial Handshake --\n"); CHECK("Status Check", libusb_control_transfer(handle, RT_D2H_VENDOR_DEV, VENDOR_REQ_MODE_CONTROL, 0x0000, 0x0000, buf, 1, USB_TIMEOUT)); + printf("\n-- Step 3: Set Initial Mode --\n"); CHECK("Set Initial Mode", libusb_control_transfer(handle, RT_H2D_VENDOR_DEV, VENDOR_REQ_MODE_CONTROL, 0x0010, 0x0000, NULL, 0, USB_TIMEOUT)); + printf("\n-- Step 4: Set Sample Rate to %d Hz --\n", rate_config->rate); + CHECK("Set Rate on Capture EP (0x86)", libusb_control_transfer(handle, RT_H2D_CLASS_EP, UAC_SET_CUR, UAC_SAMPLING_FREQ_CONTROL, EP_CAPTURE_DATA, (unsigned char*)rate_config->rate_data, 3, USB_TIMEOUT)); + CHECK("Set Rate on Playback EP (0x02)", libusb_control_transfer(handle, RT_H2D_CLASS_EP, UAC_SET_CUR, UAC_SAMPLING_FREQ_CONTROL, EP_AUDIO_OUT, (unsigned char*)rate_config->rate_data, 3, USB_TIMEOUT)); + printf("\n-- Step 5: Configure Internal Registers --\n"); CHECK("Reg Write 1 (0x0d04)", libusb_control_transfer(handle, RT_H2D_VENDOR_DEV, VENDOR_REQ_REGISTER_WRITE, 0x0d04, 0x0101, NULL, 0, USB_TIMEOUT)); CHECK("Reg Write 2 (0x0e00)", libusb_control_transfer(handle, RT_H2D_VENDOR_DEV, VENDOR_REQ_REGISTER_WRITE, 0x0e00, 0x0101, NULL, 0, USB_TIMEOUT)); CHECK("Reg Write 3 (0x0f00)", libusb_control_transfer(handle, RT_H2D_VENDOR_DEV, VENDOR_REQ_REGISTER_WRITE, 0x0f00, 0x0101, NULL, 0, USB_TIMEOUT)); + CHECK("Reg Write 4 (Rate-Dep)", libusb_control_transfer(handle, RT_H2D_VENDOR_DEV, VENDOR_REQ_REGISTER_WRITE, rate_config->rate_vendor_wValue, 0x0101, NULL, 0, USB_TIMEOUT)); + CHECK("Reg Write 5 (0x110b)", libusb_control_transfer(handle, RT_H2D_VENDOR_DEV, VENDOR_REQ_REGISTER_WRITE, 0x110b, 0x0101, NULL, 0, USB_TIMEOUT)); + printf("\n-- Step 6: Enable Streaming --\n"); CHECK("Enable Streaming", libusb_control_transfer(handle, RT_H2D_VENDOR_DEV, VENDOR_REQ_MODE_CONTROL, 0x0030, 0x0000, NULL, 0, USB_TIMEOUT)); + printf("\n--- CONFIGURATION COMPLETE ---\n\n"); return 0; +} diff --git a/tascam_streamer b/tascam_streamer new file mode 100755 index 0000000000000000000000000000000000000000..458167eb0bb3be540779b2b3dfb36d0951df47fc GIT binary patch literal 35424 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 +/* + * ALSA Driver for TASCAM US-144MKII Audio Interface + */ #include #include @@ -14,7 +17,7 @@ #include MODULE_AUTHOR("serifpersia"); -MODULE_DESCRIPTION("ALSA Driver for TASCAM US-144MKII with Isochronous Feedback"); +MODULE_DESCRIPTION("ALSA Driver for TASCAM US-144MKII"); MODULE_LICENSE("GPL"); #define DRIVER_NAME "us144mkii" @@ -23,21 +26,6 @@ MODULE_LICENSE("GPL"); /* --- Module Parameters --- */ /*============================================================================*/ -/* - * Latency Profile Cheatsheet (Updated based on Windows ASIO driver behavior) - * - * The driver selects a hardware profile dynamically based on the period size - * requested by the application. The device has 3 true hardware modes. - * The thresholds are ~2ms and ~3ms. - * - * Profile | Feedback URB | Approx. Latency | Typical Period Size (Frames) - * Name | Packet Count | (Hardware) | 48kHz | 96kHz - * --------|--------------|-----------------|------------|------------ - * Low | 1 packet | <= 2ms | <= 96 | <= 192 - * Normal | 2 packets | > 2ms to <= 3ms | <= 144 | <= 288 - * High | 5 packets | > 3ms | > 144 | > 288 - */ - static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; static bool enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0}; @@ -57,35 +45,45 @@ MODULE_PARM_DESC(enable, "Enable this US-144MKII soundcard."); #define TASCAM_VID 0x0644 #define TASCAM_PID 0x8020 -#define EP_AUDIO_OUT 0x02 -#define EP_PLAYBACK_FEEDBACK 0x81 -#define EP_CAPTURE_DATA 0x86 +/* USB Endpoints from descriptor report */ +#define EP_AUDIO_OUT 0x02 // Isochronous OUT for playback audio +#define EP_PLAYBACK_FEEDBACK 0x81 // Isochronous IN for clock feedback +#define EP_CAPTURE_DATA 0x86 // Bulk IN for capture audio/MIDI +#define EP_MIDI_OUT 0x04 // Bulk OUT for MIDI +#define EP_MIDI_IN 0x83 // Bulk IN for MIDI -#define RT_H2D_CLASS_EP 0x22 -#define RT_H2D_VENDOR_DEV 0x40 -#define RT_D2H_VENDOR_DEV 0xc0 +/* USB Control Message Request Types */ +#define RT_H2D_CLASS_EP 0x22 // Host-to-Device, Class, Endpoint +#define RT_H2D_VENDOR_DEV 0x40 // Host-to-Device, Vendor, Device +#define RT_D2H_VENDOR_DEV 0xc0 // Device-to-Host, Vendor, Device + +/* USB Control Message Requests */ #define UAC_SET_CUR 0x01 #define UAC_SAMPLING_FREQ_CONTROL 0x0100 -#define VENDOR_REQ_REGISTER_WRITE 65 -#define VENDOR_REQ_MODE_CONTROL 73 +#define VENDOR_REQ_REGISTER_WRITE 65 // bRequest 0x41 +#define VENDOR_REQ_MODE_CONTROL 73 // bRequest 0x49 +/* URB Configuration */ #define NUM_PLAYBACK_URBS 8 #define NUM_FEEDBACK_URBS 4 -#define MAX_FEEDBACK_PACKETS 5 +#define MAX_FEEDBACK_PACKETS 5 // For the highest latency setting #define PLAYBACK_URB_ISO_PACKETS 40 #define FEEDBACK_PACKET_SIZE 3 #define USB_CTRL_TIMEOUT_MS 1000 -#define BYTES_PER_SAMPLE 3 -#define ALSA_CHANNELS 2 -#define DEVICE_CHANNELS 4 +/* Audio Format Configuration */ +#define BYTES_PER_SAMPLE 3 // 24-bit +#define ALSA_CHANNELS 2 // Stereo from user-space +#define DEVICE_CHANNELS 4 // Device expects 4 channels of data #define ALSA_BYTES_PER_FRAME (ALSA_CHANNELS * BYTES_PER_SAMPLE) #define DEVICE_BYTES_PER_FRAME (DEVICE_CHANNELS * BYTES_PER_SAMPLE) +/* Feedback Synchronization Engine Configuration */ #define FEEDBACK_ACCUMULATOR_SIZE 128 static struct usb_driver tascam_alsa_driver; +/* Main driver data structure */ struct tascam_card { struct usb_device *dev; struct usb_interface *iface0; @@ -106,43 +104,78 @@ struct tascam_card { /* --- Feedback Synchronization State --- */ unsigned int feedback_accumulator_pattern[FEEDBACK_ACCUMULATOR_SIZE]; - unsigned int feedback_pattern_out_idx; - unsigned int feedback_pattern_in_idx; + unsigned int feedback_pattern_out_idx; // Read index for playback + unsigned int feedback_pattern_in_idx; // Write index from feedback bool feedback_synced; + unsigned int feedback_urb_skip_count; // Initial URBs to discard /* --- Playback Position Tracking --- */ - snd_pcm_uframes_t driver_playback_pos; - u64 playback_frames_consumed; - u64 last_period_pos; + snd_pcm_uframes_t driver_playback_pos; // Pointer within ALSA buffer + u64 playback_frames_consumed; // Total frames consumed by hw + u64 last_period_pos; // Last reported period /* --- Rate-Specific Data --- */ const unsigned int (*feedback_patterns)[8]; unsigned int feedback_base_value; unsigned int feedback_max_value; - unsigned int feedback_urb_skip_count; }; -/* Pre-calculated patterns for frames-per-microframe based on feedback value. */ -static const unsigned int latency_profile_packets[] = { 5, 1, 2, 5, 5 }; +/* + * Latency Profile Cheatsheet (from reverse-engineering report) + * + * The driver selects a hardware profile dynamically based on the period size + * requested by the application. The device has 3 true hardware modes, + * determined by the number of packets in the feedback URB. + * + * Profile | Feedback URB | Approx. Latency | Typical Period Size (Frames) + * Name | Packet Count | (Hardware) | 48kHz | 96kHz + * --------|--------------|-----------------|------------|------------ + * Low | 1 packet | <= 2ms | <= 96 | <= 192 + * Normal | 2 packets | > 2ms to <= 3ms | <= 144 | <= 288 + * High | 5 packets | > 3ms | > 144 | > 288 + */ +static const unsigned int latency_profile_packets[] = { + 0, // Profile 0 unused + 1, // Low latency + 2, // Normal latency + 5, // High latency +}; + +/* + * Pre-calculated patterns for frames-per-microframe based on feedback value. + * These are the core of the "Packet Fixing" engine. Each array represents + * the number of audio frames to send in each of the 8 microframes of a USB frame. + * The sum of each pattern equals the feedback value. + * E.g., for 48kHz, nominal is 48000/1000 = 48 frames/ms. + * The pattern for feedback value 48 is {6,6,6,6,6,6,6,6}, since 6*8=48. + */ static const unsigned int patterns_48khz[5][8] = { - {5, 6, 6, 6, 5, 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}, - {7, 6, 6, 6, 7, 6, 6, 6} + {5, 6, 6, 6, 5, 6, 6, 6}, // 46 + {5, 6, 6, 6, 6, 6, 6, 6}, // 47 + {6, 6, 6, 6, 6, 6, 6, 6}, // 48 (Nominal) + {7, 6, 6, 6, 6, 6, 6, 6}, // 49 + {7, 6, 6, 6, 7, 6, 6, 6} // 50 }; static const unsigned int patterns_96khz[5][8] = { - {11, 12, 12, 12, 11, 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}, - {13, 12, 12, 12, 13, 12, 12, 12} + {11, 12, 12, 12, 11, 12, 12, 12}, // 94 + {11, 12, 12, 12, 12, 12, 12, 12}, // 95 + {12, 12, 12, 12, 12, 12, 12, 12}, // 96 (Nominal) + {13, 12, 12, 12, 12, 12, 12, 12}, // 97 + {13, 12, 12, 12, 13, 12, 12, 12} // 98 }; static const unsigned int patterns_88khz[5][8] = { - {10, 11, 11, 11, 10, 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}, - {12, 11, 11, 11, 12, 11, 11, 11} + {10, 11, 11, 11, 10, 11, 11, 11}, // 86 + {10, 11, 11, 11, 11, 11, 11, 11}, // 87 + {11, 11, 11, 11, 11, 11, 11, 11}, // 88 (Nominal) + {12, 11, 11, 11, 11, 11, 11, 11}, // 89 + {12, 11, 11, 11, 12, 11, 11, 11} // 90 }; 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, 6, 5, 6, 5, 6, 5, 6}, {6, 5, 6, 6, 5, 6, 5, 6}, - {6, 6, 6, 5, 6, 6, 6, 5} + {5, 5, 5, 6, 5, 5, 5, 6}, // 42 + {5, 5, 6, 5, 5, 6, 5, 6}, // 43 + {5, 6, 5, 6, 5, 6, 5, 6}, // 44 (Nominal is 44.1) + {6, 5, 6, 6, 5, 6, 5, 6}, // 45 + {6, 6, 6, 5, 6, 6, 6, 5} // 46 }; @@ -208,6 +241,7 @@ static struct snd_pcm_ops tascam_playback_ops = { .pointer = tascam_pcm_pointer, }; +// Stub for capture, as this driver only implements playback. static int tascam_capture_open_stub(struct snd_pcm_substream *substream) { return -ENODEV; } static int tascam_capture_close_stub(struct snd_pcm_substream *substream) { return 0; } static struct snd_pcm_ops tascam_capture_ops = { @@ -254,6 +288,8 @@ static int tascam_alloc_urbs(struct tascam_card *tascam) int i; size_t max_frames_per_packet, max_packet_size; + // Calculate max possible packet size to allocate enough buffer space. + // Add a margin of 2 for safety. max_frames_per_packet = (96000 / 8000) + 2; max_packet_size = max_frames_per_packet * DEVICE_BYTES_PER_FRAME; tascam->playback_urb_alloc_size = max_packet_size * PLAYBACK_URB_ISO_PACKETS; @@ -265,17 +301,19 @@ static int tascam_alloc_urbs(struct tascam_card *tascam) for (i = 0; i < NUM_PLAYBACK_URBS; i++) { struct urb *urb = usb_alloc_urb(PLAYBACK_URB_ISO_PACKETS, GFP_KERNEL); - if (!urb) goto error; + if (!urb) + goto error; tascam->playback_urbs[i] = urb; urb->transfer_buffer = usb_alloc_coherent(tascam->dev, tascam->playback_urb_alloc_size, GFP_KERNEL, &urb->transfer_dma); - if (!urb->transfer_buffer) goto error; + if (!urb->transfer_buffer) + goto error; urb->dev = tascam->dev; urb->pipe = usb_sndisocpipe(tascam->dev, EP_AUDIO_OUT); urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; - urb->interval = 1; + urb->interval = 1; // bInterval from descriptor urb->context = tascam; urb->complete = playback_urb_complete; urb->number_of_packets = PLAYBACK_URB_ISO_PACKETS; @@ -285,17 +323,19 @@ static int tascam_alloc_urbs(struct tascam_card *tascam) for (i = 0; i < NUM_FEEDBACK_URBS; i++) { struct urb *f_urb = usb_alloc_urb(MAX_FEEDBACK_PACKETS, GFP_KERNEL); - if (!f_urb) goto error; + if (!f_urb) + goto error; tascam->feedback_urbs[i] = f_urb; f_urb->transfer_buffer = usb_alloc_coherent(tascam->dev, tascam->feedback_urb_alloc_size, GFP_KERNEL, &f_urb->transfer_dma); - if (!f_urb->transfer_buffer) goto error; + if (!f_urb->transfer_buffer) + goto error; f_urb->dev = tascam->dev; f_urb->pipe = usb_rcvisocpipe(tascam->dev, EP_PLAYBACK_FEEDBACK); f_urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; - f_urb->interval = 4; + f_urb->interval = 4; // bInterval from descriptor f_urb->context = tascam; f_urb->complete = feedback_urb_complete; } @@ -313,6 +353,9 @@ error: /* --- PCM Implementation --- */ /*============================================================================*/ +// This rule constrains the period size to the values reported by the +// Windows ASIO driver, ensuring we don't request a latency the +// hardware can't handle. static int tascam_pcm_period_size_rule(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { @@ -372,6 +415,7 @@ static int tascam_pcm_open(struct snd_pcm_substream *substream) static int tascam_pcm_close(struct snd_pcm_substream *substream) { struct tascam_card *tascam = snd_pcm_substream_chip(substream); + tascam_free_urbs(tascam); return 0; } @@ -388,12 +432,13 @@ static int tascam_pcm_hw_params(struct snd_pcm_substream *substream, int active_latency_profile; unsigned int feedback_urb_packets; + // Select a hardware latency profile based on the user's requested period size. if (period_frames <= (normal_thresh_ms * rate / 1000)) { - active_latency_profile = 1; // 1-packet mode + active_latency_profile = 1; // Low latency (1 feedback packet) } else if (period_frames <= (high_thresh_ms * rate / 1000)) { - active_latency_profile = 2; // 2-packet mode + active_latency_profile = 2; // Normal latency (2 feedback packets) } else { - active_latency_profile = 3; // 5-packet mode + active_latency_profile = 3; // High latency (5 feedback packets) } dev_info(tascam->card->dev, @@ -402,9 +447,11 @@ static int tascam_pcm_hw_params(struct snd_pcm_substream *substream, feedback_urb_packets = latency_profile_packets[active_latency_profile]; + // Configure the feedback URBs for the selected latency profile. for (i = 0; i < NUM_FEEDBACK_URBS; i++) { struct urb *f_urb = tascam->feedback_urbs[i]; int j; + f_urb->number_of_packets = feedback_urb_packets; f_urb->transfer_buffer_length = feedback_urb_packets * FEEDBACK_PACKET_SIZE; for (j = 0; j < feedback_urb_packets; j++) { @@ -417,6 +464,7 @@ static int tascam_pcm_hw_params(struct snd_pcm_substream *substream, if (err < 0) return err; + // Load the correct feedback patterns and range for the selected sample rate. switch (rate) { case 44100: tascam->feedback_patterns = patterns_44khz; @@ -443,6 +491,7 @@ static int tascam_pcm_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } + // If the sample rate has changed, reconfigure the device. if (tascam->current_rate != rate) { err = us144mkii_configure_device_for_rate(tascam, rate); if (err < 0) { @@ -476,15 +525,17 @@ static int tascam_pcm_prepare(struct snd_pcm_substream *substream) tascam->feedback_pattern_in_idx = 0; tascam->feedback_pattern_out_idx = 0; tascam->feedback_synced = false; + // Discard the first few feedback URBs to allow the hardware clock to stabilize. tascam->feedback_urb_skip_count = NUM_FEEDBACK_URBS * 2; - /* DEBUG: Log the initial state on prepare. */ dev_info(tascam->card->dev, "Prepare: Sync state reset, starting in unsynced mode.\n"); + // Initialize the feedback accumulator with the nominal number of frames. nominal_frames_per_packet = runtime->rate / 8000; for (i = 0; i < FEEDBACK_ACCUMULATOR_SIZE; i++) tascam->feedback_accumulator_pattern[i] = nominal_frames_per_packet; + // Initialize playback URBs with nominal packet sizes. nominal_bytes_per_packet = nominal_frames_per_packet * DEVICE_BYTES_PER_FRAME; total_bytes_in_urb = nominal_bytes_per_packet * PLAYBACK_URB_ISO_PACKETS; @@ -496,6 +547,7 @@ static int tascam_pcm_prepare(struct snd_pcm_substream *substream) for (u = 0; u < NUM_PLAYBACK_URBS; u++) { struct urb *urb = tascam->playback_urbs[u]; + memset(urb->transfer_buffer, 0, tascam->playback_urb_alloc_size); urb->transfer_buffer_length = total_bytes_in_urb; for (i = 0; i < PLAYBACK_URB_ISO_PACKETS; i++) { @@ -530,6 +582,7 @@ static int tascam_pcm_trigger(struct snd_pcm_substream *substream, int cmd) } if (start) { + // Submit all feedback and playback URBs to start the stream. for (i = 0; i < NUM_FEEDBACK_URBS; i++) { err = usb_submit_urb(tascam->feedback_urbs[i], GFP_ATOMIC); if (err < 0) { @@ -542,8 +595,10 @@ static int tascam_pcm_trigger(struct snd_pcm_substream *substream, int cmd) err = usb_submit_urb(tascam->playback_urbs[i], GFP_ATOMIC); if (err < 0) { int j; + dev_err(tascam->card->dev, "Failed to submit playback URB %d: %d\n", i, err); atomic_set(&tascam->playback_active, 0); + // Unlink any URBs that were successfully submitted. for (j = 0; j < NUM_FEEDBACK_URBS; j++) usb_unlink_urb(tascam->feedback_urbs[j]); for (j = 0; j < i; j++) @@ -552,6 +607,7 @@ static int tascam_pcm_trigger(struct snd_pcm_substream *substream, int cmd) } } } else { + // Unlink all URBs to stop the stream. for (i = 0; i < NUM_PLAYBACK_URBS; i++) usb_unlink_urb(tascam->playback_urbs[i]); for (i = 0; i < NUM_FEEDBACK_URBS; i++) @@ -570,7 +626,10 @@ static snd_pcm_uframes_t tascam_pcm_pointer(struct snd_pcm_substream *substream) return 0; pos = tascam->playback_frames_consumed; - return runtime ? div_u64(pos, 1) % runtime->buffer_size : 0; + + // Return the hardware position within the circular buffer. + // The 64-bit modulo will be handled correctly by the compiler. + return runtime ? pos % runtime->buffer_size : 0; } @@ -578,6 +637,7 @@ static snd_pcm_uframes_t tascam_pcm_pointer(struct snd_pcm_substream *substream) /* --- URB Completion Handlers --- */ /*============================================================================*/ +// This is the playback half of the "Packet Fixing" engine. static void playback_urb_complete(struct urb *urb) { struct tascam_card *tascam = urb->context; @@ -602,16 +662,13 @@ static void playback_urb_complete(struct urb *urb) spin_lock_irqsave(&tascam->lock, flags); - /* DEBUG: Log which sizing logic is being used. */ - if (tascam->feedback_synced) - dev_info_ratelimited(tascam->card->dev, "Playback: Using DYNAMIC packet sizes (synced).\n"); - else - dev_info_ratelimited(tascam->card->dev, "Playback: Using NOMINAL packet sizes (not synced).\n"); - + // Prepare the next playback URB. for (i = 0; i < PLAYBACK_URB_ISO_PACKETS; i++) { unsigned int frames_for_packet; size_t bytes_for_packet; + // If synced, use the dynamic frame count from the accumulator. + // If not, use the nominal frame count. if (tascam->feedback_synced) { frames_for_packet = tascam->feedback_accumulator_pattern[tascam->feedback_pattern_out_idx]; tascam->feedback_pattern_out_idx = (tascam->feedback_pattern_out_idx + 1) % FEEDBACK_ACCUMULATOR_SIZE; @@ -622,15 +679,18 @@ static void playback_urb_complete(struct urb *urb) bytes_for_packet = frames_for_packet * DEVICE_BYTES_PER_FRAME; if ((urb_total_bytes + bytes_for_packet) > tascam->playback_urb_alloc_size) { + dev_warn_ratelimited(tascam->card->dev, "Playback URB overflow, truncating packet.\n"); urb->iso_frame_desc[i].length = 0; urb->iso_frame_desc[i].offset = urb_total_bytes; continue; } + // Copy audio data from ALSA buffer to the URB. for (f = 0; f < frames_for_packet; f++) { size_t alsa_pos_bytes = frames_to_bytes(runtime, tascam->driver_playback_pos); char *alsa_frame_ptr = runtime->dma_area + alsa_pos_bytes; + // Copy 2 channels from ALSA, then zero-pad to 4 channels for the device. memcpy(urb_buf_ptr, alsa_frame_ptr, ALSA_BYTES_PER_FRAME); memset(urb_buf_ptr + ALSA_BYTES_PER_FRAME, 0, DEVICE_BYTES_PER_FRAME - ALSA_BYTES_PER_FRAME); @@ -649,9 +709,6 @@ static void playback_urb_complete(struct urb *urb) urb->transfer_buffer_length = urb_total_bytes; - /* DEBUG: Log the size of the URB we just prepared. */ - dev_info_ratelimited(tascam->card->dev, "Prepared playback URB, total bytes: %zu\n", urb_total_bytes); - if (atomic_read(&tascam->playback_active)) { urb->dev = tascam->dev; ret = usb_submit_urb(urb, GFP_ATOMIC); @@ -660,6 +717,7 @@ static void playback_urb_complete(struct urb *urb) } } +// This is the feedback half of the "Packet Fixing" engine. static void feedback_urb_complete(struct urb *urb) { struct tascam_card *tascam = urb->context; @@ -697,11 +755,13 @@ static void feedback_urb_complete(struct urb *urb) was_synced = tascam->feedback_synced; + // Skip initial URBs to let the clock stabilize. if (tascam->feedback_urb_skip_count > 0) { tascam->feedback_urb_skip_count--; goto unlock_and_resubmit; } + // Process each feedback packet in the URB. for (p = 0; p < urb->number_of_packets; p++) { u8 feedback_value; const unsigned int *pattern; @@ -712,9 +772,10 @@ static void feedback_urb_complete(struct urb *urb) continue; } + // The feedback value is the first byte of the 3-byte packet. feedback_value = *((u8 *)urb->transfer_buffer + urb->iso_frame_desc[p].offset); - dev_info_ratelimited(tascam->card->dev, "Feedback received, value: %u\n", feedback_value); + // Validate the feedback value and look up the corresponding pattern. if (feedback_value >= tascam->feedback_base_value && feedback_value <= tascam->feedback_max_value) { pattern_index = feedback_value - tascam->feedback_base_value; @@ -724,20 +785,24 @@ static void feedback_urb_complete(struct urb *urb) pattern = NULL; } + // If a valid pattern was found, write it to the accumulator. if (pattern) { 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]; } tascam->feedback_pattern_in_idx = (tascam->feedback_pattern_in_idx + 8) % FEEDBACK_ACCUMULATOR_SIZE; } else { + // If pattern is invalid, assume nominal rate for this interval. u64 nominal_frames_per_ms = runtime->rate / 1000; + total_frames_in_urb += nominal_frames_per_ms; } } - /* Update and log the sync state transition. */ + // Update and log the sync state transition. if (sync_lost_this_urb) { if (was_synced) dev_info(tascam->card->dev, "Sync Lost (bad packet)!\n"); @@ -748,9 +813,11 @@ static void feedback_urb_complete(struct urb *urb) tascam->feedback_synced = true; } + // Update the total number of frames consumed by the hardware. if (total_frames_in_urb > 0) tascam->playback_frames_consumed += total_frames_in_urb; + // Check if a period has elapsed and notify ALSA. current_period = div_u64(tascam->playback_frames_consumed, runtime->period_size); if (current_period > tascam->last_period_pos) { tascam->last_period_pos = current_period; @@ -778,6 +845,9 @@ resubmit: /* --- Device Configuration and Probing --- */ /*============================================================================*/ +// This function sends the precise sequence of control messages required to +// initialize the device and set a new sample rate. This sequence was +// determined by reverse-engineering the official drivers. static int us144mkii_configure_device_for_rate(struct tascam_card *tascam, int rate) { struct usb_device *dev = tascam->dev; @@ -785,6 +855,7 @@ static int us144mkii_configure_device_for_rate(struct tascam_card *tascam, int r u16 rate_vendor_wValue; int err = 0; + // Payloads for UAC_SET_CUR request, specific to each sample rate. static const u8 payload_44100[] = {0x44, 0xac, 0x00}; static const u8 payload_48000[] = {0x80, 0xbb, 0x00}; static const u8 payload_88200[] = {0x88, 0x58, 0x01}; @@ -792,7 +863,8 @@ static int us144mkii_configure_device_for_rate(struct tascam_card *tascam, int r const u8 *current_payload_src; rate_payload_buf = kmalloc(3, GFP_KERNEL); - if (!rate_payload_buf) return -ENOMEM; + if (!rate_payload_buf) + return -ENOMEM; switch (rate) { case 44100: current_payload_src = payload_44100; rate_vendor_wValue = 0x1000; break; @@ -807,31 +879,53 @@ static int us144mkii_configure_device_for_rate(struct tascam_card *tascam, int r memcpy(rate_payload_buf, current_payload_src, 3); + // --- Begin Control Message Sequence --- + dev_info(&dev->dev, "Configuring device for %d Hz\n", rate); + + // 1. Set Initial Mode err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), VENDOR_REQ_MODE_CONTROL, RT_H2D_VENDOR_DEV, 0x0010, 0x0000, NULL, 0, USB_CTRL_TIMEOUT_MS); - if (err < 0) { goto cleanup_buf; } + if (err < 0) + goto cleanup_buf; + // 2. Set Sample Rate on Capture and Playback Endpoints err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR, RT_H2D_CLASS_EP, UAC_SAMPLING_FREQ_CONTROL, EP_CAPTURE_DATA, rate_payload_buf, 3, USB_CTRL_TIMEOUT_MS); - if (err < 0) { goto cleanup_buf; } + if (err < 0) + goto cleanup_buf; 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_buf, 3, USB_CTRL_TIMEOUT_MS); - if (err < 0) { goto cleanup_buf; } + if (err < 0) + goto cleanup_buf; + // 3. Vendor-specific register writes err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), VENDOR_REQ_REGISTER_WRITE, RT_H2D_VENDOR_DEV, 0x0d04, 0x0101, NULL, 0, USB_CTRL_TIMEOUT_MS); - if (err < 0) { goto cleanup_buf; } + if (err < 0) + goto cleanup_buf; err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), VENDOR_REQ_REGISTER_WRITE, RT_H2D_VENDOR_DEV, 0x0e00, 0x0101, NULL, 0, USB_CTRL_TIMEOUT_MS); - if (err < 0) { goto cleanup_buf; } + if (err < 0) + goto cleanup_buf; err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), VENDOR_REQ_REGISTER_WRITE, RT_H2D_VENDOR_DEV, 0x0f00, 0x0101, NULL, 0, USB_CTRL_TIMEOUT_MS); - if (err < 0) { goto cleanup_buf; } + if (err < 0) + goto cleanup_buf; + // 4. Rate-dependent register write err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), VENDOR_REQ_REGISTER_WRITE, RT_H2D_VENDOR_DEV, rate_vendor_wValue, 0x0101, NULL, 0, USB_CTRL_TIMEOUT_MS); - if (err < 0) { goto cleanup_buf; } + if (err < 0) + goto cleanup_buf; + // 5. Final register write err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), VENDOR_REQ_REGISTER_WRITE, RT_H2D_VENDOR_DEV, 0x110b, 0x0101, NULL, 0, USB_CTRL_TIMEOUT_MS); - if (err < 0) { goto cleanup_buf; } + if (err < 0) + goto cleanup_buf; + // 6. Enable Streaming err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), VENDOR_REQ_MODE_CONTROL, RT_H2D_VENDOR_DEV, 0x0030, 0x0000, NULL, 0, USB_CTRL_TIMEOUT_MS); - if (err < 0) { goto cleanup_buf; } + if (err < 0) + goto cleanup_buf; + + // --- End Control Message Sequence --- cleanup_buf: + if (err < 0) + dev_err(&dev->dev, "Device configuration failed at rate %d with error %d\n", rate, err); kfree(rate_payload_buf); return err; } @@ -841,6 +935,7 @@ static int tascam_create_pcm(struct tascam_card *tascam) struct snd_pcm *pcm; int err; + // Create one PCM device with 1 playback and 1 (stubbed) capture stream. err = snd_pcm_new(tascam->card, "US144MKII PCM", 0, 1, 1, &pcm); if (err < 0) { dev_err(tascam->card->dev, "Failed to create snd_pcm: %d\n", err); @@ -864,6 +959,7 @@ static int tascam_create_pcm(struct tascam_card *tascam) static void tascam_card_private_free(struct snd_card *card) { struct tascam_card *tascam = card->private_data; + if (tascam && tascam->dev) { usb_put_dev(tascam->dev); tascam->dev = NULL; @@ -878,6 +974,7 @@ static int tascam_probe(struct usb_interface *intf, const struct usb_device_id * int err, dev_idx; u8 *handshake_buf; + // This driver binds to interface 0. dev_idx = intf->cur_altsetting->desc.bInterfaceNumber; if (dev_idx != 0) return -ENODEV; @@ -907,6 +1004,7 @@ static int tascam_probe(struct usb_interface *intf, const struct usb_device_id * le16_to_cpu(dev->descriptor.idProduct), dev->bus->bus_name); + // The device has two interfaces; we need to claim both. tascam->iface1 = usb_ifnum_to_if(dev, 1); if (!tascam->iface1) { dev_err(&intf->dev, "Interface 1 not found.\n"); @@ -920,13 +1018,24 @@ static int tascam_probe(struct usb_interface *intf, const struct usb_device_id * goto free_card_obj; } + // Set both interfaces to alternate setting 1 to enable all endpoints. err = usb_set_interface(dev, 0, 1); - if (err < 0) { dev_err(&intf->dev, "Set Alt Setting on Intf 0 failed: %d\n", err); goto release_iface1_and_free_card; } + if (err < 0) { + dev_err(&intf->dev, "Set Alt Setting on Intf 0 failed: %d\n", err); + goto release_iface1_and_free_card; + } err = usb_set_interface(dev, 1, 1); - if (err < 0) { dev_err(&intf->dev, "Set Alt Setting on Intf 1 failed: %d\n", err); goto release_iface1_and_free_card; } + if (err < 0) { + dev_err(&intf->dev, "Set Alt Setting on Intf 1 failed: %d\n", err); + goto release_iface1_and_free_card; + } + // Perform the initial handshake read, as per the reverse-eng report. handshake_buf = kmalloc(1, GFP_KERNEL); - if (!handshake_buf) { err = -ENOMEM; goto release_iface1_and_free_card; } + if (!handshake_buf) { + err = -ENOMEM; + goto release_iface1_and_free_card; + } err = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), VENDOR_REQ_MODE_CONTROL, RT_D2H_VENDOR_DEV, 0x0000, 0x0000, handshake_buf, 1, USB_CTRL_TIMEOUT_MS); @@ -971,6 +1080,7 @@ static void tascam_disconnect(struct usb_interface *intf) if (!tascam) return; + // Only disconnect if this is the primary interface (iface0). if (intf != tascam->iface0) return; @@ -978,12 +1088,14 @@ static void tascam_disconnect(struct usb_interface *intf) snd_card_disconnect(tascam->card); + // Release the secondary interface. if (tascam->iface1) { usb_set_intfdata(tascam->iface1, NULL); usb_driver_release_interface(&tascam_alsa_driver, tascam->iface1); tascam->iface1 = NULL; } + // The card and its private data will be freed when all PCMs are closed. snd_card_free_when_closed(tascam->card); } diff --git a/us144mkii.ko b/us144mkii.ko index e30e1e7d8a795363f458917f9e97b7ace38b3ff8..5e8fbc30ac34dac8fbc9b0f0fa5987277786b63e 100644 GIT binary patch delta 109416 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 Hn&tlhy-u=E