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