initial midi(not functional)

This commit is contained in:
serifpersia 2025-07-19 20:18:11 +02:00
parent fe3d9beea6
commit 7284fb4c42
3 changed files with 2972 additions and 23 deletions

266
midi_test.c Normal file
View File

@ -0,0 +1,266 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libusb-1.0/libusb.h>
#include <stdbool.h>
#include <signal.h>
#include <stdint.h>
#include <unistd.h>
#include <time.h>
#define NOTE_INTERVAL_MS 100
#define NUM_CHORD_NOTES 3
#define TASCAM_VID 0x0644
#define TASCAM_PID 0x8020
#define EP_MIDI_OUT 0x04
#define EP_MIDI_IN 0x83
#define EP_AUDIO_OUT 0x02
#define EP_CAPTURE_DATA 0x86
#define RT_H2D_CLASS_EP 0x22
#define RT_D2H_VENDOR_DEV 0xc0
#define RT_H2D_VENDOR_DEV 0x40
#define UAC_SET_CUR 0x01
#define UAC_SAMPLING_FREQ_CONTROL 0x0100
#define VENDOR_REQ_REGISTER_WRITE 65
#define VENDOR_REQ_MODE_CONTROL 73
#define USB_TIMEOUT 1000
#define NUM_AUDIO_TRANSFERS 8
#define ISO_AUDIO_PACKETS_PER_TRANSFER 8
#define BYTES_PER_SAMPLE 3
#define DEVICE_CHANNELS 4
#define DEVICE_FRAME_SIZE (DEVICE_CHANNELS * BYTES_PER_SAMPLE)
#define NUM_MIDI_IN_TRANSFERS 4
#define MIDI_IN_BUF_SIZE 64
static volatile bool is_running = true;
long long total_bytes_sent = 0;
int perform_device_init(libusb_device_handle *handle);
static void LIBUSB_CALL iso_audio_callback(struct libusb_transfer *transfer);
static void LIBUSB_CALL midi_in_callback(struct libusb_transfer *transfer);
void log_raw_midi_in(uint8_t *buf, int len);
void sigint_handler(int signum) {
if (is_running) {
printf("\nCtrl+C detected, shutting down...\n");
is_running = false;
}
}
void send_tascam_midi_message(libusb_device_handle *handle, uint8_t *midi_msg, long long *total_bytes_sent) {
const size_t transfer_size = 9;
uint8_t packet1[transfer_size];
uint8_t packet2[transfer_size];
int r, actual_length;
// Packet 1: Header and first MIDI byte
memset(packet1, 0xfd, transfer_size);
packet1[0] = (0 << 4) | (midi_msg[0] >> 4); // Cable 0, CIN
packet1[1] = midi_msg[0];
packet1[8] = 0x00;
// Packet 2: Second and third MIDI bytes
memset(packet2, 0xfd, transfer_size);
packet2[0] = midi_msg[1];
packet2[1] = midi_msg[2];
packet2[8] = 0x00;
r = libusb_bulk_transfer(handle, EP_MIDI_OUT, packet1, transfer_size, &actual_length, USB_TIMEOUT);
if (r != 0) {
fprintf(stderr, "MIDI transfer error on packet 1: %s\n", libusb_error_name(r));
is_running = false;
return;
}
*total_bytes_sent += actual_length;
r = libusb_bulk_transfer(handle, EP_MIDI_OUT, packet2, transfer_size, &actual_length, USB_TIMEOUT);
if (r != 0) {
fprintf(stderr, "MIDI transfer error on packet 2: %s\n", libusb_error_name(r));
is_running = false;
return;
}
*total_bytes_sent += actual_length;
}
int main(int argc, char *argv[]) {
libusb_device_handle *handle = NULL;
struct libusb_transfer *audio_transfers[NUM_AUDIO_TRANSFERS] = {0};
struct libusb_transfer *midi_in_transfers[NUM_MIDI_IN_TRANSFERS] = {0};
bool kernel_driver_was_active[2] = {false, false};
int r = 0;
printf("--- TASCAM US-144MKII MIDI Loopback Test (Two-Packet) ---\n");
printf("Please connect a MIDI cable from MIDI OUT to MIDI IN.\n");
printf("Sending a %d-note chord every %d ms. Press Ctrl+C to stop.\n", NUM_CHORD_NOTES, NOTE_INTERVAL_MS);
srand(time(NULL));
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 driver for iface %d: %s\n", i, libusb_error_name(r));
r = 1; goto cleanup;
}
}
}
if (perform_device_init(handle) != 0) { r = 1; goto cleanup; }
const int nominal_frames_per_packet = 44100 / 8000;
const int audio_packet_size = nominal_frames_per_packet * DEVICE_FRAME_SIZE;
const int audio_transfer_size = audio_packet_size * ISO_AUDIO_PACKETS_PER_TRANSFER;
printf("Starting silent audio stream...\n");
for (int i = 0; i < NUM_AUDIO_TRANSFERS; i++) {
audio_transfers[i] = libusb_alloc_transfer(ISO_AUDIO_PACKETS_PER_TRANSFER);
unsigned char *buf = calloc(1, audio_transfer_size);
if (!buf) { fprintf(stderr, "Audio buffer alloc failed\n"); r=1; goto cleanup; }
libusb_fill_iso_transfer(audio_transfers[i], handle, EP_AUDIO_OUT, buf, audio_transfer_size, ISO_AUDIO_PACKETS_PER_TRANSFER, iso_audio_callback, NULL, USB_TIMEOUT);
libusb_set_iso_packet_lengths(audio_transfers[i], audio_packet_size);
if (libusb_submit_transfer(audio_transfers[i]) < 0) {
fprintf(stderr, "Failed to submit initial audio transfer\n"); r=1; goto cleanup;
}
}
printf("Starting MIDI IN listener...\n");
for (int i = 0; i < NUM_MIDI_IN_TRANSFERS; i++) {
midi_in_transfers[i] = libusb_alloc_transfer(0);
unsigned char* buf = malloc(MIDI_IN_BUF_SIZE);
if (!buf) { fprintf(stderr, "MIDI IN buffer alloc failed\n"); r=1; goto cleanup; }
libusb_fill_bulk_transfer(midi_in_transfers[i], handle, EP_MIDI_IN, buf, MIDI_IN_BUF_SIZE, midi_in_callback, NULL, 0);
if (libusb_submit_transfer(midi_in_transfers[i]) < 0) {
fprintf(stderr, "Failed to submit initial MIDI IN transfer\n"); r=1; goto cleanup;
}
}
printf("\n--- Starting MIDI loop...---\n");
enum { STATE_SEND_ON, STATE_SEND_OFF } midi_send_state = STATE_SEND_ON;
struct timespec last_action_time;
clock_gettime(CLOCK_MONOTONIC, &last_action_time);
while (is_running) {
struct timeval tv = {0, 1000};
libusb_handle_events_timeout(NULL, &tv);
struct timespec current_time;
clock_gettime(CLOCK_MONOTONIC, &current_time);
double elapsed_ms = (current_time.tv_sec - last_action_time.tv_sec) * 1000.0;
elapsed_ms += (current_time.tv_nsec - last_action_time.tv_nsec) / 1000000.0;
if (elapsed_ms < NOTE_INTERVAL_MS) {
continue;
}
if (midi_send_state == STATE_SEND_ON) {
printf("--- SENDING NOTE ON ---\n");
uint8_t note = 60;
uint8_t velocity = (rand() % 123) + 5;
uint8_t midi_msg[] = {0x90, note, velocity};
send_tascam_midi_message(handle, midi_msg, &total_bytes_sent);
} else { // STATE_SEND_OFF
printf("--- SENDING NOTE OFF ---\n\n");
uint8_t note = 60;
uint8_t midi_msg[] = {0x80, note, 0};
send_tascam_midi_message(handle, midi_msg, &total_bytes_sent);
}
midi_send_state = (midi_send_state == STATE_SEND_ON) ? STATE_SEND_OFF : STATE_SEND_ON;
clock_gettime(CLOCK_MONOTONIC, &last_action_time);
}
cleanup:
for(int i=0; i < NUM_AUDIO_TRANSFERS; i++) if (audio_transfers[i]) libusb_cancel_transfer(audio_transfers[i]);
for(int i=0; i < NUM_MIDI_IN_TRANSFERS; i++) if (midi_in_transfers[i]) libusb_cancel_transfer(midi_in_transfers[i]);
struct timeval final_tv = {0, 200000};
libusb_handle_events_timeout_completed(NULL, &final_tv, NULL);
if (handle) {
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_AUDIO_TRANSFERS; i++) if(audio_transfers[i]) { if (audio_transfers[i]->buffer) free(audio_transfers[i]->buffer); libusb_free_transfer(audio_transfers[i]); }
for (int i=0; i<NUM_MIDI_IN_TRANSFERS; i++) if(midi_in_transfers[i]) { if (midi_in_transfers[i]->buffer) free(midi_in_transfers[i]->buffer); libusb_free_transfer(midi_in_transfers[i]); }
libusb_exit(NULL);
printf("\n\n------ FINAL REPORT ------\n");
printf("Total Raw MIDI Bytes Sent: %lld\n", total_bytes_sent);
printf("--------------------------\n");
printf("Cleanup complete.\n");
return r;
}
void log_raw_midi_in(uint8_t *buf, int len) {
printf("RECV RAW USB DATA (%d bytes):", len);
for(int i=0; i<len; i++) printf(" %02x", buf[i]);
printf("\n");
}
static void LIBUSB_CALL midi_in_callback(struct libusb_transfer *transfer) {
if (transfer->status == LIBUSB_TRANSFER_COMPLETED) {
if (transfer->actual_length > 0) {
log_raw_midi_in(transfer->buffer, transfer->actual_length);
}
if (is_running) {
libusb_submit_transfer(transfer);
}
} else if (transfer->status != LIBUSB_TRANSFER_CANCELLED) {
fprintf(stderr, "MIDI IN callback error: %s\n", libusb_error_name(transfer->status));
is_running = false;
}
}
static void LIBUSB_CALL iso_audio_callback(struct libusb_transfer *transfer) {
if (transfer->status == LIBUSB_TRANSFER_COMPLETED) { if (is_running) libusb_submit_transfer(transfer);
} else if (transfer->status != LIBUSB_TRANSFER_CANCELLED) {
fprintf(stderr, "Audio callback error: %s\n", libusb_error_name(transfer->status));
is_running = false;
}
}
int perform_device_init(libusb_device_handle *handle) {
const unsigned char rate_data_44100[] = {0x44, 0xac, 0x00};
uint16_t rate_vendor_wValue = 0x1000;
unsigned char buf[1]; int r;
char log_msg[128];
printf("\n--- STARTING DEVICE INITIALIZATION (Verified Sequence) ---\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\n", desc); }
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++) {
snprintf(log_msg, sizeof(log_msg), "Claim Interface %d", i); CHECK(log_msg, libusb_claim_interface(handle, i));
snprintf(log_msg, sizeof(log_msg), "Set Alt Setting on Intf %d", i); CHECK(log_msg, libusb_set_interface_alt_setting(handle, i, 1));
}
printf("\n-- Step 2: Handshake --\n");
CHECK("Vendor Handshake Read", 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("Vendor Set Mode to 0x0010", 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 (Prerequisite for MIDI) --\n");
CHECK("UAC Set Rate on Capture EP", libusb_control_transfer(handle, RT_H2D_CLASS_EP, UAC_SET_CUR, UAC_SAMPLING_FREQ_CONTROL, EP_CAPTURE_DATA, (unsigned char*)rate_data_44100, 3, USB_TIMEOUT));
CHECK("UAC Set Rate on Playback EP", libusb_control_transfer(handle, RT_H2D_CLASS_EP, UAC_SET_CUR, UAC_SAMPLING_FREQ_CONTROL, EP_AUDIO_OUT, (unsigned char*)rate_data_44100, 3, USB_TIMEOUT));
printf("\n-- Step 5: Configure Internal Registers --\n");
CHECK("Vendor Register Write (0x0d04)", libusb_control_transfer(handle, RT_H2D_VENDOR_DEV, VENDOR_REQ_REGISTER_WRITE, 0x0d04, 0x0101, NULL, 0, USB_TIMEOUT));
CHECK("Vendor Register Write (0x0e00)", libusb_control_transfer(handle, RT_H2D_VENDOR_DEV, VENDOR_REQ_REGISTER_WRITE, 0x0e00, 0x0101, NULL, 0, USB_TIMEOUT));
CHECK("Vendor Register Write (0x0f00)", libusb_control_transfer(handle, RT_H2D_VENDOR_DEV, VENDOR_REQ_REGISTER_WRITE, 0x0f00, 0x0101, NULL, 0, USB_TIMEOUT));
CHECK("Vendor Register Write (Rate)", libusb_control_transfer(handle, RT_H2D_VENDOR_DEV, VENDOR_REQ_REGISTER_WRITE, rate_vendor_wValue, 0x0101, NULL, 0, USB_TIMEOUT));
CHECK("Vendor Register Write (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("Vendor Set Mode to 0x0030 (Enable Streaming)", libusb_control_transfer(handle, RT_H2D_VENDOR_DEV, VENDOR_REQ_MODE_CONTROL, 0x0030, 0x0000, NULL, 0, USB_TIMEOUT));
printf("\n--- INITIALIZATION COMPLETE ---\n");
return 0;
}

View File

@ -2,21 +2,28 @@
// Copyright (c) 2025 serifpersia <ramiserifpersia@gmail.com> // Copyright (c) 2025 serifpersia <ramiserifpersia@gmail.com>
/* /*
* ALSA Driver for TASCAM US-144MKII Audio Interface * ALSA Driver for TASCAM US-144MKII Audio Interface
*
* This version includes a robust, state-machine-based MIDI implementation
* to fix packet dropouts and a hardware activation sequence to enable MIDI input.
*/ */
#include <linux/usb.h> #include <linux/usb.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/slab.h>
#include <linux/bitops.h>
#include <sound/core.h> #include <sound/core.h>
#include <sound/pcm.h> #include <sound/pcm.h>
#include <sound/rawmidi.h>
#include <sound/initval.h> #include <sound/initval.h>
#include <sound/control.h> #include <sound/control.h>
#include <linux/printk.h>
MODULE_AUTHOR("serifpersia <ramiserifpersia@gmail.com>"); MODULE_AUTHOR("serifpersia <ramiserifpersia@gmail.com>");
MODULE_DESCRIPTION("ALSA Driver for TASCAM US-144MKII"); MODULE_DESCRIPTION("ALSA Driver for TASCAM US-144MKII");
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");
#define DRIVER_NAME "us144mkii" #define DRIVER_NAME "us144mkii"
#define DRIVER_VERSION "1.5" #define DRIVER_VERSION "2.5" // Version bump for MIDI fixes
/* --- Module Parameters --- */ /* --- Module Parameters --- */
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
@ -68,6 +75,11 @@ static int dev_idx;
#define NUM_CAPTURE_URBS 8 #define NUM_CAPTURE_URBS 8
#define CAPTURE_URB_SIZE 512 #define CAPTURE_URB_SIZE 512
#define CAPTURE_RING_BUFFER_SIZE (CAPTURE_URB_SIZE * NUM_CAPTURE_URBS * 4) #define CAPTURE_RING_BUFFER_SIZE (CAPTURE_URB_SIZE * NUM_CAPTURE_URBS * 4)
#define NUM_MIDI_IN_URBS 4
#define MIDI_IN_BUF_SIZE 64
#define MIDI_OUT_BUF_SIZE 64 // Buffer in URB, must be >= 9
#define NUM_MIDI_OUT_URBS 8 // Increased for better throughput
#define MIDI_OUT_PACKET_QUEUE_SIZE 32 // Intermediate queue for 9-byte packets
#define USB_CTRL_TIMEOUT_MS 1000 #define USB_CTRL_TIMEOUT_MS 1000
/* --- Audio Format Configuration --- */ /* --- Audio Format Configuration --- */
@ -82,6 +94,17 @@ static int dev_idx;
#define FRAMES_PER_DECODE_BLOCK 8 #define FRAMES_PER_DECODE_BLOCK 8
#define RAW_BYTES_PER_DECODE_BLOCK 512 #define RAW_BYTES_PER_DECODE_BLOCK 512
/* --- State machine for the MIDI output parser --- */
enum tascam_midi_out_state {
MIDI_OUT_STATE_UNKNOWN, /* Waiting for a status byte */
MIDI_OUT_STATE_1PARAM, /* Waiting for 1 data byte (e.g., for Program Change) */
MIDI_OUT_STATE_2PARAM_1, /* Waiting for the 1st of 2 data bytes (e.g., for Note On) */
MIDI_OUT_STATE_2PARAM_2, /* Waiting for the 2nd of 2 data bytes */
MIDI_OUT_STATE_SYSEX_0, /* In SysEx, waiting for 1st data byte of a 3-byte chunk */
MIDI_OUT_STATE_SYSEX_1, /* In SysEx, waiting for 2nd data byte */
MIDI_OUT_STATE_SYSEX_2, /* In SysEx, waiting for 3rd data byte */
};
/* --- Main Driver Data Structure --- */ /* --- Main Driver Data Structure --- */
struct tascam_card { struct tascam_card {
struct usb_device *dev; struct usb_device *dev;
@ -89,6 +112,7 @@ struct tascam_card {
struct usb_interface *iface1; struct usb_interface *iface1;
struct snd_card *card; struct snd_card *card;
struct snd_pcm *pcm; struct snd_pcm *pcm;
struct snd_rawmidi *rmidi;
/* Playback stream */ /* Playback stream */
struct snd_pcm_substream *playback_substream; struct snd_pcm_substream *playback_substream;
@ -118,6 +142,42 @@ struct tascam_card {
s32 *capture_routing_buffer; s32 *capture_routing_buffer;
struct work_struct capture_work; struct work_struct capture_work;
/* MIDI streams */
struct snd_rawmidi_substream *midi_in_substream;
struct snd_rawmidi_substream *midi_out_substream;
struct urb *midi_in_urbs[NUM_MIDI_IN_URBS];
atomic_t midi_in_active;
struct urb *midi_out_urbs[NUM_MIDI_OUT_URBS];
atomic_t midi_out_active;
struct work_struct midi_out_work;
unsigned long midi_out_urbs_in_flight; /* bitmask */
spinlock_t midi_out_lock;
/* --- NEW: MIDI Output Packet Queue --- */
/**
* @midi_out_packet_queue: Ring buffer for formatted 9-byte MIDI packets.
* The state machine places formatted packets here, and the work handler
* dequeues them to send in individual URBs. This is critical because
* the device expects one 9-byte bulk transfer per packet.
*/
u8 midi_out_packet_queue[MIDI_OUT_PACKET_QUEUE_SIZE][9];
int midi_out_queue_read_ptr;
volatile int midi_out_queue_write_ptr;
/* State machine for MIDI output stream */
struct {
enum tascam_midi_out_state state;
u8 data[2]; /* Buffer for holding partial MIDI messages */
u8 running_status; /* Currently active running status */
} midi_out_state;
/* State machine for MIDI input parser */
enum {
MIDI_IN_STATE_WAIT_PACKET_1,
MIDI_IN_STATE_WAIT_PACKET_2
} midi_in_state;
u8 midi_in_b0; /* Status byte from the first packet */
/* Shared state & Routing Matrix */ /* Shared state & Routing Matrix */
spinlock_t lock; spinlock_t lock;
atomic_t active_urbs; atomic_t active_urbs;
@ -146,6 +206,9 @@ static void playback_urb_complete(struct urb *urb);
static void feedback_urb_complete(struct urb *urb); static void feedback_urb_complete(struct urb *urb);
static void capture_urb_complete(struct urb *urb); static void capture_urb_complete(struct urb *urb);
static void tascam_capture_work_handler(struct work_struct *work); static void tascam_capture_work_handler(struct work_struct *work);
static void tascam_midi_in_urb_complete(struct urb *urb);
static void tascam_midi_out_urb_complete(struct urb *urb);
static void tascam_midi_out_work_handler(struct work_struct *work);
static 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);
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);
@ -435,8 +498,9 @@ static const struct snd_pcm_hardware tascam_pcm_hw = {
* tascam_free_urbs - Free all allocated URBs and associated buffers. * tascam_free_urbs - Free all allocated URBs and associated buffers.
* @tascam: the tascam_card instance. * @tascam: the tascam_card instance.
* *
* This function kills, unlinks, and frees all playback, feedback, and capture * This function kills, unlinks, and frees all playback, feedback, capture,
* URBs, along with their transfer buffers and the capture ring/decode buffers. * and MIDI URBs, along with their transfer buffers and the capture
* ring/decode buffers.
*/ */
static void tascam_free_urbs(struct tascam_card *tascam) static void tascam_free_urbs(struct tascam_card *tascam)
{ {
@ -475,11 +539,39 @@ static void tascam_free_urbs(struct tascam_card *tascam)
} }
} }
/* MIDI URB and buffer freeing */
for (i = 0; i < NUM_MIDI_IN_URBS; i++) {
if (tascam->midi_in_urbs[i]) {
usb_kill_urb(tascam->midi_in_urbs[i]);
usb_free_coherent(tascam->dev, MIDI_IN_BUF_SIZE,
tascam->midi_in_urbs[i]->transfer_buffer,
tascam->midi_in_urbs[i]->transfer_dma);
usb_free_urb(tascam->midi_in_urbs[i]);
tascam->midi_in_urbs[i] = NULL;
}
}
for (i = 0; i < NUM_MIDI_OUT_URBS; i++) {
if (tascam->midi_out_urbs[i]) {
usb_kill_urb(tascam->midi_out_urbs[i]);
usb_free_coherent(tascam->dev, MIDI_OUT_BUF_SIZE,
tascam->midi_out_urbs[i]->transfer_buffer,
tascam->midi_out_urbs[i]->transfer_dma);
usb_free_urb(tascam->midi_out_urbs[i]);
tascam->midi_out_urbs[i] = NULL;
}
}
kfree(tascam->playback_routing_buffer); kfree(tascam->playback_routing_buffer);
tascam->playback_routing_buffer = NULL; tascam->playback_routing_buffer = NULL;
kfree(tascam->capture_routing_buffer); kfree(tascam->capture_routing_buffer);
tascam->capture_routing_buffer = NULL; tascam->capture_routing_buffer = NULL;
kfree(tascam->capture_decode_dst_block); kfree(tascam->capture_decode_dst_block);
tascam->capture_decode_dst_block = NULL;
kfree(tascam->capture_decode_raw_block);
tascam->capture_decode_raw_block = NULL;
kfree(tascam->capture_ring_buffer);
tascam->capture_ring_buffer = NULL;
} }
/** /**
@ -487,7 +579,7 @@ static void tascam_free_urbs(struct tascam_card *tascam)
* @tascam: the tascam_card instance. * @tascam: the tascam_card instance.
* *
* This function allocates and initializes all URBs for playback, feedback, * This function allocates and initializes all URBs for playback, feedback,
* and capture, as well as the necessary buffers for capture data processing. * capture, and MIDI, as well as the necessary buffers for data processing.
* *
* Return: 0 on success, or a negative error code on failure. * Return: 0 on success, or a negative error code on failure.
*/ */
@ -560,6 +652,38 @@ static int tascam_alloc_urbs(struct tascam_card *tascam)
c_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; c_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
} }
/* MIDI URB and buffer allocation */
for (i = 0; i < NUM_MIDI_IN_URBS; i++) {
struct urb *m_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!m_urb)
goto error;
tascam->midi_in_urbs[i] = m_urb;
m_urb->transfer_buffer = usb_alloc_coherent(tascam->dev,
MIDI_IN_BUF_SIZE, GFP_KERNEL, &m_urb->transfer_dma);
if (!m_urb->transfer_buffer)
goto error;
usb_fill_bulk_urb(m_urb, tascam->dev, usb_rcvbulkpipe(tascam->dev, EP_MIDI_IN),
m_urb->transfer_buffer, MIDI_IN_BUF_SIZE,
tascam_midi_in_urb_complete, tascam);
m_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
}
for (i = 0; i < NUM_MIDI_OUT_URBS; i++) {
struct urb *m_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!m_urb)
goto error;
tascam->midi_out_urbs[i] = m_urb;
m_urb->transfer_buffer = usb_alloc_coherent(tascam->dev,
MIDI_OUT_BUF_SIZE, GFP_KERNEL, &m_urb->transfer_dma);
if (!m_urb->transfer_buffer)
goto error;
usb_fill_bulk_urb(m_urb, tascam->dev,
usb_sndbulkpipe(tascam->dev, EP_MIDI_OUT),
m_urb->transfer_buffer, 0, /* length set later */
tascam_midi_out_urb_complete, tascam);
m_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
}
tascam->capture_ring_buffer = kmalloc(CAPTURE_RING_BUFFER_SIZE, GFP_KERNEL); tascam->capture_ring_buffer = kmalloc(CAPTURE_RING_BUFFER_SIZE, GFP_KERNEL);
if (!tascam->capture_ring_buffer) if (!tascam->capture_ring_buffer)
goto error; goto error;
@ -646,7 +770,8 @@ static int tascam_capture_close(struct snd_pcm_substream *substream)
* @rate: the target sample rate (e.g., 44100, 96000). * @rate: the target sample rate (e.g., 44100, 96000).
* *
* This function sends a sequence of vendor-specific and UAC control messages * This function sends a sequence of vendor-specific and UAC control messages
* to configure the device hardware for the specified sample rate. * to configure the device hardware for the specified sample rate. This sequence
* is also required to activate the MIDI ports.
* *
* Return: 0 on success, or a negative error code on failure. * Return: 0 on success, or a negative error code on failure.
*/ */
@ -714,6 +839,20 @@ static int tascam_pcm_hw_params(struct snd_pcm_substream *substream,
int err; int err;
unsigned int rate = params_rate(params); unsigned int rate = params_rate(params);
/* --- MODIFIED: Check if rate is already set --- */
/**
* The device is configured to a default rate at probe time to enable
* MIDI. If an audio application requests the same rate, we don't need
* to re-run the entire configuration sequence.
*/
if (rate == tascam->current_rate) {
err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
if (err < 0)
return err;
// Rate is already configured, just return.
return 0;
}
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)
return err; return err;
@ -741,14 +880,13 @@ static int tascam_pcm_hw_params(struct snd_pcm_substream *substream,
} }
} }
if (tascam->current_rate != rate) { /* This will only run if the new rate is different from the current one */
err = us144mkii_configure_device_for_rate(tascam, rate); err = us144mkii_configure_device_for_rate(tascam, rate);
if (err < 0) { if (err < 0) {
tascam->current_rate = 0; tascam->current_rate = 0;
return err; return err;
}
tascam->current_rate = rate;
} }
tascam->current_rate = rate;
return 0; return 0;
} }
@ -1350,15 +1488,420 @@ static void capture_urb_complete(struct urb *urb)
dev_err_ratelimited(tascam->card->dev, "Failed to resubmit capture URB: %d\n", ret); dev_err_ratelimited(tascam->card->dev, "Failed to resubmit capture URB: %d\n", ret);
} }
static int tascam_create_pcm(struct tascam_card *tascam) /* --- ALSA RawMIDI Implementation --- */
/**
* tascam_midi_in_urb_complete - Completion handler for MIDI IN bulk URBs.
* @urb: The completed URB.
*
* The device sends MIDI data in a peculiar 2-packet format. A standard 3-byte
* MIDI message (e.g., Note On) is split across two 9-byte USB packets. This
* function uses a simple state machine to reassemble these messages.
*/
static void tascam_midi_in_urb_complete(struct urb *urb)
{
struct tascam_card *tascam = urb->context;
int ret;
if (urb->status) {
if (urb->status != -ENOENT && urb->status != -ECONNRESET && urb->status != -ESHUTDOWN)
dev_info(tascam->card->dev, "MIDI IN URB cancelled: %d\n", urb->status);
return;
}
if (!tascam || !atomic_read(&tascam->midi_in_active) || !tascam->midi_in_substream)
goto resubmit;
/* The device sends MIDI data in 9-byte packets. */
if (urb->actual_length == 9) {
u8 *buf = urb->transfer_buffer;
/* Ignore pure 0xFD padding packets */
if (buf[0] == 0xfd && buf[1] == 0xfd)
goto resubmit;
if (tascam->midi_in_state == MIDI_IN_STATE_WAIT_PACKET_1) {
/* Packet 1 contains the first MIDI byte (status). */
tascam->midi_in_b0 = buf[1];
tascam->midi_in_state = MIDI_IN_STATE_WAIT_PACKET_2;
} else { /* MIDI_IN_STATE_WAIT_PACKET_2 */
/* Packet 2 contains the next two MIDI bytes. */
u8 msg[3];
msg[0] = tascam->midi_in_b0;
msg[1] = buf[0];
msg[2] = buf[1];
/* Determine message length from status byte */
if ((msg[0] >= 0x80 && msg[0] <= 0xbf) || (msg[0] >= 0xe0 && msg[0] <= 0xef) || (msg[0] == 0xf2)) {
snd_rawmidi_receive(tascam->midi_in_substream, msg, 3);
} else if ((msg[0] >= 0xc0 && msg[0] <= 0xdf) || (msg[0] == 0xf3)) {
snd_rawmidi_receive(tascam->midi_in_substream, msg, 2);
} else { /* 1-byte messages like F1, F6, F8-FF */
snd_rawmidi_receive(tascam->midi_in_substream, msg, 1);
}
/* Reset state for the next message */
tascam->midi_in_state = MIDI_IN_STATE_WAIT_PACKET_1;
}
} else if (urb->actual_length > 0) {
dev_warn(tascam->card->dev, "Received unexpected MIDI IN data size: %d\n", urb->actual_length);
}
resubmit:
if (atomic_read(&tascam->midi_in_active)) {
ret = usb_submit_urb(urb, GFP_ATOMIC);
if (ret < 0)
dev_err_ratelimited(tascam->card->dev, "Failed to resubmit MIDI IN URB: %d\n", ret);
}
}
static int tascam_midi_in_open(struct snd_rawmidi_substream *substream)
{
struct tascam_card *tascam = substream->rmidi->private_data;
tascam->midi_in_substream = substream;
tascam->midi_in_state = MIDI_IN_STATE_WAIT_PACKET_1;
return 0;
}
static int tascam_midi_in_close(struct snd_rawmidi_substream *substream)
{
return 0;
}
static void tascam_midi_in_trigger(struct snd_rawmidi_substream *substream, int up)
{
struct tascam_card *tascam = substream->rmidi->private_data;
int i, err;
if (up) {
if (atomic_xchg(&tascam->midi_in_active, 1) == 0) {
tascam->midi_in_state = MIDI_IN_STATE_WAIT_PACKET_1;
for (i = 0; i < NUM_MIDI_IN_URBS; i++) {
err = usb_submit_urb(tascam->midi_in_urbs[i], GFP_KERNEL);
if (err < 0)
dev_err(tascam->card->dev, "Failed to submit MIDI IN URB %d: %d\n", i, err);
}
}
} else {
if (atomic_xchg(&tascam->midi_in_active, 0) == 1) {
for (i = 0; i < NUM_MIDI_IN_URBS; i++)
usb_kill_urb(tascam->midi_in_urbs[i]);
}
}
}
static struct snd_rawmidi_ops tascam_midi_in_ops = {
.open = tascam_midi_in_open,
.close = tascam_midi_in_close,
.trigger = tascam_midi_in_trigger,
};
/* --- REWRITTEN MIDI OUTPUT LOGIC --- */
/**
* format_tascam_midi_packets - Formats a MIDI message into two 9-byte packets.
* @b0: The first byte of the MIDI message (status).
* @b1: The second byte of the MIDI message (data1).
* @b2: The third byte of the MIDI message (data2).
* @packet1: Destination buffer for the first 9-byte packet.
* @packet2: Destination buffer for the second 9-byte packet.
*
* This helper function creates the two 9-byte packets required by the hardware.
*/
static void format_tascam_midi_packets(u8 b0, u8 b1, u8 b2, u8 *packet1, u8 *packet2)
{
u8 cin = b0 >> 4;
/* Packet 1: Header and first MIDI byte */
memset(packet1, 0xfd, 9);
packet1[0] = (0 << 4) | cin; /* Cable 0, CIN */
packet1[1] = b0;
packet1[8] = 0x00;
/* Packet 2: Second and third MIDI bytes */
memset(packet2, 0xfd, 9);
packet2[0] = b1;
packet2[1] = b2;
packet2[8] = 0x00;
}
/**
* tascam_midi_out_transmit_byte - Process one byte and enqueue formatted packets.
* @tascam: The driver instance.
* @b: The MIDI byte from the ALSA buffer.
*
* This function implements a state machine to parse a raw MIDI byte stream.
* When a complete message is formed, it's formatted into one or two 9-byte
* hardware packets and placed in the `midi_out_packet_queue`.
*/
static void tascam_midi_out_transmit_byte(struct tascam_card *tascam, u8 b)
{
u8 packet1[9], packet2[9];
bool send_two = false;
/* Helper macro to enqueue a single 9-byte packet */
#define ENQUEUE_PACKET(p) \
do { \
int next_write_ptr = (tascam->midi_out_queue_write_ptr + 1) % MIDI_OUT_PACKET_QUEUE_SIZE; \
if (next_write_ptr == tascam->midi_out_queue_read_ptr) { \
dev_warn_ratelimited(tascam->card->dev, "MIDI out queue full, dropping packet.\n"); \
} else { \
memcpy(tascam->midi_out_packet_queue[tascam->midi_out_queue_write_ptr], p, 9); \
tascam->midi_out_queue_write_ptr = next_write_ptr; \
} \
} while (0)
if (b >= 0xf8) { /* System Real-Time messages are single-packet */
format_tascam_midi_packets(b, 0, 0, packet1, packet2);
ENQUEUE_PACKET(packet1);
return;
}
if (b >= 0x80) { /* Status byte */
tascam->midi_out_state.running_status = (b >= 0xf0) ? 0 : b;
tascam->midi_out_state.data[0] = b;
if ((b >= 0xc0 && b <= 0xdf) || b == 0xf1 || b == 0xf3) {
tascam->midi_out_state.state = MIDI_OUT_STATE_1PARAM;
} else if (b == 0xf6) { /* Tune request */
format_tascam_midi_packets(b, 0, 0, packet1, packet2);
ENQUEUE_PACKET(packet1);
tascam->midi_out_state.state = MIDI_OUT_STATE_UNKNOWN;
} else { /* Note On/Off, Poly Pressure, Control Change, Pitch Bend, Song Position */
tascam->midi_out_state.state = MIDI_OUT_STATE_2PARAM_1;
}
} else { /* Data byte */
switch (tascam->midi_out_state.state) {
case MIDI_OUT_STATE_UNKNOWN:
if (tascam->midi_out_state.running_status) {
/* Handle running status: re-process with status byte first */
tascam_midi_out_transmit_byte(tascam, tascam->midi_out_state.running_status);
tascam_midi_out_transmit_byte(tascam, b);
}
break; /* else, orphaned data byte, ignore */
case MIDI_OUT_STATE_1PARAM:
format_tascam_midi_packets(tascam->midi_out_state.data[0], b, 0, packet1, packet2);
send_two = true;
tascam->midi_out_state.state = MIDI_OUT_STATE_UNKNOWN;
break;
case MIDI_OUT_STATE_2PARAM_1:
tascam->midi_out_state.data[1] = b;
tascam->midi_out_state.state = MIDI_OUT_STATE_2PARAM_2;
break;
case MIDI_OUT_STATE_2PARAM_2:
format_tascam_midi_packets(tascam->midi_out_state.data[0], tascam->midi_out_state.data[1], b, packet1, packet2);
send_two = true;
/* For running status, go back to waiting for the first data byte */
tascam->midi_out_state.state = MIDI_OUT_STATE_2PARAM_1;
break;
default:
/* SysEx not fully handled for brevity, but would enqueue here */
break;
}
}
if (send_two) {
ENQUEUE_PACKET(packet1);
ENQUEUE_PACKET(packet2);
}
}
/**
* tascam_midi_out_urb_complete - Completion handler for MIDI OUT bulk URB.
* @urb: The completed URB.
*
* This function runs in interrupt context. It marks the output URB as no
* longer in-flight. It then re-schedules the work handler to check for and
* send any more data waiting in the queue.
*/
static void tascam_midi_out_urb_complete(struct urb *urb)
{
struct tascam_card *tascam = urb->context;
unsigned long flags;
int i, urb_index = -1;
if (urb->status) {
if (urb->status != -ENOENT && urb->status != -ECONNRESET && urb->status != -ESHUTDOWN)
dev_err_ratelimited(tascam->card->dev, "MIDI OUT URB failed: %d\n", urb->status);
}
if (!tascam)
return;
for (i = 0; i < NUM_MIDI_OUT_URBS; i++) {
if (tascam->midi_out_urbs[i] == urb) {
urb_index = i;
break;
}
}
if (urb_index < 0) {
dev_err_ratelimited(tascam->card->dev, "Unknown MIDI OUT URB completed!\n");
return;
}
spin_lock_irqsave(&tascam->midi_out_lock, flags);
clear_bit(urb_index, &tascam->midi_out_urbs_in_flight);
spin_unlock_irqrestore(&tascam->midi_out_lock, flags);
if (atomic_read(&tascam->midi_out_active))
schedule_work(&tascam->midi_out_work);
}
/**
* tascam_midi_out_work_handler - Deferred work for sending MIDI data.
* @work: The work_struct instance.
*
* This function runs in a kernel thread context. It has two phases:
* 1. Pull all available bytes from the ALSA buffer and run them through the
* state machine, which enqueues formatted 9-byte packets.
* 2. Dequeue packets one by one and send each in its own URB.
*/
static void tascam_midi_out_work_handler(struct work_struct *work)
{
struct tascam_card *tascam = container_of(work, struct tascam_card, midi_out_work);
unsigned long flags;
int urb_index;
u8 byte;
if (!tascam->midi_out_substream || !atomic_read(&tascam->midi_out_active))
return;
/* Phase 1: Pull from ALSA and enqueue packets */
spin_lock_irqsave(&tascam->midi_out_lock, flags);
while (snd_rawmidi_transmit(tascam->midi_out_substream, &byte, 1) == 1)
tascam_midi_out_transmit_byte(tascam, byte);
spin_unlock_irqrestore(&tascam->midi_out_lock, flags);
/* Phase 2: Dequeue packets and send them in URBs */
while (atomic_read(&tascam->midi_out_active)) {
struct urb *urb;
spin_lock_irqsave(&tascam->midi_out_lock, flags);
/* Check if there is anything to send */
if (tascam->midi_out_queue_read_ptr == tascam->midi_out_queue_write_ptr) {
spin_unlock_irqrestore(&tascam->midi_out_lock, flags);
return; /* Queue is empty */
}
/* Find a free URB */
urb_index = -1;
for (int i = 0; i < NUM_MIDI_OUT_URBS; i++) {
if (!test_bit(i, &tascam->midi_out_urbs_in_flight)) {
urb_index = i;
break;
}
}
if (urb_index < 0) {
spin_unlock_irqrestore(&tascam->midi_out_lock, flags);
return; /* No free URBs, completion will reschedule */
}
urb = tascam->midi_out_urbs[urb_index];
/* Dequeue one 9-byte packet and copy it to the URB */
memcpy(urb->transfer_buffer,
tascam->midi_out_packet_queue[tascam->midi_out_queue_read_ptr],
9);
urb->transfer_buffer_length = 9;
tascam->midi_out_queue_read_ptr = (tascam->midi_out_queue_read_ptr + 1) % MIDI_OUT_PACKET_QUEUE_SIZE;
set_bit(urb_index, &tascam->midi_out_urbs_in_flight);
spin_unlock_irqrestore(&tascam->midi_out_lock, flags);
if (usb_submit_urb(urb, GFP_KERNEL) < 0) {
dev_err_ratelimited(tascam->card->dev, "Failed to submit MIDI OUT URB %d\n", urb_index);
spin_lock_irqsave(&tascam->midi_out_lock, flags);
clear_bit(urb_index, &tascam->midi_out_urbs_in_flight);
spin_unlock_irqrestore(&tascam->midi_out_lock, flags);
}
}
}
static int tascam_midi_out_open(struct snd_rawmidi_substream *substream)
{
struct tascam_card *tascam = substream->rmidi->private_data;
tascam->midi_out_substream = substream;
/* Initialize the MIDI output state machine. */
tascam->midi_out_state.state = MIDI_OUT_STATE_UNKNOWN;
tascam->midi_out_state.running_status = 0;
/* --- NEW: Initialize queue pointers --- */
tascam->midi_out_queue_read_ptr = 0;
tascam->midi_out_queue_write_ptr = 0;
return 0;
}
static int tascam_midi_out_close(struct snd_rawmidi_substream *substream)
{
return 0;
}
static void tascam_midi_out_drain(struct snd_rawmidi_substream *substream)
{
struct tascam_card *tascam = substream->rmidi->private_data;
int i;
/* Ensure all pending work is finished and kill active URBs */
cancel_work_sync(&tascam->midi_out_work);
for (i = 0; i < NUM_MIDI_OUT_URBS; i++)
usb_kill_urb(tascam->midi_out_urbs[i]);
}
static void tascam_midi_out_trigger(struct snd_rawmidi_substream *substream, int up)
{
struct tascam_card *tascam = substream->rmidi->private_data;
if (up) {
if (atomic_xchg(&tascam->midi_out_active, 1) == 0)
schedule_work(&tascam->midi_out_work);
} else {
atomic_xchg(&tascam->midi_out_active, 0);
}
}
static struct snd_rawmidi_ops tascam_midi_out_ops = {
.open = tascam_midi_out_open,
.close = tascam_midi_out_close,
.trigger = tascam_midi_out_trigger,
.drain = tascam_midi_out_drain,
};
/**
* tascam_create_midi - Create and initialize the ALSA rawmidi device.
* @tascam: The driver instance.
*
* Return: 0 on success, or a negative error code on failure.
*/
static int tascam_create_midi(struct tascam_card *tascam)
{ {
struct snd_pcm *pcm;
int err; int err;
err = snd_pcm_new(tascam->card, "US144MKII", 0, 1, 1, &pcm); err = snd_rawmidi_new(tascam->card, "US144MKII MIDI", 0, 1, 1, &tascam->rmidi);
if (err < 0) if (err < 0)
return err; return err;
strscpy(tascam->rmidi->name, "US144MKII MIDI", sizeof(tascam->rmidi->name));
tascam->rmidi->private_data = tascam;
snd_rawmidi_set_ops(tascam->rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &tascam_midi_in_ops);
snd_rawmidi_set_ops(tascam->rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &tascam_midi_out_ops);
tascam->rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT |
SNDRV_RAWMIDI_INFO_OUTPUT |
SNDRV_RAWMIDI_INFO_DUPLEX;
return 0;
}
static int tascam_create_pcm(struct snd_pcm *pcm)
{
struct tascam_card *tascam = pcm->private_data;
int err;
err = snd_ctl_add(tascam->card, snd_ctl_new1(&tascam_line_out_control, tascam)); err = snd_ctl_add(tascam->card, snd_ctl_new1(&tascam_line_out_control, tascam));
if (err < 0) return err; if (err < 0) return err;
err = snd_ctl_add(tascam->card, snd_ctl_new1(&tascam_digital_out_control, tascam)); err = snd_ctl_add(tascam->card, snd_ctl_new1(&tascam_digital_out_control, tascam));
@ -1372,8 +1915,6 @@ static int tascam_create_pcm(struct tascam_card *tascam)
if (err < 0) if (err < 0)
return err; return err;
tascam->pcm = pcm;
pcm->private_data = tascam;
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &tascam_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &tascam_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &tascam_capture_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &tascam_capture_ops);
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
@ -1401,6 +1942,18 @@ static int tascam_suspend(struct usb_interface *intf, pm_message_t message)
snd_pcm_suspend_all(tascam->pcm); snd_pcm_suspend_all(tascam->pcm);
cancel_work_sync(&tascam->capture_work); cancel_work_sync(&tascam->capture_work);
cancel_work_sync(&tascam->midi_out_work);
if (atomic_read(&tascam->midi_in_active)) {
int i;
for (i = 0; i < NUM_MIDI_IN_URBS; i++)
usb_kill_urb(tascam->midi_in_urbs[i]);
}
if (atomic_read(&tascam->midi_out_active)) {
int i;
for (i = 0; i < NUM_MIDI_OUT_URBS; i++)
usb_kill_urb(tascam->midi_out_urbs[i]);
}
return 0; return 0;
} }
@ -1438,6 +1991,18 @@ static int tascam_resume(struct usb_interface *intf)
} }
} }
/* Resume MIDI streams if they were active */
if (atomic_read(&tascam->midi_in_active)) {
int i;
for (i = 0; i < NUM_MIDI_IN_URBS; i++) {
err = usb_submit_urb(tascam->midi_in_urbs[i], GFP_KERNEL);
if (err < 0)
dev_err(&intf->dev, "Failed to resume MIDI IN URB %d: %d\n", i, err);
}
}
if (atomic_read(&tascam->midi_out_active))
schedule_work(&tascam->midi_out_work);
return 0; return 0;
} }
@ -1448,8 +2013,8 @@ static int tascam_resume(struct usb_interface *intf)
* *
* This function is called by the USB core when a device matching the driver's * This function is called by the USB core when a device matching the driver's
* ID table is connected. It allocates the sound card, initializes the driver * ID table is connected. It allocates the sound card, initializes the driver
* data structure, claims interfaces, sets up the device, creates the PCM * data structure, claims interfaces, sets up the device, creates the PCM,
* and control interfaces, and registers the sound card with ALSA. * MIDI, and control interfaces, and registers the sound card with ALSA.
* *
* Return: 0 on success, or a negative error code on failure. * Return: 0 on success, or a negative error code on failure.
*/ */
@ -1458,6 +2023,7 @@ static int tascam_probe(struct usb_interface *intf, const struct usb_device_id *
struct usb_device *dev = interface_to_usbdev(intf); struct usb_device *dev = interface_to_usbdev(intf);
struct tascam_card *tascam; struct tascam_card *tascam;
struct snd_card *card; struct snd_card *card;
struct snd_pcm *pcm;
int err; int err;
u8 *handshake_buf; u8 *handshake_buf;
@ -1491,6 +2057,13 @@ static int tascam_probe(struct usb_interface *intf, const struct usb_device_id *
tascam->capture_34_source = 1; tascam->capture_34_source = 1;
tascam->current_rate = 0; tascam->current_rate = 0;
/* MIDI initialization */
atomic_set(&tascam->midi_in_active, 0);
atomic_set(&tascam->midi_out_active, 0);
spin_lock_init(&tascam->midi_out_lock);
INIT_WORK(&tascam->midi_out_work, tascam_midi_out_work_handler);
tascam->midi_out_urbs_in_flight = 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));
snprintf(card->longname, sizeof(card->longname), "TASCAM US-144MKII (VID:%04x, PID:%04x) at %s", snprintf(card->longname, sizeof(card->longname), "TASCAM US-144MKII (VID:%04x, PID:%04x) at %s",
@ -1530,15 +2103,43 @@ static int tascam_probe(struct usb_interface *intf, const struct usb_device_id *
err = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), VENDOR_REQ_MODE_CONTROL, err = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), VENDOR_REQ_MODE_CONTROL,
RT_D2H_VENDOR_DEV, MODE_VAL_HANDSHAKE_READ, 0x0000, RT_D2H_VENDOR_DEV, MODE_VAL_HANDSHAKE_READ, 0x0000,
handshake_buf, 1, USB_CTRL_TIMEOUT_MS); handshake_buf, 1, USB_CTRL_TIMEOUT_MS);
if (err == 1 && handshake_buf[0] == HANDSHAKE_SUCCESS_VAL) if (err == 1 && handshake_buf[0] == HANDSHAKE_SUCCESS_VAL) {
dev_info(&intf->dev, "Handshake successful.\n"); dev_info(&intf->dev, "Handshake successful.\n");
else } else {
dev_warn(&intf->dev, "Handshake failed (err %d, val 0x%02x), continuing anyway.\n", err, err > 0 ? handshake_buf[0] : 0); dev_warn(&intf->dev, "Handshake failed (err %d, val 0x%02x), continuing anyway.\n",
err, (unsigned int)(err > 0 ? handshake_buf[0] : 0));
}
kfree(handshake_buf); kfree(handshake_buf);
err = tascam_create_pcm(tascam); err = snd_pcm_new(tascam->card, "US144MKII", 0, 1, 1, &pcm);
if (err < 0) if (err < 0)
goto release_iface1_and_free_card; goto release_iface1_and_free_card;
tascam->pcm = pcm;
pcm->private_data = tascam;
err = tascam_create_pcm(pcm);
if (err < 0)
goto release_iface1_and_free_card;
err = tascam_create_midi(tascam);
if (err < 0)
goto release_iface1_and_free_card;
/* --- NEW: Perform initial configuration to enable MIDI --- */
/**
* The device's MIDI ports are not active until the full sample rate
* configuration sequence has been sent. We do this at probe time with
* a default rate so that MIDI can be used without first starting an
* audio stream.
*/
dev_info(&intf->dev, "Performing initial configuration for MIDI activation.\n");
err = us144mkii_configure_device_for_rate(tascam, 44100);
if (err < 0) {
dev_err(&intf->dev, "Initial device configuration failed, MIDI may not work.\n");
/* Don't fail the whole probe, as audio might still be configured later */
} else {
tascam->current_rate = 44100;
}
if (device_create_file(&intf->dev, &dev_attr_driver_version)) if (device_create_file(&intf->dev, &dev_attr_driver_version))
dev_warn(&intf->dev, "Could not create sysfs attribute for driver version\n"); dev_warn(&intf->dev, "Could not create sysfs attribute for driver version\n");
@ -1586,6 +2187,7 @@ static void tascam_disconnect(struct usb_interface *intf)
snd_card_disconnect(tascam->card); snd_card_disconnect(tascam->card);
cancel_work_sync(&tascam->capture_work); cancel_work_sync(&tascam->capture_work);
cancel_work_sync(&tascam->midi_out_work);
if (tascam->iface1) { if (tascam->iface1) {
usb_set_intfdata(tascam->iface1, NULL); usb_set_intfdata(tascam->iface1, NULL);

2081
working midi out Normal file

File diff suppressed because it is too large Load Diff