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