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