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