1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Linux driver for TerraTec DMX 6Fire USB 4 * 5 * Rawmidi driver 6 * 7 * Author: Torsten Schenk <torsten.schenk@zoho.com> 8 * Created: Jan 01, 2011 9 * Copyright: (C) Torsten Schenk 10 */ 11 12 #include <sound/rawmidi.h> 13 14 #include "midi.h" 15 #include "chip.h" 16 #include "comm.h" 17 18 enum { 19 MIDI_BUFSIZE = 64 20 }; 21 22 static void usb6fire_midi_out_handler(struct urb *urb) 23 { 24 struct midi_runtime *rt = urb->context; 25 int ret; 26 27 guard(spinlock_irqsave)(&rt->out_lock); 28 29 if (rt->out) { 30 ret = snd_rawmidi_transmit(rt->out, rt->out_buffer + 4, 31 MIDI_BUFSIZE - 4); 32 if (ret > 0) { /* more data available, send next packet */ 33 rt->out_buffer[1] = ret + 2; 34 rt->out_buffer[3] = rt->out_serial++; 35 urb->transfer_buffer_length = ret + 4; 36 37 ret = usb_submit_urb(urb, GFP_ATOMIC); 38 if (ret < 0) 39 dev_err(&urb->dev->dev, 40 "midi out urb submit failed: %d\n", 41 ret); 42 } else /* no more data to transmit */ 43 rt->out = NULL; 44 } 45 } 46 47 static void usb6fire_midi_in_received( 48 struct midi_runtime *rt, u8 *data, int length) 49 { 50 guard(spinlock_irqsave)(&rt->in_lock); 51 if (rt->in) 52 snd_rawmidi_receive(rt->in, data, length); 53 } 54 55 static int usb6fire_midi_out_open(struct snd_rawmidi_substream *alsa_sub) 56 { 57 return 0; 58 } 59 60 static int usb6fire_midi_out_close(struct snd_rawmidi_substream *alsa_sub) 61 { 62 return 0; 63 } 64 65 static void usb6fire_midi_out_trigger( 66 struct snd_rawmidi_substream *alsa_sub, int up) 67 { 68 struct midi_runtime *rt = alsa_sub->rmidi->private_data; 69 struct urb *urb = &rt->out_urb; 70 __s8 ret; 71 72 guard(spinlock_irqsave)(&rt->out_lock); 73 if (up) { /* start transfer */ 74 if (rt->out) /* we are already transmitting so just return */ 75 return; 76 77 ret = snd_rawmidi_transmit(alsa_sub, rt->out_buffer + 4, 78 MIDI_BUFSIZE - 4); 79 if (ret > 0) { 80 rt->out_buffer[1] = ret + 2; 81 rt->out_buffer[3] = rt->out_serial++; 82 urb->transfer_buffer_length = ret + 4; 83 84 ret = usb_submit_urb(urb, GFP_ATOMIC); 85 if (ret < 0) 86 dev_err(&urb->dev->dev, 87 "midi out urb submit failed: %d\n", 88 ret); 89 else 90 rt->out = alsa_sub; 91 } 92 } else if (rt->out == alsa_sub) 93 rt->out = NULL; 94 } 95 96 static void usb6fire_midi_out_drain(struct snd_rawmidi_substream *alsa_sub) 97 { 98 struct midi_runtime *rt = alsa_sub->rmidi->private_data; 99 int retry = 0; 100 101 while (rt->out && retry++ < 100) 102 msleep(10); 103 } 104 105 static int usb6fire_midi_in_open(struct snd_rawmidi_substream *alsa_sub) 106 { 107 return 0; 108 } 109 110 static int usb6fire_midi_in_close(struct snd_rawmidi_substream *alsa_sub) 111 { 112 return 0; 113 } 114 115 static void usb6fire_midi_in_trigger( 116 struct snd_rawmidi_substream *alsa_sub, int up) 117 { 118 struct midi_runtime *rt = alsa_sub->rmidi->private_data; 119 120 guard(spinlock_irqsave)(&rt->in_lock); 121 if (up) 122 rt->in = alsa_sub; 123 else 124 rt->in = NULL; 125 } 126 127 static const struct snd_rawmidi_ops out_ops = { 128 .open = usb6fire_midi_out_open, 129 .close = usb6fire_midi_out_close, 130 .trigger = usb6fire_midi_out_trigger, 131 .drain = usb6fire_midi_out_drain 132 }; 133 134 static const struct snd_rawmidi_ops in_ops = { 135 .open = usb6fire_midi_in_open, 136 .close = usb6fire_midi_in_close, 137 .trigger = usb6fire_midi_in_trigger 138 }; 139 140 int usb6fire_midi_init(struct sfire_chip *chip) 141 { 142 int ret; 143 struct midi_runtime *rt = kzalloc(sizeof(struct midi_runtime), 144 GFP_KERNEL); 145 struct comm_runtime *comm_rt = chip->comm; 146 147 if (!rt) 148 return -ENOMEM; 149 150 rt->out_buffer = kzalloc(MIDI_BUFSIZE, GFP_KERNEL); 151 if (!rt->out_buffer) { 152 kfree(rt); 153 return -ENOMEM; 154 } 155 156 rt->chip = chip; 157 rt->in_received = usb6fire_midi_in_received; 158 rt->out_buffer[0] = 0x80; /* 'send midi' command */ 159 rt->out_buffer[1] = 0x00; /* size of data */ 160 rt->out_buffer[2] = 0x00; /* always 0 */ 161 spin_lock_init(&rt->in_lock); 162 spin_lock_init(&rt->out_lock); 163 164 comm_rt->init_urb(comm_rt, &rt->out_urb, rt->out_buffer, rt, 165 usb6fire_midi_out_handler); 166 167 ret = snd_rawmidi_new(chip->card, "6FireUSB", 0, 1, 1, &rt->instance); 168 if (ret < 0) { 169 kfree(rt->out_buffer); 170 kfree(rt); 171 dev_err(&chip->dev->dev, "unable to create midi.\n"); 172 return ret; 173 } 174 rt->instance->private_data = rt; 175 strscpy(rt->instance->name, "DMX6FireUSB MIDI"); 176 rt->instance->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | 177 SNDRV_RAWMIDI_INFO_INPUT | 178 SNDRV_RAWMIDI_INFO_DUPLEX; 179 snd_rawmidi_set_ops(rt->instance, SNDRV_RAWMIDI_STREAM_OUTPUT, 180 &out_ops); 181 snd_rawmidi_set_ops(rt->instance, SNDRV_RAWMIDI_STREAM_INPUT, 182 &in_ops); 183 184 chip->midi = rt; 185 return 0; 186 } 187 188 void usb6fire_midi_abort(struct sfire_chip *chip) 189 { 190 struct midi_runtime *rt = chip->midi; 191 192 if (rt) 193 usb_poison_urb(&rt->out_urb); 194 } 195 196 void usb6fire_midi_destroy(struct sfire_chip *chip) 197 { 198 struct midi_runtime *rt = chip->midi; 199 200 kfree(rt->out_buffer); 201 kfree(rt); 202 chip->midi = NULL; 203 } 204