feat: Playback goal complete. Fully working up to spec audio playback handling
The ALSA driver properly transfers audio data to the device, using JACK with 48 samples and -n 3 gives stable lowest latency possible for it at 48khz. Future goals are stablity on resume and shutdown, MIDI, audio routing, alsa mixers and GUI tool to trigger these mixers.
This commit is contained in:
parent
b870235238
commit
b88d64867b
304
us144mkii.c
304
us144mkii.c
|
|
@ -4,6 +4,10 @@
|
||||||
* ALSA Driver for TASCAM US-144MKII Audio Interface
|
* ALSA Driver for TASCAM US-144MKII Audio Interface
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// TODO: Verify on resume/shutdown that device works as expected
|
||||||
|
// TODO: Implement audio capture, MIDI IN/OUT
|
||||||
|
// TODO: 4 Channel routing & ALSA mixers and GUI tool that will use these mixers to control device's audio in/out routing, latency profile, sample rate & digital out format.
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/usb.h>
|
#include <linux/usb.h>
|
||||||
|
|
@ -22,10 +26,7 @@ MODULE_LICENSE("GPL");
|
||||||
|
|
||||||
#define DRIVER_NAME "us144mkii"
|
#define DRIVER_NAME "us144mkii"
|
||||||
|
|
||||||
/*============================================================================*/
|
/* Module Parameters */
|
||||||
/* --- Module Parameters --- */
|
|
||||||
/*============================================================================*/
|
|
||||||
|
|
||||||
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
|
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
|
||||||
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
|
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
|
||||||
static bool enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0};
|
static bool enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0};
|
||||||
|
|
@ -37,18 +38,14 @@ MODULE_PARM_DESC(id, "ID string for the US-144MKII soundcard.");
|
||||||
module_param_array(enable, bool, NULL, 0444);
|
module_param_array(enable, bool, NULL, 0444);
|
||||||
MODULE_PARM_DESC(enable, "Enable this US-144MKII soundcard.");
|
MODULE_PARM_DESC(enable, "Enable this US-144MKII soundcard.");
|
||||||
|
|
||||||
|
/* Constants and Structs */
|
||||||
/*============================================================================*/
|
|
||||||
/* --- Constants and Structs --- */
|
|
||||||
/*============================================================================*/
|
|
||||||
|
|
||||||
#define TASCAM_VID 0x0644
|
#define TASCAM_VID 0x0644
|
||||||
#define TASCAM_PID 0x8020
|
#define TASCAM_PID 0x8020
|
||||||
|
|
||||||
/* USB Endpoints from descriptor report */
|
/* USB Endpoints */
|
||||||
#define EP_AUDIO_OUT 0x02 // Isochronous OUT for playback audio
|
#define EP_AUDIO_OUT 0x02 // Isochronous OUT for playback audio
|
||||||
#define EP_PLAYBACK_FEEDBACK 0x81 // Isochronous IN for clock feedback
|
#define EP_PLAYBACK_FEEDBACK 0x81 // Isochronous IN for clock feedback
|
||||||
#define EP_CAPTURE_DATA 0x86 // Bulk IN for capture audio/MIDI
|
#define EP_CAPTURE_DATA 0x86 // Bulk IN for capture audio
|
||||||
#define EP_MIDI_OUT 0x04 // Bulk OUT for MIDI
|
#define EP_MIDI_OUT 0x04 // Bulk OUT for MIDI
|
||||||
#define EP_MIDI_IN 0x83 // Bulk IN for MIDI
|
#define EP_MIDI_IN 0x83 // Bulk IN for MIDI
|
||||||
|
|
||||||
|
|
@ -66,8 +63,8 @@ MODULE_PARM_DESC(enable, "Enable this US-144MKII soundcard.");
|
||||||
/* URB Configuration */
|
/* URB Configuration */
|
||||||
#define NUM_PLAYBACK_URBS 8
|
#define NUM_PLAYBACK_URBS 8
|
||||||
#define NUM_FEEDBACK_URBS 4
|
#define NUM_FEEDBACK_URBS 4
|
||||||
#define MAX_FEEDBACK_PACKETS 5 // For the highest latency setting
|
#define MAX_FEEDBACK_PACKETS 5
|
||||||
#define MAX_PLAYBACK_URB_ISO_PACKETS 40 // Max possible for allocation (needed for urb_alloc_urb)
|
#define MAX_PLAYBACK_URB_ISO_PACKETS 40
|
||||||
#define FEEDBACK_PACKET_SIZE 3
|
#define FEEDBACK_PACKET_SIZE 3
|
||||||
#define USB_CTRL_TIMEOUT_MS 1000
|
#define USB_CTRL_TIMEOUT_MS 1000
|
||||||
|
|
||||||
|
|
@ -78,7 +75,6 @@ MODULE_PARM_DESC(enable, "Enable this US-144MKII soundcard.");
|
||||||
#define ALSA_BYTES_PER_FRAME (ALSA_CHANNELS * BYTES_PER_SAMPLE)
|
#define ALSA_BYTES_PER_FRAME (ALSA_CHANNELS * BYTES_PER_SAMPLE)
|
||||||
#define DEVICE_BYTES_PER_FRAME (DEVICE_CHANNELS * BYTES_PER_SAMPLE)
|
#define DEVICE_BYTES_PER_FRAME (DEVICE_CHANNELS * BYTES_PER_SAMPLE)
|
||||||
|
|
||||||
/* Feedback Synchronization Engine Configuration */
|
|
||||||
#define FEEDBACK_ACCUMULATOR_SIZE 128
|
#define FEEDBACK_ACCUMULATOR_SIZE 128
|
||||||
|
|
||||||
static struct usb_driver tascam_alsa_driver;
|
static struct usb_driver tascam_alsa_driver;
|
||||||
|
|
@ -94,7 +90,7 @@ struct tascam_card {
|
||||||
struct snd_pcm_substream *playback_substream;
|
struct snd_pcm_substream *playback_substream;
|
||||||
struct urb *playback_urbs[NUM_PLAYBACK_URBS];
|
struct urb *playback_urbs[NUM_PLAYBACK_URBS];
|
||||||
size_t playback_urb_alloc_size;
|
size_t playback_urb_alloc_size;
|
||||||
unsigned int playback_urb_iso_packets; // Added for dynamic packet count
|
unsigned int playback_urb_iso_packets; // Dynamic packet count
|
||||||
|
|
||||||
struct urb *feedback_urbs[NUM_FEEDBACK_URBS];
|
struct urb *feedback_urbs[NUM_FEEDBACK_URBS];
|
||||||
size_t feedback_urb_alloc_size;
|
size_t feedback_urb_alloc_size;
|
||||||
|
|
@ -103,55 +99,44 @@ struct tascam_card {
|
||||||
atomic_t playback_active;
|
atomic_t playback_active;
|
||||||
int current_rate;
|
int current_rate;
|
||||||
|
|
||||||
/* --- Feedback Synchronization State --- */
|
/* Feedback Synchronization State */
|
||||||
unsigned int feedback_accumulator_pattern[FEEDBACK_ACCUMULATOR_SIZE];
|
unsigned int feedback_accumulator_pattern[FEEDBACK_ACCUMULATOR_SIZE];
|
||||||
unsigned int feedback_pattern_out_idx; // Read index for playback
|
unsigned int feedback_pattern_out_idx;
|
||||||
unsigned int feedback_pattern_in_idx; // Write index from feedback
|
unsigned int feedback_pattern_in_idx;
|
||||||
bool feedback_synced;
|
bool feedback_synced;
|
||||||
unsigned int feedback_urb_skip_count; // Initial URBs to discard
|
unsigned int feedback_urb_skip_count;
|
||||||
|
|
||||||
/* --- Playback Position Tracking --- */
|
/* Playback Position Tracking */
|
||||||
snd_pcm_uframes_t driver_playback_pos; // Pointer within ALSA buffer
|
snd_pcm_uframes_t driver_playback_pos;
|
||||||
u64 playback_frames_consumed; // Total frames consumed by hw
|
u64 playback_frames_consumed;
|
||||||
u64 last_period_pos; // Last reported period
|
u64 last_period_pos;
|
||||||
|
|
||||||
/* --- Rate-Specific Data --- */
|
/* Rate-Specific Data */
|
||||||
const unsigned int (*feedback_patterns)[8];
|
const unsigned int (*feedback_patterns)[8];
|
||||||
unsigned int feedback_base_value;
|
unsigned int feedback_base_value;
|
||||||
unsigned int feedback_max_value;
|
unsigned int feedback_max_value;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Latency Profile Cheatsheet (from reverse-engineering report)
|
* Latency Profile Cheatsheet
|
||||||
*
|
*
|
||||||
* The driver selects a hardware profile dynamically based on the period size
|
* The driver selects a hardware profile dynamically based on the period size
|
||||||
* requested by the application. The device has 3 true hardware modes,
|
* requested by the application. The device has 3 true hardware modes,
|
||||||
* determined by the number of packets in the feedback URB.
|
* 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
|
|
||||||
* --------|--------------|-----------------|------------|------------
|
|
||||||
* 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 hardware_feedback_packet_counts[] = {
|
static const unsigned int hardware_feedback_packet_counts[] = {
|
||||||
1, // Tier 0: For Low latency (period_frames <= low_profile_thresh_frames)
|
1, // Tier 0: For Low latency
|
||||||
2, // Tier 1: For Normal latency (period_frames <= normal_profile_thresh_frames)
|
2, // Tier 1: For Normal latency
|
||||||
5, // Tier 2: For High latency (period_frames > normal_profile_thresh_frames)
|
5, // Tier 2: For High latency
|
||||||
};
|
};
|
||||||
|
|
||||||
// New array to map ALSA period size tiers to desired PLAYBACK_URB_ISO_PACKETS
|
// Maps 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[] = {
|
static const unsigned int playback_iso_packet_counts_tiers[] = {
|
||||||
8, // Tier 0: For Low latency (e.g., 64-frame buffers)
|
8, // Tier 0: For Low latency (e.g., 64-frame buffers)
|
||||||
24, // Tier 1: For Normal latency (e.g., 128/256-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)
|
40 // Tier 2: For High latency (e.g., 512/1024-frame buffers)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Pre-calculated patterns for frames-per-microframe based on feedback value.
|
* Pre-calculated patterns for frames-per-microframe based on feedback value.
|
||||||
* These are the core of the "Packet Fixing" engine. Each array represents
|
* These are the core of the "Packet Fixing" engine. Each array represents
|
||||||
|
|
@ -189,11 +174,7 @@ static const unsigned int patterns_44khz[5][8] = {
|
||||||
{6, 6, 6, 5, 6, 6, 6, 5} // 46
|
{6, 6, 6, 5, 6, 6, 6, 5} // 46
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Forward Declarations */
|
||||||
/*============================================================================*/
|
|
||||||
/* --- Forward Declarations --- */
|
|
||||||
/*============================================================================*/
|
|
||||||
|
|
||||||
static void tascam_free_urbs(struct tascam_card *tascam);
|
static void tascam_free_urbs(struct tascam_card *tascam);
|
||||||
static int tascam_alloc_urbs(struct tascam_card *tascam);
|
static int tascam_alloc_urbs(struct tascam_card *tascam);
|
||||||
static int tascam_pcm_open(struct snd_pcm_substream *substream);
|
static int tascam_pcm_open(struct snd_pcm_substream *substream);
|
||||||
|
|
@ -210,11 +191,7 @@ static int tascam_create_pcm(struct tascam_card *tascam);
|
||||||
static int tascam_probe(struct usb_interface *intf, const struct usb_device_id *id);
|
static int tascam_probe(struct usb_interface *intf, const struct usb_device_id *id);
|
||||||
static void tascam_disconnect(struct usb_interface *intf);
|
static void tascam_disconnect(struct usb_interface *intf);
|
||||||
|
|
||||||
|
/* PCM Hardware Definition */
|
||||||
/*============================================================================*/
|
|
||||||
/* --- PCM Hardware Definition --- */
|
|
||||||
/*============================================================================*/
|
|
||||||
|
|
||||||
static const struct snd_pcm_hardware tascam_pcm_hw = {
|
static const struct snd_pcm_hardware tascam_pcm_hw = {
|
||||||
.info = (SNDRV_PCM_INFO_MMAP |
|
.info = (SNDRV_PCM_INFO_MMAP |
|
||||||
SNDRV_PCM_INFO_INTERLEAVED |
|
SNDRV_PCM_INFO_INTERLEAVED |
|
||||||
|
|
@ -230,18 +207,13 @@ static const struct snd_pcm_hardware tascam_pcm_hw = {
|
||||||
.channels_min = 2,
|
.channels_min = 2,
|
||||||
.channels_max = 2,
|
.channels_max = 2,
|
||||||
.buffer_bytes_max = 1024 * 1024,
|
.buffer_bytes_max = 1024 * 1024,
|
||||||
// Adjusted min period size to match the lowest *effective* ASIO buffer (64 samples for 48kHz, etc.)
|
.period_bytes_min = 48 * ALSA_BYTES_PER_FRAME,
|
||||||
.period_bytes_min = 64 * ALSA_BYTES_PER_FRAME, // Corresponds to 'Low' profile's minimum
|
.period_bytes_max = 1024 * ALSA_BYTES_PER_FRAME,
|
||||||
.period_bytes_max = 1024 * ALSA_BYTES_PER_FRAME, // Corresponds to 'Highest' profile's maximum
|
.periods_min = 2,
|
||||||
.periods_min = 2, // Standard double-buffering
|
.periods_max = 1024,
|
||||||
.periods_max = 1024, // Allow for flexible number of periods
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* PCM Operations */
|
||||||
/*============================================================================*/
|
|
||||||
/* --- PCM Operations --- */
|
|
||||||
/*============================================================================*/
|
|
||||||
|
|
||||||
static struct snd_pcm_ops tascam_playback_ops = {
|
static struct snd_pcm_ops tascam_playback_ops = {
|
||||||
.open = tascam_pcm_open,
|
.open = tascam_pcm_open,
|
||||||
.close = tascam_pcm_close,
|
.close = tascam_pcm_close,
|
||||||
|
|
@ -261,11 +233,6 @@ static struct snd_pcm_ops tascam_capture_ops = {
|
||||||
.close = tascam_capture_close_stub,
|
.close = tascam_capture_close_stub,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/*============================================================================*/
|
|
||||||
/* --- URB Allocation and Cleanup --- */
|
|
||||||
/*============================================================================*/
|
|
||||||
|
|
||||||
static void tascam_free_urbs(struct tascam_card *tascam)
|
static void tascam_free_urbs(struct tascam_card *tascam)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
@ -301,13 +268,11 @@ static int tascam_alloc_urbs(struct tascam_card *tascam)
|
||||||
size_t max_frames_per_packet, max_packet_size;
|
size_t max_frames_per_packet, max_packet_size;
|
||||||
|
|
||||||
// Calculate max possible packet size to allocate enough buffer space.
|
// Calculate max possible packet size to allocate enough buffer space.
|
||||||
// Add a margin of 2 for safety.
|
// Max frames per packet at 96kHz is 12. Add a margin 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_frames_per_packet = (96000 / 8000) + 2;
|
||||||
max_packet_size = max_frames_per_packet * DEVICE_BYTES_PER_FRAME;
|
max_packet_size = max_frames_per_packet * DEVICE_BYTES_PER_FRAME;
|
||||||
|
|
||||||
// Allocate playback URB buffer size for the absolute maximum possible packets
|
// Allocate playback URB buffer 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;
|
tascam->playback_urb_alloc_size = max_packet_size * MAX_PLAYBACK_URB_ISO_PACKETS;
|
||||||
|
|
||||||
if (tascam->playback_urb_alloc_size == 0) {
|
if (tascam->playback_urb_alloc_size == 0) {
|
||||||
|
|
@ -316,7 +281,7 @@ static int tascam_alloc_urbs(struct tascam_card *tascam)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < NUM_PLAYBACK_URBS; i++) {
|
for (i = 0; i < NUM_PLAYBACK_URBS; i++) {
|
||||||
// URB is allocated with max possible packets, actual # set in hw_params/prepare
|
// URB is allocated with max possible packets; actual # is set in hw_params.
|
||||||
struct urb *urb = usb_alloc_urb(MAX_PLAYBACK_URB_ISO_PACKETS, GFP_KERNEL);
|
struct urb *urb = usb_alloc_urb(MAX_PLAYBACK_URB_ISO_PACKETS, GFP_KERNEL);
|
||||||
if (!urb)
|
if (!urb)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
@ -330,10 +295,9 @@ static int tascam_alloc_urbs(struct tascam_card *tascam)
|
||||||
urb->dev = tascam->dev;
|
urb->dev = tascam->dev;
|
||||||
urb->pipe = usb_sndisocpipe(tascam->dev, EP_AUDIO_OUT);
|
urb->pipe = usb_sndisocpipe(tascam->dev, EP_AUDIO_OUT);
|
||||||
urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
|
urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
|
||||||
urb->interval = 1; // bInterval from descriptor
|
urb->interval = 1;
|
||||||
urb->context = tascam;
|
urb->context = tascam;
|
||||||
urb->complete = playback_urb_complete;
|
urb->complete = playback_urb_complete;
|
||||||
// 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;
|
tascam->feedback_urb_alloc_size = FEEDBACK_PACKET_SIZE * MAX_FEEDBACK_PACKETS;
|
||||||
|
|
@ -352,7 +316,7 @@ static int tascam_alloc_urbs(struct tascam_card *tascam)
|
||||||
f_urb->dev = tascam->dev;
|
f_urb->dev = tascam->dev;
|
||||||
f_urb->pipe = usb_rcvisocpipe(tascam->dev, EP_PLAYBACK_FEEDBACK);
|
f_urb->pipe = usb_rcvisocpipe(tascam->dev, EP_PLAYBACK_FEEDBACK);
|
||||||
f_urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
|
f_urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
|
||||||
f_urb->interval = 4; // bInterval from descriptor
|
f_urb->interval = 4;
|
||||||
f_urb->context = tascam;
|
f_urb->context = tascam;
|
||||||
f_urb->complete = feedback_urb_complete;
|
f_urb->complete = feedback_urb_complete;
|
||||||
}
|
}
|
||||||
|
|
@ -365,46 +329,6 @@ error:
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*============================================================================*/
|
|
||||||
/* --- 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)
|
|
||||||
{
|
|
||||||
struct snd_interval *period_interval = hw_param_interval(params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
|
|
||||||
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; // Lowest profile for 44.1kHz
|
|
||||||
new_constraint.max = 512; // Highest profile for 44.1kHz
|
|
||||||
break;
|
|
||||||
case 48000:
|
|
||||||
new_constraint.min = 48; // Lowest profile for 48kHz
|
|
||||||
new_constraint.max = 512; // Highest profile for 48kHz
|
|
||||||
break;
|
|
||||||
case 88200:
|
|
||||||
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; // Lowest profile for 96kHz
|
|
||||||
new_constraint.max = 1024; // Highest profile for 96kHz
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return snd_interval_refine(period_interval, &new_constraint);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int tascam_pcm_open(struct snd_pcm_substream *substream)
|
static int tascam_pcm_open(struct snd_pcm_substream *substream)
|
||||||
{
|
{
|
||||||
struct tascam_card *tascam = snd_pcm_substream_chip(substream);
|
struct tascam_card *tascam = snd_pcm_substream_chip(substream);
|
||||||
|
|
@ -415,14 +339,6 @@ static int tascam_pcm_open(struct snd_pcm_substream *substream)
|
||||||
tascam->playback_substream = substream;
|
tascam->playback_substream = substream;
|
||||||
atomic_set(&tascam->playback_active, 0);
|
atomic_set(&tascam->playback_active, 0);
|
||||||
|
|
||||||
err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
|
|
||||||
tascam_pcm_period_size_rule, NULL,
|
|
||||||
SNDRV_PCM_HW_PARAM_RATE, -1);
|
|
||||||
if (err < 0) {
|
|
||||||
dev_err(tascam->card->dev, "Failed to add period size rule: %d\n", err);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
err = tascam_alloc_urbs(tascam);
|
err = tascam_alloc_urbs(tascam);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
|
|
@ -445,71 +361,50 @@ static int tascam_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||||
int err, i;
|
int err, i;
|
||||||
unsigned int rate = params_rate(params);
|
unsigned int rate = params_rate(params);
|
||||||
unsigned int period_frames = params_period_size(params);
|
unsigned int period_frames = params_period_size(params);
|
||||||
|
unsigned int low_asio_frames, normal_asio_frames;
|
||||||
// ASIO reported buffer sizes (in frames) for each profile at current rate
|
int tier_idx;
|
||||||
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 feedback_urb_packets;
|
||||||
unsigned int playback_iso_packets;
|
unsigned int playback_iso_packets;
|
||||||
|
|
||||||
// Set ASIO reported frame counts based on sample rate from your tables
|
// Set ASIO-reported frame counts based on sample rate.
|
||||||
switch (rate) {
|
switch (rate) {
|
||||||
case 44100:
|
case 44100:
|
||||||
lowest_asio_frames = 49;
|
low_asio_frames = 64;
|
||||||
low_asio_frames = 64;
|
normal_asio_frames = 128;
|
||||||
normal_asio_frames = 128;
|
break;
|
||||||
high_asio_frames = 256;
|
case 48000:
|
||||||
break;
|
low_asio_frames = 64;
|
||||||
case 48000:
|
normal_asio_frames = 128;
|
||||||
lowest_asio_frames = 48;
|
break;
|
||||||
low_asio_frames = 64;
|
case 88200:
|
||||||
normal_asio_frames = 128;
|
low_asio_frames = 128;
|
||||||
high_asio_frames = 256;
|
normal_asio_frames = 256;
|
||||||
break;
|
break;
|
||||||
case 88200:
|
case 96000:
|
||||||
lowest_asio_frames = 98;
|
low_asio_frames = 128;
|
||||||
low_asio_frames = 128;
|
normal_asio_frames = 256;
|
||||||
normal_asio_frames = 256;
|
break;
|
||||||
high_asio_frames = 512;
|
default:
|
||||||
break;
|
dev_err(tascam->card->dev, "Unsupported sample rate: %u\n", rate);
|
||||||
case 96000:
|
return -EINVAL;
|
||||||
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
|
// Determine latency tier based on requested period size.
|
||||||
// This maps ALSA's requested period_frames to one of your 3 USB packet count tiers.
|
if (period_frames <= low_asio_frames) {
|
||||||
// We'll use the 'ASIO Buffer (Reported)' values as our decision points.
|
tier_idx = 0; // Low
|
||||||
if (period_frames <= low_asio_frames) { // Covers original Lowest, Low
|
} else if (period_frames <= normal_asio_frames) {
|
||||||
tier_idx = 0; // Low USB Packet Count (8)
|
tier_idx = 1; // Normal
|
||||||
} else if (period_frames <= normal_asio_frames) { // Covers original Normal
|
} else {
|
||||||
tier_idx = 1; // Normal USB Packet Count (24)
|
tier_idx = 2; // High
|
||||||
} else { // Covers original High, Highest (period_frames > normal_asio_frames)
|
|
||||||
tier_idx = 2; // High USB Packet Count (40)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_info(tascam->card->dev,
|
dev_info(tascam->card->dev,
|
||||||
"User requested period of %u frames @ %u Hz, mapping to playback URB tier %d\n",
|
"User requested period of %u frames @ %u Hz, mapping to playback URB tier %d\n",
|
||||||
period_frames, rate, tier_idx);
|
period_frames, rate, tier_idx);
|
||||||
|
|
||||||
// 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];
|
feedback_urb_packets = hardware_feedback_packet_counts[tier_idx];
|
||||||
playback_iso_packets = playback_iso_packet_counts_tiers[tier_idx];
|
playback_iso_packets = playback_iso_packet_counts_tiers[tier_idx];
|
||||||
|
tascam->playback_urb_iso_packets = playback_iso_packets;
|
||||||
tascam->playback_urb_iso_packets = playback_iso_packets; // Store for prepare and complete functions
|
|
||||||
|
|
||||||
// Configure the feedback URBs for the selected latency profile.
|
// Configure the feedback URBs for the selected latency profile.
|
||||||
for (i = 0; i < NUM_FEEDBACK_URBS; i++) {
|
for (i = 0; i < NUM_FEEDBACK_URBS; i++) {
|
||||||
|
|
@ -524,12 +419,11 @@ static int tascam_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure playback URBs with the dynamically chosen packet count
|
// Configure playback URBs with the dynamically chosen packet count.
|
||||||
for (i = 0; i < NUM_PLAYBACK_URBS; i++) {
|
for (i = 0; i < NUM_PLAYBACK_URBS; i++) {
|
||||||
struct urb *urb = tascam->playback_urbs[i];
|
struct urb *urb = tascam->playback_urbs[i];
|
||||||
urb->number_of_packets = tascam->playback_urb_iso_packets;
|
urb->number_of_packets = tascam->playback_urb_iso_packets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
|
err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
|
|
@ -558,7 +452,6 @@ static int tascam_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||||
tascam->feedback_max_value = 98;
|
tascam->feedback_max_value = 98;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// 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);
|
dev_err(tascam->card->dev, "Unsupported sample rate for feedback patterns: %u\n", rate);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
@ -607,9 +500,9 @@ static int tascam_pcm_prepare(struct snd_pcm_substream *substream)
|
||||||
for (i = 0; i < FEEDBACK_ACCUMULATOR_SIZE; i++)
|
for (i = 0; i < FEEDBACK_ACCUMULATOR_SIZE; i++)
|
||||||
tascam->feedback_accumulator_pattern[i] = nominal_frames_per_packet;
|
tascam->feedback_accumulator_pattern[i] = nominal_frames_per_packet;
|
||||||
|
|
||||||
// Initialize playback URBs with nominal packet sizes based on the determined count
|
// Initialize playback URBs with nominal packet sizes.
|
||||||
nominal_bytes_per_packet = nominal_frames_per_packet * DEVICE_BYTES_PER_FRAME;
|
nominal_bytes_per_packet = nominal_frames_per_packet * DEVICE_BYTES_PER_FRAME;
|
||||||
total_bytes_in_urb = nominal_bytes_per_packet * tascam->playback_urb_iso_packets; // Use dynamic value
|
total_bytes_in_urb = nominal_bytes_per_packet * tascam->playback_urb_iso_packets;
|
||||||
|
|
||||||
if (total_bytes_in_urb > tascam->playback_urb_alloc_size) {
|
if (total_bytes_in_urb > tascam->playback_urb_alloc_size) {
|
||||||
dev_err(tascam->card->dev, "Prepare: Nominal URB size %zu > allocated %zu\n",
|
dev_err(tascam->card->dev, "Prepare: Nominal URB size %zu > allocated %zu\n",
|
||||||
|
|
@ -620,12 +513,10 @@ static int tascam_pcm_prepare(struct snd_pcm_substream *substream)
|
||||||
for (u = 0; u < NUM_PLAYBACK_URBS; u++) {
|
for (u = 0; u < NUM_PLAYBACK_URBS; u++) {
|
||||||
struct urb *urb = tascam->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;
|
||||||
urb->number_of_packets = tascam->playback_urb_iso_packets;
|
|
||||||
|
|
||||||
memset(urb->transfer_buffer, 0, tascam->playback_urb_alloc_size);
|
memset(urb->transfer_buffer, 0, tascam->playback_urb_alloc_size);
|
||||||
urb->transfer_buffer_length = total_bytes_in_urb;
|
urb->transfer_buffer_length = total_bytes_in_urb;
|
||||||
for (i = 0; i < tascam->playback_urb_iso_packets; i++) { // Use dynamic value here
|
for (i = 0; i < tascam->playback_urb_iso_packets; i++) {
|
||||||
urb->iso_frame_desc[i].offset = i * nominal_bytes_per_packet;
|
urb->iso_frame_desc[i].offset = i * nominal_bytes_per_packet;
|
||||||
urb->iso_frame_desc[i].length = nominal_bytes_per_packet;
|
urb->iso_frame_desc[i].length = nominal_bytes_per_packet;
|
||||||
}
|
}
|
||||||
|
|
@ -703,15 +594,9 @@ static snd_pcm_uframes_t tascam_pcm_pointer(struct snd_pcm_substream *substream)
|
||||||
pos = tascam->playback_frames_consumed;
|
pos = tascam->playback_frames_consumed;
|
||||||
|
|
||||||
// Return the hardware position within the circular buffer.
|
// 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;
|
return runtime ? pos % runtime->buffer_size : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*============================================================================*/
|
|
||||||
/* --- URB Completion Handlers --- */
|
|
||||||
/*============================================================================*/
|
|
||||||
|
|
||||||
// This is the playback half of the "Packet Fixing" engine.
|
// This is the playback half of the "Packet Fixing" engine.
|
||||||
static void playback_urb_complete(struct urb *urb)
|
static void playback_urb_complete(struct urb *urb)
|
||||||
{
|
{
|
||||||
|
|
@ -738,7 +623,6 @@ static void playback_urb_complete(struct urb *urb)
|
||||||
spin_lock_irqsave(&tascam->lock, flags);
|
spin_lock_irqsave(&tascam->lock, flags);
|
||||||
|
|
||||||
// Prepare the next playback URB.
|
// Prepare the next playback URB.
|
||||||
// Use the dynamic number of packets for this URB
|
|
||||||
for (i = 0; i < tascam->playback_urb_iso_packets; i++) {
|
for (i = 0; i < tascam->playback_urb_iso_packets; i++) {
|
||||||
unsigned int frames_for_packet;
|
unsigned int frames_for_packet;
|
||||||
size_t bytes_for_packet;
|
size_t bytes_for_packet;
|
||||||
|
|
@ -916,14 +800,8 @@ resubmit:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*============================================================================*/
|
|
||||||
/* --- Device Configuration and Probing --- */
|
|
||||||
/*============================================================================*/
|
|
||||||
|
|
||||||
// This function sends the precise sequence of control messages required to
|
// This function sends the precise sequence of control messages required to
|
||||||
// initialize the device and set a new sample rate. This sequence was
|
// initialize the device and set a new sample rate.
|
||||||
// determined by reverse-engineering the official drivers.
|
|
||||||
static int us144mkii_configure_device_for_rate(struct tascam_card *tascam, int rate)
|
static int us144mkii_configure_device_for_rate(struct tascam_card *tascam, int rate)
|
||||||
{
|
{
|
||||||
struct usb_device *dev = tascam->dev;
|
struct usb_device *dev = tascam->dev;
|
||||||
|
|
@ -955,7 +833,6 @@ static int us144mkii_configure_device_for_rate(struct tascam_card *tascam, int r
|
||||||
|
|
||||||
memcpy(rate_payload_buf, current_payload_src, 3);
|
memcpy(rate_payload_buf, current_payload_src, 3);
|
||||||
|
|
||||||
// --- Begin Control Message Sequence ---
|
|
||||||
dev_info(&dev->dev, "Configuring device for %d Hz\n", rate);
|
dev_info(&dev->dev, "Configuring device for %d Hz\n", rate);
|
||||||
|
|
||||||
// 1. Set Initial Mode
|
// 1. Set Initial Mode
|
||||||
|
|
@ -997,8 +874,6 @@ static int us144mkii_configure_device_for_rate(struct tascam_card *tascam, int r
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto cleanup_buf;
|
goto cleanup_buf;
|
||||||
|
|
||||||
// --- End Control Message Sequence ---
|
|
||||||
|
|
||||||
cleanup_buf:
|
cleanup_buf:
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
dev_err(&dev->dev, "Device configuration failed at rate %d with error %d\n", rate, err);
|
dev_err(&dev->dev, "Device configuration failed at rate %d with error %d\n", rate, err);
|
||||||
|
|
@ -1070,8 +945,8 @@ static int tascam_probe(struct usb_interface *intf, const struct usb_device_id *
|
||||||
usb_set_intfdata(intf, tascam);
|
usb_set_intfdata(intf, tascam);
|
||||||
spin_lock_init(&tascam->lock);
|
spin_lock_init(&tascam->lock);
|
||||||
atomic_set(&tascam->playback_active, 0);
|
atomic_set(&tascam->playback_active, 0);
|
||||||
tascam->current_rate = 0; // Initialize to 0 to force first configuration
|
tascam->current_rate = 0;
|
||||||
tascam->playback_urb_iso_packets = 0; // Initialize dynamic packet count
|
tascam->playback_urb_iso_packets = 0;
|
||||||
|
|
||||||
strscpy(card->driver, DRIVER_NAME, sizeof(card->driver));
|
strscpy(card->driver, DRIVER_NAME, sizeof(card->driver));
|
||||||
strscpy(card->shortname, "TASCAM US-144MKII", sizeof(card->shortname));
|
strscpy(card->shortname, "TASCAM US-144MKII", sizeof(card->shortname));
|
||||||
|
|
@ -1107,7 +982,7 @@ static int tascam_probe(struct usb_interface *intf, const struct usb_device_id *
|
||||||
goto release_iface1_and_free_card;
|
goto release_iface1_and_free_card;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform the initial handshake read, as per the reverse-eng report.
|
// Perform the initial handshake read.
|
||||||
handshake_buf = kmalloc(1, GFP_KERNEL);
|
handshake_buf = kmalloc(1, GFP_KERNEL);
|
||||||
if (!handshake_buf) {
|
if (!handshake_buf) {
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
|
|
@ -1176,11 +1051,6 @@ static void tascam_disconnect(struct usb_interface *intf)
|
||||||
snd_card_free_when_closed(tascam->card);
|
snd_card_free_when_closed(tascam->card);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*============================================================================*/
|
|
||||||
/* --- USB Driver Registration --- */
|
|
||||||
/*============================================================================*/
|
|
||||||
|
|
||||||
static const struct usb_device_id tascam_id_table[] = {
|
static const struct usb_device_id tascam_id_table[] = {
|
||||||
{ USB_DEVICE(TASCAM_VID, TASCAM_PID) },
|
{ USB_DEVICE(TASCAM_VID, TASCAM_PID) },
|
||||||
{ }
|
{ }
|
||||||
|
|
|
||||||
BIN
us144mkii.ko
BIN
us144mkii.ko
Binary file not shown.
Loading…
Reference in New Issue