167afec15SŠerif Rami // SPDX-License-Identifier: GPL-2.0-only 267afec15SŠerif Rami // Copyright (c) 2025 Šerif Rami <ramiserifpersia@gmail.com> 367afec15SŠerif Rami 467afec15SŠerif Rami #include "us144mkii.h" 567afec15SŠerif Rami 667afec15SŠerif Rami /** 767afec15SŠerif Rami * tascam_midi_in_work_handler() - Deferred work for processing MIDI input. 867afec15SŠerif Rami * @work: The work_struct instance. 967afec15SŠerif Rami * 1067afec15SŠerif Rami * This function runs in a thread context. It safely reads raw USB data from 1167afec15SŠerif Rami * the kfifo, processes it by stripping protocol-specific padding bytes, and 1267afec15SŠerif Rami * passes the clean MIDI data to the ALSA rawmidi subsystem. 1367afec15SŠerif Rami */ 1467afec15SŠerif Rami static void tascam_midi_in_work_handler(struct work_struct *work) 1567afec15SŠerif Rami { 1667afec15SŠerif Rami struct tascam_card *tascam = 1767afec15SŠerif Rami container_of(work, struct tascam_card, midi_in_work); 1867afec15SŠerif Rami u8 buf[9]; 1967afec15SŠerif Rami u8 clean_buf[8]; 2067afec15SŠerif Rami unsigned int count, clean_count; 2167afec15SŠerif Rami 2267afec15SŠerif Rami if (!tascam->midi_in_substream) 2367afec15SŠerif Rami return; 2467afec15SŠerif Rami 2567afec15SŠerif Rami while (kfifo_out_spinlocked(&tascam->midi_in_fifo, buf, sizeof(buf), 2667afec15SŠerif Rami &tascam->midi_in_lock) == sizeof(buf)) { 2767afec15SŠerif Rami clean_count = 0; 2867afec15SŠerif Rami for (count = 0; count < 8; ++count) { 2967afec15SŠerif Rami if (buf[count] != 0xfd) 3067afec15SŠerif Rami clean_buf[clean_count++] = buf[count]; 3167afec15SŠerif Rami } 3267afec15SŠerif Rami 3367afec15SŠerif Rami if (clean_count > 0) 3467afec15SŠerif Rami snd_rawmidi_receive(tascam->midi_in_substream, 3567afec15SŠerif Rami clean_buf, clean_count); 3667afec15SŠerif Rami } 3767afec15SŠerif Rami } 3867afec15SŠerif Rami 3967afec15SŠerif Rami void tascam_midi_in_urb_complete(struct urb *urb) 4067afec15SŠerif Rami { 4167afec15SŠerif Rami struct tascam_card *tascam = urb->context; 4267afec15SŠerif Rami int ret; 4367afec15SŠerif Rami 440c5e2ae2SŠerif Rami if (!tascam) 450c5e2ae2SŠerif Rami goto out; 460c5e2ae2SŠerif Rami 4767afec15SŠerif Rami if (urb->status) { 4867afec15SŠerif Rami if (urb->status != -ENOENT && urb->status != -ECONNRESET && 4967afec15SŠerif Rami urb->status != -ESHUTDOWN && urb->status != -EPROTO) { 5067afec15SŠerif Rami dev_err_ratelimited(tascam->card->dev, 5167afec15SŠerif Rami "MIDI IN URB failed: status %d\n", 5267afec15SŠerif Rami urb->status); 5367afec15SŠerif Rami } 5467afec15SŠerif Rami goto out; 5567afec15SŠerif Rami } 5667afec15SŠerif Rami 570c5e2ae2SŠerif Rami if (atomic_read(&tascam->midi_in_active) && 5867afec15SŠerif Rami urb->actual_length > 0) { 5967afec15SŠerif Rami kfifo_in_spinlocked(&tascam->midi_in_fifo, urb->transfer_buffer, 6067afec15SŠerif Rami urb->actual_length, &tascam->midi_in_lock); 6167afec15SŠerif Rami schedule_work(&tascam->midi_in_work); 6267afec15SŠerif Rami } 6367afec15SŠerif Rami 6467afec15SŠerif Rami usb_get_urb(urb); 6567afec15SŠerif Rami usb_anchor_urb(urb, &tascam->midi_in_anchor); 6667afec15SŠerif Rami ret = usb_submit_urb(urb, GFP_ATOMIC); 6767afec15SŠerif Rami if (ret < 0) { 6867afec15SŠerif Rami dev_err(tascam->card->dev, 6967afec15SŠerif Rami "Failed to resubmit MIDI IN URB: error %d\n", ret); 7067afec15SŠerif Rami usb_unanchor_urb(urb); 710c5e2ae2SŠerif Rami goto out; 7267afec15SŠerif Rami } 730c5e2ae2SŠerif Rami 7467afec15SŠerif Rami out: 7567afec15SŠerif Rami usb_put_urb(urb); 7667afec15SŠerif Rami } 7767afec15SŠerif Rami 7867afec15SŠerif Rami /** 7967afec15SŠerif Rami * tascam_midi_in_open() - Opens the MIDI input substream. 8067afec15SŠerif Rami * @substream: The ALSA rawmidi substream to open. 8167afec15SŠerif Rami * 8267afec15SŠerif Rami * This function stores a reference to the MIDI input substream in the 8367afec15SŠerif Rami * driver's private data. 8467afec15SŠerif Rami * 8567afec15SŠerif Rami * Return: 0 on success. 8667afec15SŠerif Rami */ 8767afec15SŠerif Rami static int tascam_midi_in_open(struct snd_rawmidi_substream *substream) 8867afec15SŠerif Rami { 8967afec15SŠerif Rami struct tascam_card *tascam = substream->rmidi->private_data; 9067afec15SŠerif Rami 9167afec15SŠerif Rami tascam->midi_in_substream = substream; 9267afec15SŠerif Rami return 0; 9367afec15SŠerif Rami } 9467afec15SŠerif Rami 9567afec15SŠerif Rami /** 9667afec15SŠerif Rami * tascam_midi_in_close() - Closes the MIDI input substream. 9767afec15SŠerif Rami * @substream: The ALSA rawmidi substream to close. 9867afec15SŠerif Rami * 9967afec15SŠerif Rami * Return: 0 on success. 10067afec15SŠerif Rami */ 10167afec15SŠerif Rami static int tascam_midi_in_close(struct snd_rawmidi_substream *substream) 10267afec15SŠerif Rami { 10367afec15SŠerif Rami return 0; 10467afec15SŠerif Rami } 10567afec15SŠerif Rami 10667afec15SŠerif Rami /** 10767afec15SŠerif Rami * tascam_midi_in_trigger() - Triggers MIDI input stream activity. 10867afec15SŠerif Rami * @substream: The ALSA rawmidi substream. 10967afec15SŠerif Rami * @up: Boolean indicating whether to start (1) or stop (0) the stream. 11067afec15SŠerif Rami * 11167afec15SŠerif Rami * This function starts or stops the MIDI input URBs based on the 'up' 11267afec15SŠerif Rami * parameter. When starting, it resets the kfifo and submits all MIDI input 11367afec15SŠerif Rami * URBs. When stopping, it kills all anchored MIDI input URBs and cancels the 11467afec15SŠerif Rami * associated workqueue. 11567afec15SŠerif Rami */ 11667afec15SŠerif Rami static void tascam_midi_in_trigger(struct snd_rawmidi_substream *substream, 11767afec15SŠerif Rami int up) 11867afec15SŠerif Rami { 11967afec15SŠerif Rami struct tascam_card *tascam = substream->rmidi->private_data; 12067afec15SŠerif Rami int i, err; 12167afec15SŠerif Rami 12267afec15SŠerif Rami if (up) { 12367afec15SŠerif Rami if (atomic_xchg(&tascam->midi_in_active, 1) == 0) { 12467afec15SŠerif Rami scoped_guard(spinlock_irqsave, &tascam->midi_in_lock) 12567afec15SŠerif Rami { 12667afec15SŠerif Rami kfifo_reset(&tascam->midi_in_fifo); 12767afec15SŠerif Rami } 12867afec15SŠerif Rami 12967afec15SŠerif Rami for (i = 0; i < NUM_MIDI_IN_URBS; i++) { 13067afec15SŠerif Rami usb_get_urb(tascam->midi_in_urbs[i]); 13167afec15SŠerif Rami usb_anchor_urb(tascam->midi_in_urbs[i], 13267afec15SŠerif Rami &tascam->midi_in_anchor); 13367afec15SŠerif Rami err = usb_submit_urb(tascam->midi_in_urbs[i], 13467afec15SŠerif Rami GFP_KERNEL); 13567afec15SŠerif Rami if (err < 0) { 13667afec15SŠerif Rami dev_err(tascam->card->dev, 13767afec15SŠerif Rami "Failed to submit MIDI IN URB %d: %d\n", 13867afec15SŠerif Rami i, err); 13967afec15SŠerif Rami usb_unanchor_urb( 14067afec15SŠerif Rami tascam->midi_in_urbs[i]); 14167afec15SŠerif Rami usb_put_urb(tascam->midi_in_urbs[i]); 14267afec15SŠerif Rami } 14367afec15SŠerif Rami } 14467afec15SŠerif Rami } 14567afec15SŠerif Rami } else { 14667afec15SŠerif Rami if (atomic_xchg(&tascam->midi_in_active, 0) == 1) { 14767afec15SŠerif Rami usb_kill_anchored_urbs(&tascam->midi_in_anchor); 14867afec15SŠerif Rami cancel_work_sync(&tascam->midi_in_work); 14967afec15SŠerif Rami } 15067afec15SŠerif Rami } 15167afec15SŠerif Rami } 15267afec15SŠerif Rami 15367afec15SŠerif Rami /** 15467afec15SŠerif Rami * tascam_midi_in_ops - ALSA rawmidi operations for MIDI input. 15567afec15SŠerif Rami * 15667afec15SŠerif Rami * This structure defines the callback functions for MIDI input stream 15767afec15SŠerif Rami * operations, including open, close, and trigger. 15867afec15SŠerif Rami */ 15967afec15SŠerif Rami static const struct snd_rawmidi_ops tascam_midi_in_ops = { 16067afec15SŠerif Rami .open = tascam_midi_in_open, 16167afec15SŠerif Rami .close = tascam_midi_in_close, 16267afec15SŠerif Rami .trigger = tascam_midi_in_trigger, 16367afec15SŠerif Rami }; 16467afec15SŠerif Rami 16567afec15SŠerif Rami void tascam_midi_out_urb_complete(struct urb *urb) 16667afec15SŠerif Rami { 16767afec15SŠerif Rami struct tascam_card *tascam = urb->context; 16867afec15SŠerif Rami int i, urb_index = -1; 16967afec15SŠerif Rami 17067afec15SŠerif Rami if (urb->status) { 17167afec15SŠerif Rami if (urb->status != -ENOENT && urb->status != -ECONNRESET && 17267afec15SŠerif Rami urb->status != -ESHUTDOWN) { 17367afec15SŠerif Rami dev_err_ratelimited(tascam->card->dev, 17467afec15SŠerif Rami "MIDI OUT URB failed: %d\n", 17567afec15SŠerif Rami urb->status); 17667afec15SŠerif Rami } 17767afec15SŠerif Rami goto out; 17867afec15SŠerif Rami } 17967afec15SŠerif Rami 18067afec15SŠerif Rami if (!tascam) 18167afec15SŠerif Rami goto out; 18267afec15SŠerif Rami 18367afec15SŠerif Rami for (i = 0; i < NUM_MIDI_OUT_URBS; i++) { 18467afec15SŠerif Rami if (tascam->midi_out_urbs[i] == urb) { 18567afec15SŠerif Rami urb_index = i; 18667afec15SŠerif Rami break; 18767afec15SŠerif Rami } 18867afec15SŠerif Rami } 18967afec15SŠerif Rami 19067afec15SŠerif Rami if (urb_index < 0) { 19167afec15SŠerif Rami dev_err_ratelimited(tascam->card->dev, 19267afec15SŠerif Rami "Unknown MIDI OUT URB completed!\n"); 19367afec15SŠerif Rami goto out; 19467afec15SŠerif Rami } 19567afec15SŠerif Rami 19667afec15SŠerif Rami scoped_guard(spinlock_irqsave, &tascam->midi_out_lock) 19767afec15SŠerif Rami { 19867afec15SŠerif Rami clear_bit(urb_index, &tascam->midi_out_urbs_in_flight); 19967afec15SŠerif Rami } 20067afec15SŠerif Rami 20167afec15SŠerif Rami if (atomic_read(&tascam->midi_out_active)) 20267afec15SŠerif Rami schedule_work(&tascam->midi_out_work); 20367afec15SŠerif Rami 20467afec15SŠerif Rami out: 20567afec15SŠerif Rami usb_put_urb(urb); 20667afec15SŠerif Rami } 20767afec15SŠerif Rami 20867afec15SŠerif Rami /** 20967afec15SŠerif Rami * tascam_midi_out_work_handler() - Deferred work for sending MIDI data 21067afec15SŠerif Rami * @work: The work_struct instance. 21167afec15SŠerif Rami * 21267afec15SŠerif Rami * This function handles the proprietary output protocol: take the raw MIDI 21367afec15SŠerif Rami * message bytes from the application, place them at the start of a 9-byte 21467afec15SŠerif Rami * buffer, pad the rest with 0xFD, and add a terminator byte (0x00). 21567afec15SŠerif Rami * This function pulls as many bytes as will fit into one packet from the 21667afec15SŠerif Rami * ALSA buffer and sends them. 21767afec15SŠerif Rami */ 21867afec15SŠerif Rami static void tascam_midi_out_work_handler(struct work_struct *work) 21967afec15SŠerif Rami { 22067afec15SŠerif Rami struct tascam_card *tascam = 22167afec15SŠerif Rami container_of(work, struct tascam_card, midi_out_work); 22267afec15SŠerif Rami struct snd_rawmidi_substream *substream = tascam->midi_out_substream; 22367afec15SŠerif Rami int i; 22467afec15SŠerif Rami 22567afec15SŠerif Rami if (!substream || !atomic_read(&tascam->midi_out_active)) 22667afec15SŠerif Rami return; 22767afec15SŠerif Rami 22867afec15SŠerif Rami while (snd_rawmidi_transmit_peek(substream, (u8[]){ 0 }, 1) == 1) { 22967afec15SŠerif Rami int urb_index; 23067afec15SŠerif Rami struct urb *urb; 23167afec15SŠerif Rami u8 *buf; 23267afec15SŠerif Rami int bytes_to_send; 23367afec15SŠerif Rami 23467afec15SŠerif Rami scoped_guard(spinlock_irqsave, &tascam->midi_out_lock) { 23567afec15SŠerif Rami urb_index = -1; 23667afec15SŠerif Rami for (i = 0; i < NUM_MIDI_OUT_URBS; i++) { 23767afec15SŠerif Rami if (!test_bit( 23867afec15SŠerif Rami i, 23967afec15SŠerif Rami &tascam->midi_out_urbs_in_flight)) { 24067afec15SŠerif Rami urb_index = i; 24167afec15SŠerif Rami break; 24267afec15SŠerif Rami } 24367afec15SŠerif Rami } 24467afec15SŠerif Rami 24567afec15SŠerif Rami if (urb_index < 0) 24667afec15SŠerif Rami return; /* No free URBs, will be rescheduled by 24767afec15SŠerif Rami * completion handler 24867afec15SŠerif Rami */ 24967afec15SŠerif Rami 25067afec15SŠerif Rami urb = tascam->midi_out_urbs[urb_index]; 25167afec15SŠerif Rami buf = urb->transfer_buffer; 25267afec15SŠerif Rami bytes_to_send = snd_rawmidi_transmit(substream, buf, 8); 25367afec15SŠerif Rami 25467afec15SŠerif Rami if (bytes_to_send <= 0) 25567afec15SŠerif Rami break; /* No more data */ 25667afec15SŠerif Rami 25767afec15SŠerif Rami if (bytes_to_send < 9) 25867afec15SŠerif Rami memset(buf + bytes_to_send, 0xfd, 25967afec15SŠerif Rami 9 - bytes_to_send); 260*cdbd2aceSŠerif Rami buf[8] = 0xe0; 26167afec15SŠerif Rami 26267afec15SŠerif Rami set_bit(urb_index, &tascam->midi_out_urbs_in_flight); 26367afec15SŠerif Rami urb->transfer_buffer_length = 9; 26467afec15SŠerif Rami } 26567afec15SŠerif Rami 26667afec15SŠerif Rami usb_get_urb(urb); 26767afec15SŠerif Rami usb_anchor_urb(urb, &tascam->midi_out_anchor); 26867afec15SŠerif Rami if (usb_submit_urb(urb, GFP_KERNEL) < 0) { 26967afec15SŠerif Rami dev_err_ratelimited( 27067afec15SŠerif Rami tascam->card->dev, 27167afec15SŠerif Rami "Failed to submit MIDI OUT URB %d\n", 27267afec15SŠerif Rami urb_index); 27367afec15SŠerif Rami scoped_guard(spinlock_irqsave, &tascam->midi_out_lock) 27467afec15SŠerif Rami { 27567afec15SŠerif Rami clear_bit(urb_index, 27667afec15SŠerif Rami &tascam->midi_out_urbs_in_flight); 27767afec15SŠerif Rami } 27867afec15SŠerif Rami usb_unanchor_urb(urb); 27967afec15SŠerif Rami usb_put_urb(urb); 28067afec15SŠerif Rami break; /* Stop on error */ 28167afec15SŠerif Rami } 28267afec15SŠerif Rami } 28367afec15SŠerif Rami } 28467afec15SŠerif Rami 28567afec15SŠerif Rami /** 28667afec15SŠerif Rami * tascam_midi_out_open() - Opens the MIDI output substream. 28767afec15SŠerif Rami * @substream: The ALSA rawmidi substream to open. 28867afec15SŠerif Rami * 28967afec15SŠerif Rami * This function stores a reference to the MIDI output substream in the 29067afec15SŠerif Rami * driver's private data and initializes the MIDI running status. 29167afec15SŠerif Rami * 29267afec15SŠerif Rami * Return: 0 on success. 29367afec15SŠerif Rami */ 29467afec15SŠerif Rami static int tascam_midi_out_open(struct snd_rawmidi_substream *substream) 29567afec15SŠerif Rami { 29667afec15SŠerif Rami struct tascam_card *tascam = substream->rmidi->private_data; 29767afec15SŠerif Rami 29867afec15SŠerif Rami tascam->midi_out_substream = substream; 29967afec15SŠerif Rami /* Initialize the running status state for the packet packer. */ 30067afec15SŠerif Rami tascam->midi_running_status = 0; 30167afec15SŠerif Rami return 0; 30267afec15SŠerif Rami } 30367afec15SŠerif Rami 30467afec15SŠerif Rami /** 30567afec15SŠerif Rami * tascam_midi_out_close() - Closes the MIDI output substream. 30667afec15SŠerif Rami * @substream: The ALSA rawmidi substream to close. 30767afec15SŠerif Rami * 30867afec15SŠerif Rami * Return: 0 on success. 30967afec15SŠerif Rami */ 31067afec15SŠerif Rami static int tascam_midi_out_close(struct snd_rawmidi_substream *substream) 31167afec15SŠerif Rami { 31267afec15SŠerif Rami return 0; 31367afec15SŠerif Rami } 31467afec15SŠerif Rami 31567afec15SŠerif Rami /** 31667afec15SŠerif Rami * tascam_midi_out_drain() - Drains the MIDI output stream. 31767afec15SŠerif Rami * @substream: The ALSA rawmidi substream. 31867afec15SŠerif Rami * 31967afec15SŠerif Rami * This function cancels any pending MIDI output work and kills all 32067afec15SŠerif Rami * anchored MIDI output URBs, ensuring all data is sent or discarded. 32167afec15SŠerif Rami */ 32267afec15SŠerif Rami static void tascam_midi_out_drain(struct snd_rawmidi_substream *substream) 32367afec15SŠerif Rami { 32467afec15SŠerif Rami struct tascam_card *tascam = substream->rmidi->private_data; 32567afec15SŠerif Rami bool in_flight = true; 32667afec15SŠerif Rami 32767afec15SŠerif Rami while (in_flight) { 32867afec15SŠerif Rami in_flight = false; 32967afec15SŠerif Rami for (int i = 0; i < NUM_MIDI_OUT_URBS; i++) { 33067afec15SŠerif Rami if (test_bit(i, &tascam->midi_out_urbs_in_flight)) { 33167afec15SŠerif Rami in_flight = true; 33267afec15SŠerif Rami break; 33367afec15SŠerif Rami } 33467afec15SŠerif Rami } 33567afec15SŠerif Rami if (in_flight) 33667afec15SŠerif Rami schedule_timeout_uninterruptible(1); 33767afec15SŠerif Rami } 33867afec15SŠerif Rami 33967afec15SŠerif Rami cancel_work_sync(&tascam->midi_out_work); 34067afec15SŠerif Rami usb_kill_anchored_urbs(&tascam->midi_out_anchor); 34167afec15SŠerif Rami } 34267afec15SŠerif Rami 34367afec15SŠerif Rami /** 34467afec15SŠerif Rami * tascam_midi_out_trigger() - Triggers MIDI output stream activity. 34567afec15SŠerif Rami * @substream: The ALSA rawmidi substream. 34667afec15SŠerif Rami * @up: Boolean indicating whether to start (1) or stop (0) the stream. 34767afec15SŠerif Rami * 34867afec15SŠerif Rami * This function starts or stops the MIDI output workqueue based on the 34967afec15SŠerif Rami * 'up' parameter. 35067afec15SŠerif Rami */ 35167afec15SŠerif Rami static void tascam_midi_out_trigger(struct snd_rawmidi_substream *substream, 35267afec15SŠerif Rami int up) 35367afec15SŠerif Rami { 35467afec15SŠerif Rami struct tascam_card *tascam = substream->rmidi->private_data; 35567afec15SŠerif Rami 35667afec15SŠerif Rami if (up) { 35767afec15SŠerif Rami atomic_set(&tascam->midi_out_active, 1); 35867afec15SŠerif Rami schedule_work(&tascam->midi_out_work); 35967afec15SŠerif Rami } else { 36067afec15SŠerif Rami atomic_set(&tascam->midi_out_active, 0); 36167afec15SŠerif Rami } 36267afec15SŠerif Rami } 36367afec15SŠerif Rami 36467afec15SŠerif Rami /** 36567afec15SŠerif Rami * tascam_midi_out_ops - ALSA rawmidi operations for MIDI output. 36667afec15SŠerif Rami * 36767afec15SŠerif Rami * This structure defines the callback functions for MIDI output stream 36867afec15SŠerif Rami * operations, including open, close, trigger, and drain. 36967afec15SŠerif Rami */ 37067afec15SŠerif Rami static const struct snd_rawmidi_ops tascam_midi_out_ops = { 37167afec15SŠerif Rami .open = tascam_midi_out_open, 37267afec15SŠerif Rami .close = tascam_midi_out_close, 37367afec15SŠerif Rami .trigger = tascam_midi_out_trigger, 37467afec15SŠerif Rami .drain = tascam_midi_out_drain, 37567afec15SŠerif Rami }; 37667afec15SŠerif Rami 37767afec15SŠerif Rami int tascam_create_midi(struct tascam_card *tascam) 37867afec15SŠerif Rami { 37967afec15SŠerif Rami int err; 38067afec15SŠerif Rami 38167afec15SŠerif Rami err = snd_rawmidi_new(tascam->card, "US144MKII MIDI", 0, 1, 1, 38267afec15SŠerif Rami &tascam->rmidi); 38367afec15SŠerif Rami if (err < 0) 38467afec15SŠerif Rami return err; 38567afec15SŠerif Rami 38667afec15SŠerif Rami strscpy(tascam->rmidi->name, "US144MKII MIDI", 38767afec15SŠerif Rami sizeof(tascam->rmidi->name)); 38867afec15SŠerif Rami tascam->rmidi->private_data = tascam; 38967afec15SŠerif Rami 39067afec15SŠerif Rami snd_rawmidi_set_ops(tascam->rmidi, SNDRV_RAWMIDI_STREAM_INPUT, 39167afec15SŠerif Rami &tascam_midi_in_ops); 39267afec15SŠerif Rami snd_rawmidi_set_ops(tascam->rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, 39367afec15SŠerif Rami &tascam_midi_out_ops); 39467afec15SŠerif Rami 39567afec15SŠerif Rami tascam->rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT | 39667afec15SŠerif Rami SNDRV_RAWMIDI_INFO_OUTPUT | 39767afec15SŠerif Rami SNDRV_RAWMIDI_INFO_DUPLEX; 39867afec15SŠerif Rami 39967afec15SŠerif Rami INIT_WORK(&tascam->midi_in_work, tascam_midi_in_work_handler); 40067afec15SŠerif Rami INIT_WORK(&tascam->midi_out_work, tascam_midi_out_work_handler); 40167afec15SŠerif Rami 40267afec15SŠerif Rami return 0; 40367afec15SŠerif Rami } 404