xref: /openbmc/linux/sound/usb/6fire/midi.c (revision 8e9356c6)
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