diff --git a/us144mkii.c b/us144mkii.c index 1414a74..e206faa 100644 --- a/us144mkii.c +++ b/us144mkii.c @@ -67,7 +67,7 @@ MODULE_PARM_DESC(enable, "Enable this US-144MKII soundcard."); #define NUM_PLAYBACK_URBS 8 #define NUM_FEEDBACK_URBS 4 #define MAX_FEEDBACK_PACKETS 5 // For the highest latency setting -#define PLAYBACK_URB_ISO_PACKETS 40 +#define MAX_PLAYBACK_URB_ISO_PACKETS 40 // Max possible for allocation (needed for urb_alloc_urb) #define FEEDBACK_PACKET_SIZE 3 #define USB_CTRL_TIMEOUT_MS 1000 @@ -94,6 +94,7 @@ struct tascam_card { struct snd_pcm_substream *playback_substream; struct urb *playback_urbs[NUM_PLAYBACK_URBS]; size_t playback_urb_alloc_size; + unsigned int playback_urb_iso_packets; // Added for dynamic packet count struct urb *feedback_urbs[NUM_FEEDBACK_URBS]; size_t feedback_urb_alloc_size; @@ -130,17 +131,27 @@ struct tascam_card { * Profile | Feedback URB | Approx. Latency | Typical Period Size (Frames) * Name | Packet Count | (Hardware) | 48kHz | 96kHz * --------|--------------|-----------------|------------|------------ + * Lowest | 1 packet | <= 2ms | <= 96 | <= 192 * Low | 1 packet | <= 2ms | <= 96 | <= 192 * Normal | 2 packets | > 2ms to <= 3ms | <= 144 | <= 288 * High | 5 packets | > 3ms | > 144 | > 288 + * Highest | 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 +static const unsigned int hardware_feedback_packet_counts[] = { + 1, // Tier 0: For Low latency (period_frames <= low_profile_thresh_frames) + 2, // Tier 1: For Normal latency (period_frames <= normal_profile_thresh_frames) + 5, // Tier 2: For High latency (period_frames > normal_profile_thresh_frames) }; +// New array to map ALSA period size tiers to desired PLAYBACK_URB_ISO_PACKETS +// This maps to (0:Low, 1:Normal, 2:High) tiers for simplicity +static const unsigned int playback_iso_packet_counts_tiers[] = { + 8, // Tier 0: For Low latency (e.g., 64-frame buffers) + 24, // Tier 1: For Normal latency (e.g., 128/256-frame buffers) + 40 // Tier 2: For High latency (e.g., 512/1024-frame buffers) +}; + + /* * Pre-calculated patterns for frames-per-microframe based on feedback value. * These are the core of the "Packet Fixing" engine. Each array represents @@ -219,10 +230,11 @@ static const struct snd_pcm_hardware tascam_pcm_hw = { .channels_min = 2, .channels_max = 2, .buffer_bytes_max = 1024 * 1024, - .period_bytes_min = 48 * ALSA_BYTES_PER_FRAME, - .period_bytes_max = 1024 * ALSA_BYTES_PER_FRAME, - .periods_min = 2, - .periods_max = 1024, + // Adjusted min period size to match the lowest *effective* ASIO buffer (64 samples for 48kHz, etc.) + .period_bytes_min = 64 * ALSA_BYTES_PER_FRAME, // Corresponds to 'Low' profile's minimum + .period_bytes_max = 1024 * ALSA_BYTES_PER_FRAME, // Corresponds to 'Highest' profile's maximum + .periods_min = 2, // Standard double-buffering + .periods_max = 1024, // Allow for flexible number of periods }; @@ -290,9 +302,13 @@ static int tascam_alloc_urbs(struct tascam_card *tascam) // Calculate max possible packet size to allocate enough buffer space. // Add a margin of 2 for safety. + // Max frames per packet at 96kHz is 96000/8000 = 12. So 14 frames/packet is safe. 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; + + // Allocate playback URB buffer size for the absolute maximum possible packets + // (40, as determined for >5ms latency). This ensures we have enough memory. + tascam->playback_urb_alloc_size = max_packet_size * MAX_PLAYBACK_URB_ISO_PACKETS; if (tascam->playback_urb_alloc_size == 0) { dev_err(tascam->card->dev, "Calculated playback_urb_alloc_size is zero.\n"); @@ -300,7 +316,8 @@ 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); + // URB is allocated with max possible packets, actual # set in hw_params/prepare + struct urb *urb = usb_alloc_urb(MAX_PLAYBACK_URB_ISO_PACKETS, GFP_KERNEL); if (!urb) goto error; tascam->playback_urbs[i] = urb; @@ -316,7 +333,7 @@ static int tascam_alloc_urbs(struct tascam_card *tascam) urb->interval = 1; // bInterval from descriptor urb->context = tascam; urb->complete = playback_urb_complete; - urb->number_of_packets = PLAYBACK_URB_ISO_PACKETS; + // Do NOT set urb->number_of_packets here. It will be set dynamically in hw_params. } tascam->feedback_urb_alloc_size = FEEDBACK_PACKET_SIZE * MAX_FEEDBACK_PACKETS; @@ -363,22 +380,23 @@ static int tascam_pcm_period_size_rule(struct snd_pcm_hw_params *params, const struct snd_interval *rate_interval = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval new_constraint = { .min = 0, .max = 0, .integer = 1 }; + // Update period size constraints based on your tables for min/max reported ASIO buffers switch (rate_interval->min) { case 44100: - new_constraint.min = 49; - new_constraint.max = 512; + new_constraint.min = 49; // Lowest profile for 44.1kHz + new_constraint.max = 512; // Highest profile for 44.1kHz break; case 48000: - new_constraint.min = 48; - new_constraint.max = 512; + new_constraint.min = 48; // Lowest profile for 48kHz + new_constraint.max = 512; // Highest profile for 48kHz break; case 88200: - new_constraint.min = 98; - new_constraint.max = 1024; + new_constraint.min = 98; // Lowest profile for 88.2kHz + new_constraint.max = 1024; // Highest profile for 88.2kHz break; case 96000: - new_constraint.min = 96; - new_constraint.max = 1024; + new_constraint.min = 96; // Lowest profile for 96kHz + new_constraint.max = 1024; // Highest profile for 96kHz break; default: return -EINVAL; @@ -427,25 +445,71 @@ static int tascam_pcm_hw_params(struct snd_pcm_substream *substream, int err, i; unsigned int rate = params_rate(params); unsigned int period_frames = params_period_size(params); - unsigned int normal_thresh_ms = 2; - unsigned int high_thresh_ms = 3; - int active_latency_profile; + + // ASIO reported buffer sizes (in frames) for each profile at current rate + unsigned int lowest_asio_frames; + unsigned int low_asio_frames; + unsigned int normal_asio_frames; + unsigned int high_asio_frames; + + int tier_idx; // 0 for Low, 1 for Normal, 2 for High USB packet counts unsigned int feedback_urb_packets; + unsigned int playback_iso_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; // Low latency (1 feedback packet) - } else if (period_frames <= (high_thresh_ms * rate / 1000)) { - active_latency_profile = 2; // Normal latency (2 feedback packets) - } else { - active_latency_profile = 3; // High latency (5 feedback packets) + // Set ASIO reported frame counts based on sample rate from your tables + switch (rate) { + case 44100: + lowest_asio_frames = 49; + low_asio_frames = 64; + normal_asio_frames = 128; + high_asio_frames = 256; + break; + case 48000: + lowest_asio_frames = 48; + low_asio_frames = 64; + normal_asio_frames = 128; + high_asio_frames = 256; + break; + case 88200: + lowest_asio_frames = 98; + low_asio_frames = 128; + normal_asio_frames = 256; + high_asio_frames = 512; + break; + case 96000: + lowest_asio_frames = 96; + low_asio_frames = 128; + normal_asio_frames = 256; + high_asio_frames = 512; + break; + default: + dev_err(tascam->card->dev, "Unsupported sample rate: %u\n", rate); + return -EINVAL; + } + + // Determine tier_idx based on requested period_frames for playback_iso_packet_counts_tiers + // This maps ALSA's requested period_frames to one of your 3 USB packet count tiers. + // We'll use the 'ASIO Buffer (Reported)' values as our decision points. + if (period_frames <= low_asio_frames) { // Covers original Lowest, Low + tier_idx = 0; // Low USB Packet Count (8) + } else if (period_frames <= normal_asio_frames) { // Covers original Normal + tier_idx = 1; // Normal USB Packet Count (24) + } else { // Covers original High, Highest (period_frames > normal_asio_frames) + tier_idx = 2; // High USB Packet Count (40) } dev_info(tascam->card->dev, - "User requested period of %u frames, selecting latency profile %d\n", - period_frames, active_latency_profile); + "User requested period of %u frames @ %u Hz, mapping to playback URB tier %d\n", + period_frames, rate, tier_idx); - feedback_urb_packets = latency_profile_packets[active_latency_profile]; + // Select feedback_urb_packets based on the determined tier_idx, + // using the existing logic from latency_profile_packets. + // Note: The hardware_feedback_packet_counts array is also 0-indexed for these tiers. + // Tier 0 (low) maps to index 0, Tier 1 (normal) maps to index 1, Tier 2 (high) maps to index 2 + feedback_urb_packets = hardware_feedback_packet_counts[tier_idx]; + playback_iso_packets = playback_iso_packet_counts_tiers[tier_idx]; + + tascam->playback_urb_iso_packets = playback_iso_packets; // Store for prepare and complete functions // Configure the feedback URBs for the selected latency profile. for (i = 0; i < NUM_FEEDBACK_URBS; i++) { @@ -460,6 +524,13 @@ static int tascam_pcm_hw_params(struct snd_pcm_substream *substream, } } + // Configure playback URBs with the dynamically chosen packet count + for (i = 0; i < NUM_PLAYBACK_URBS; i++) { + struct urb *urb = tascam->playback_urbs[i]; + urb->number_of_packets = tascam->playback_urb_iso_packets; + } + + err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); if (err < 0) return err; @@ -487,7 +558,8 @@ static int tascam_pcm_hw_params(struct snd_pcm_substream *substream, tascam->feedback_max_value = 98; break; default: - dev_err(tascam->card->dev, "Unsupported sample rate for feedback: %u\n", rate); + // Should not happen if tascam_pcm_period_size_rule is correct + dev_err(tascam->card->dev, "Unsupported sample rate for feedback patterns: %u\n", rate); return -EINVAL; } @@ -535,9 +607,9 @@ static int tascam_pcm_prepare(struct snd_pcm_substream *substream) for (i = 0; i < FEEDBACK_ACCUMULATOR_SIZE; i++) tascam->feedback_accumulator_pattern[i] = nominal_frames_per_packet; - // Initialize playback URBs with nominal packet sizes. + // Initialize playback URBs with nominal packet sizes based on the determined count nominal_bytes_per_packet = nominal_frames_per_packet * DEVICE_BYTES_PER_FRAME; - total_bytes_in_urb = nominal_bytes_per_packet * PLAYBACK_URB_ISO_PACKETS; + total_bytes_in_urb = nominal_bytes_per_packet * tascam->playback_urb_iso_packets; // Use dynamic value if (total_bytes_in_urb > tascam->playback_urb_alloc_size) { dev_err(tascam->card->dev, "Prepare: Nominal URB size %zu > allocated %zu\n", @@ -548,9 +620,12 @@ 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]; + // This was already set in hw_params, but good to ensure consistency + urb->number_of_packets = tascam->playback_urb_iso_packets; + 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++) { + for (i = 0; i < tascam->playback_urb_iso_packets; i++) { // Use dynamic value here urb->iso_frame_desc[i].offset = i * nominal_bytes_per_packet; urb->iso_frame_desc[i].length = nominal_bytes_per_packet; } @@ -663,7 +738,8 @@ static void playback_urb_complete(struct urb *urb) spin_lock_irqsave(&tascam->lock, flags); // Prepare the next playback URB. - for (i = 0; i < PLAYBACK_URB_ISO_PACKETS; i++) { + // Use the dynamic number of packets for this URB + for (i = 0; i < tascam->playback_urb_iso_packets; i++) { unsigned int frames_for_packet; size_t bytes_for_packet; @@ -995,6 +1071,7 @@ static int tascam_probe(struct usb_interface *intf, const struct usb_device_id * spin_lock_init(&tascam->lock); atomic_set(&tascam->playback_active, 0); tascam->current_rate = 0; // Initialize to 0 to force first configuration + tascam->playback_urb_iso_packets = 0; // Initialize dynamic packet count strscpy(card->driver, DRIVER_NAME, sizeof(card->driver)); strscpy(card->shortname, "TASCAM US-144MKII", sizeof(card->shortname)); diff --git a/us144mkii.ko b/us144mkii.ko index 5e8fbc3..4415211 100644 Binary files a/us144mkii.ko and b/us144mkii.ko differ