xref: /linux/sound/usb/usx2y/us144mkii_midi.c (revision 05a54fa773284d1a7923cdfdd8f0c8dabb98bd26)
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