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