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 * Version: 0.3.0 9 * Copyright: (C) Torsten Schenk 10 * 11 * This program is free software; you can redistribute it and/or modify 12 * it under the terms of the GNU General Public License as published by 13 * the Free Software Foundation; either version 2 of the License, or 14 * (at your option) any later version. 15 */ 16 17 #include <sound/rawmidi.h> 18 19 #include "midi.h" 20 #include "chip.h" 21 #include "comm.h" 22 23 static void usb6fire_midi_out_handler(struct urb *urb) 24 { 25 struct midi_runtime *rt = urb->context; 26 int ret; 27 unsigned long flags; 28 29 spin_lock_irqsave(&rt->out_lock, flags); 30 31 if (rt->out) { 32 ret = snd_rawmidi_transmit(rt->out, rt->out_buffer + 4, 33 MIDI_BUFSIZE - 4); 34 if (ret > 0) { /* more data available, send next packet */ 35 rt->out_buffer[1] = ret + 2; 36 rt->out_buffer[3] = rt->out_serial++; 37 urb->transfer_buffer_length = ret + 4; 38 39 ret = usb_submit_urb(urb, GFP_ATOMIC); 40 if (ret < 0) 41 snd_printk(KERN_ERR PREFIX "midi out urb " 42 "submit failed: %d\n", 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 snd_printk(KERN_ERR PREFIX "midi out urb " 95 "submit failed: %d\n", ret); 96 else 97 rt->out = alsa_sub; 98 } 99 } else if (rt->out == alsa_sub) 100 rt->out = NULL; 101 spin_unlock_irqrestore(&rt->out_lock, flags); 102 } 103 104 static void usb6fire_midi_out_drain(struct snd_rawmidi_substream *alsa_sub) 105 { 106 struct midi_runtime *rt = alsa_sub->rmidi->private_data; 107 int retry = 0; 108 109 while (rt->out && retry++ < 100) 110 msleep(10); 111 } 112 113 static int usb6fire_midi_in_open(struct snd_rawmidi_substream *alsa_sub) 114 { 115 return 0; 116 } 117 118 static int usb6fire_midi_in_close(struct snd_rawmidi_substream *alsa_sub) 119 { 120 return 0; 121 } 122 123 static void usb6fire_midi_in_trigger( 124 struct snd_rawmidi_substream *alsa_sub, int up) 125 { 126 struct midi_runtime *rt = alsa_sub->rmidi->private_data; 127 unsigned long flags; 128 129 spin_lock_irqsave(&rt->in_lock, flags); 130 if (up) 131 rt->in = alsa_sub; 132 else 133 rt->in = NULL; 134 spin_unlock_irqrestore(&rt->in_lock, flags); 135 } 136 137 static struct snd_rawmidi_ops out_ops = { 138 .open = usb6fire_midi_out_open, 139 .close = usb6fire_midi_out_close, 140 .trigger = usb6fire_midi_out_trigger, 141 .drain = usb6fire_midi_out_drain 142 }; 143 144 static struct snd_rawmidi_ops in_ops = { 145 .open = usb6fire_midi_in_open, 146 .close = usb6fire_midi_in_close, 147 .trigger = usb6fire_midi_in_trigger 148 }; 149 150 int __devinit usb6fire_midi_init(struct sfire_chip *chip) 151 { 152 int ret; 153 struct midi_runtime *rt = kzalloc(sizeof(struct midi_runtime), 154 GFP_KERNEL); 155 struct comm_runtime *comm_rt = chip->comm; 156 157 if (!rt) 158 return -ENOMEM; 159 160 rt->chip = chip; 161 rt->in_received = usb6fire_midi_in_received; 162 rt->out_buffer[0] = 0x80; /* 'send midi' command */ 163 rt->out_buffer[1] = 0x00; /* size of data */ 164 rt->out_buffer[2] = 0x00; /* always 0 */ 165 spin_lock_init(&rt->in_lock); 166 spin_lock_init(&rt->out_lock); 167 168 comm_rt->init_urb(comm_rt, &rt->out_urb, rt->out_buffer, rt, 169 usb6fire_midi_out_handler); 170 171 ret = snd_rawmidi_new(chip->card, "6FireUSB", 0, 1, 1, &rt->instance); 172 if (ret < 0) { 173 kfree(rt); 174 snd_printk(KERN_ERR PREFIX "unable to create midi.\n"); 175 return ret; 176 } 177 rt->instance->private_data = rt; 178 strcpy(rt->instance->name, "DMX6FireUSB MIDI"); 179 rt->instance->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | 180 SNDRV_RAWMIDI_INFO_INPUT | 181 SNDRV_RAWMIDI_INFO_DUPLEX; 182 snd_rawmidi_set_ops(rt->instance, SNDRV_RAWMIDI_STREAM_OUTPUT, 183 &out_ops); 184 snd_rawmidi_set_ops(rt->instance, SNDRV_RAWMIDI_STREAM_INPUT, 185 &in_ops); 186 187 chip->midi = rt; 188 return 0; 189 } 190 191 void usb6fire_midi_abort(struct sfire_chip *chip) 192 { 193 struct midi_runtime *rt = chip->midi; 194 195 if (rt) 196 usb_poison_urb(&rt->out_urb); 197 } 198 199 void usb6fire_midi_destroy(struct sfire_chip *chip) 200 { 201 kfree(chip->midi); 202 chip->midi = NULL; 203 } 204