Analysis Report & Driver Development Specification for TASCAM US-144 MKII (48kHz Playback) Document Version: 8.0 Date: 2025-06-25 Subject: This document provides a complete specification for the 48kHz playback protocol of the TASCAM US-144 MKII, based on reverse-engineering of the official Windows driver. It is intended as a technical reference for implementing a functional Linux driver. 1. Core Architecture (48kHz) Playback is achieved via a host-paced asynchronous isochronous feedback loop. Audio Data (OUT): Endpoint 0x02. The host sends continuous audio data. Clock Feedback (IN): Endpoint 0x81. The device sends its master clock rate to the host, allowing the driver to correct for drift. 2. Data Stream Format (48kHz) Audio Frame: A single sample for all four channels, formatted as 24-bit Little Endian PCM. Total Frame Size: 12 bytes (4 channels * 3 bytes/sample). Host Driver Responsibility: The driver must always populate all 4 channels. For standard stereo playback, channels 1 & 2 contain the audio data, and channels 3 & 4 must be padded with digital silence (zeros). 3. Playback OUT Stream (Endpoint 0x02) URB Structure: Analysis of all latency settings shows that the playback URB structure is constant and independent of the latency setting. Packets per URB: 40 Packet Size: 72 bytes Frames per Packet: 6 (72 bytes / 12 bytes/frame) Total Frames per URB: 240 (40 packets * 6 frames) Total Audio Data per URB: 2880 bytes (240 frames * 12 bytes/frame) Nominal Time per URB: 5.0 ms (240 frames / 48000 Hz) 4. Feedback IN Stream (Endpoint 0x81) This is the core of the latency control and clock synchronization. The "Latency" setting in the control panel directly maps to the number of feedback packets requested per URB, which in turn dictates the feedback interval. Feedback Packet Format: 3 bytes. For 48kHz, the nominal value is 0x303030. The first byte (0x30 = 48) represents the number of audio frames the device consumes per 125µs USB microframe, which translates to a nominal 48kHz rate. Latency Setting to URB Mapping: | Setting Name | App Buffer | Feedback URB Size (Packets) | Feedback Interval | | :--- | :--- | :--- | :--- | | Lowest | 48 smp | 1 packet | ~1 ms | | Low | 64 smp | 1 packet | ~1 ms | | Normal | 128 smp| 2 packets | ~2 ms | | High | 256 smp| 5 packets | ~5 ms | | Highest | 512 smp| 5 packets | ~5 ms | 5. Driver Implementation Logic ("Packet Fixing") A functional driver must implement the following logic for Normal Latency: Audio OUT URBs (Playback): Continuously prepare and submit URBs to EP 0x02. Each URB must contain exactly 40 isochronous packet descriptors. Each packet must be exactly 72 bytes long. Feedback IN URBs (Clocking): Continuously prepare and submit URBs to EP 0x81. For Normal Latency, each URB must request 2 isochronous packets. Each packet descriptor will be for 3 bytes. These URBs will complete approximately every 2ms. Clock Drift Correction (The "Fixing" Mechanism): The driver must maintain a high-precision accumulator (e.g., a 16.16 fixed-point number) representing the number of frames to send. On Feedback URB completion: Read the feedback value from the received packet (e.g., 0x30). Add this value (adjusted for the feedback interval) to the accumulator. For Normal latency (2ms interval), this means adding 48 * 2 = 96 frames to the accumulator's integer part. On Playback URB completion: Consult the accumulator to determine the exact number of frames to send in the next URB (N_frames). This will be the integer part of the accumulator. Subtract N_frames from the accumulator, leaving the fractional part for the next calculation. Copy N_frames of audio from the ALSA ring buffer into the new URB's transfer buffer. The total audio data in a playback URB is fixed at 240 frames (2880 bytes). If N_frames is less than 240, the remaining (240 - N_frames) of the URB must be padded with digital silence (zeros). If N_frames is greater than 240, this implies the host is running slow and needs to "stuff" an extra frame. This is handled by copying N_frames and ensuring the total data size still fits within the URB buffer (which it should, as N_frames will only differ from 240 by a small amount). This process ensures that the average rate of audio data sent perfectly matches the device's master clock, preventing buffer over/underruns. is report summarizes the critical findings from the macOS driver reverse engineering, focusing on the playback data path and the "Packet Fixing" synchronization logic. It's designed to be a standalone reference for implementing or debugging the Linux driver. Device Overview: Model: TASCAM US-144 MKII USB Mode: USB 2.0 High Speed (VID: 0x0644, PID: 0x8020) Supported Playback Rate: 48kHz (primary focus) Audio Format: 24-bit Little Endian PCM Channels: 4 (Stereo input duplicated to 4 channels) 1. USB Device Configuration & Initialization: Interfaces: Interface 0: Primary Audio/MIDI. Select Alternate Setting 1. Interface 1: Additional Audio/MIDI. Select Alternate Setting 1. Endpoints: Playback (OUT): Endpoint 0x02 (Isochronous OUT) Feedback (IN): Endpoint 0x81 (Isochronous IN) Capture (IN): Endpoint 0x86 (Bulk IN) - Not covered in detail for playback. MIDI (IN/OUT): Endpoints 0x83 (Bulk IN), 0x04 (Bulk OUT) - Not covered in detail. Initial Control Messages (from PGKernelDeviceTascamUSB2MKII::configureDevice): After usb_set_interface calls, a sequence of usb_control_msg (vendor-specific and UAC) is sent. Vendor Requests: VENDOR_REQ_MODE_CONTROL (0x49): bmRequestType=0x40 (Host to Device, Vendor, Device) wValue=0x0010, wIndex=0x0000, wLength=0 (Set Initial Mode) wValue=0x0030, wIndex=0x0000, wLength=0 (Enable Streaming) VENDOR_REQ_REGISTER_WRITE (0x41): bmRequestType=0x40 (Host to Device, Vendor, Device) wValue=0x0d04, wIndex=0x0101, wLength=0 wValue=0x0e00, wIndex=0x0101, wLength=0 wValue=0x0f00, wIndex=0x0101, wLength=0 wValue=0x110b, wIndex=0x0101, wLength=0 wValue=0x1002 (for 48kHz), wIndex=0x0101, wLength=0 (Rate-dependent register write) UAC Requests: UAC_SET_CUR (0x01) for UAC_SAMPLING_FREQ_CONTROL (0x0100): bmRequestType=0x22 (Host to Device, Class, Endpoint) wIndex=EP_CAPTURE_DATA (0x86), wLength=3, data=3-byte_rate_payload (e.g., 0x80, 0xbb, 0x00 for 48kHz) wIndex=EP_AUDIO_OUT (0x02), wLength=3, data=3-byte_rate_payload (e.g., 0x80, 0xbb, 0x00 for 48kHz) Action: Replicate these usb_control_msg calls in the Linux driver's us144mkii_configure_device_for_rate function. 2. Playback (OUT) Stream (Endpoint 0x02): URB Structure: number_of_packets: 40 iso_frame_desc[i].length: 72 bytes for each packet. iso_frame_desc[i].offset: j * 72 (where j is packet index). transfer_buffer_length: 40 * 72 = 2880 bytes. Audio Data Format (pcmTo24LSB equivalent): Input: 32-bit signed interleaved PCM (from ALSA). Output: 24-bit Little Endian PCM, 4 channels. Channel Mapping: Stereo input (L, R) is duplicated to 4 channels (L, R, L, R). dest[0-2] = src_L (24-bit LSB) dest[3-5] = src_R (24-bit LSB) dest[6-8] = src_L (24-bit LSB) dest[9-11] = src_R (24-bit LSB) Action: Implement a convert_and_copy_audio function that performs this conversion and copy. URB Completion Handler (playback_urb_complete): After checking URB status, determine N_frames (frames to send in this URB) using the tascam_get_frames_to_send logic (see below). Zero out the entire URB transfer_buffer to handle padding. Copy N_frames of audio data from the ALSA ring buffer into the URB's transfer_buffer using convert_and_copy_audio. Handle buffer wrap-around. Update tascam->playback_frames_sent_nominal by N_frames. Resubmit the URB. 3. Feedback (IN) Stream (Endpoint 0x81): URB Structure (for "Normal Latency"): number_of_packets: 2 iso_frame_desc[i].length: 3 bytes for each packet. iso_frame_desc[i].offset: j * 3 (where j is packet index). transfer_buffer_length: 2 * 3 = 6 bytes. Feedback Packet Format: 3 bytes. The first byte contains the feedback value (e.g., 0x30 for 48 frames/ms). URB Completion Handler (feedback_urb_complete): After checking URB status, iterate through each received packet. For each packet with actual_length == 3 and status == 0: Extract feedback_value = feedback_data[0]. Update the Feedback Accumulator (PGFramePatternObserver equivalent): Calculate pattern_idx = feedback_value - 46. If pattern_idx is valid (0-4), select the corresponding feedback_patterns_lookup table. Copy the 8 entries from the selected pattern table into tascam->feedback_accumulator_pattern starting at tascam->feedback_accumulator_idx * 8. Advance tascam->feedback_accumulator_idx by 1 (wrapping around at 16, as 128 entries / 8 entries/pattern = 16 slots). Set tascam->feedback_synced = true. If feedback_value is invalid or packet has error, set tascam->feedback_synced = false. Update tascam->playback_frames_consumed_by_device: If tascam->feedback_synced is true, sum the 16 entries (2 packets * 8 microframes/packet) from the feedback_accumulator_pattern that correspond to this interval. Add this sum to tascam->playback_frames_consumed_by_device. If not synced, add a nominal 96 frames (2 packets * 48 frames/ms) to avoid stalling. Resubmit the URB. 4. Clock Drift Correction ("Packet Fixing") Logic: Feedback Accumulator (tascam_card::feedback_accumulator_pattern): An array of 128 unsigned int values. Initialized to 6 (nominal frames per microframe for 48kHz) in tascam_probe. Updated by feedback_urb_complete by copying 8 entries from a selected pattern table. Pattern Tables (for 48kHz): These are the 8-entry arrays of frames per microframe. feedback_value = 46 (0x2E): {5, 6, 6, 6, 5, 6, 6, 6} (sum 46) feedback_value = 47 (0x2F): {5, 6, 6, 6, 6, 6, 6, 6} (sum 47) feedback_value = 48 (0x30): {6, 6, 6, 6, 6, 6, 6, 6} (sum 48) - Nominal feedback_value = 49 (0x31): {7, 6, 6, 6, 6, 6, 6, 6} (sum 49) feedback_value = 50 (0x32): {7, 6, 6, 6, 7, 6, 6, 6} (sum 50) tascam_get_frames_to_send (adaptation of rtsGetMinAvailableFrames): Input: tascam_card pointer, nominal_frames_per_urb (240 for 48kHz). Logic: Calculates frames_difference = tascam->playback_frames_consumed_by_device - tascam->playback_frames_sent_nominal. If tascam->feedback_synced is true, it adjusts nominal_frames_per_urb by +1 if frames_difference > 1 or -1 if frames_difference < -1. Clamps the result to nominal_frames_per_urb +/- 2. Output: Returns the calculated N_frames for the next playback URB. tascam_pcm_pointer: Returns tascam->playback_frames_consumed_by_device % runtime->buffer_size. This reflects the device's actual position.