xref: /OK3568_Linux_fs/kernel/sound/usb/6fire/midi.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Linux driver for TerraTec DMX 6Fire USB
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Rawmidi driver
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  * Author:	Torsten Schenk <torsten.schenk@zoho.com>
8*4882a593Smuzhiyun  * Created:	Jan 01, 2011
9*4882a593Smuzhiyun  * Copyright:	(C) Torsten Schenk
10*4882a593Smuzhiyun  */
11*4882a593Smuzhiyun 
12*4882a593Smuzhiyun #include <sound/rawmidi.h>
13*4882a593Smuzhiyun 
14*4882a593Smuzhiyun #include "midi.h"
15*4882a593Smuzhiyun #include "chip.h"
16*4882a593Smuzhiyun #include "comm.h"
17*4882a593Smuzhiyun 
18*4882a593Smuzhiyun enum {
19*4882a593Smuzhiyun 	MIDI_BUFSIZE = 64
20*4882a593Smuzhiyun };
21*4882a593Smuzhiyun 
usb6fire_midi_out_handler(struct urb * urb)22*4882a593Smuzhiyun static void usb6fire_midi_out_handler(struct urb *urb)
23*4882a593Smuzhiyun {
24*4882a593Smuzhiyun 	struct midi_runtime *rt = urb->context;
25*4882a593Smuzhiyun 	int ret;
26*4882a593Smuzhiyun 	unsigned long flags;
27*4882a593Smuzhiyun 
28*4882a593Smuzhiyun 	spin_lock_irqsave(&rt->out_lock, flags);
29*4882a593Smuzhiyun 
30*4882a593Smuzhiyun 	if (rt->out) {
31*4882a593Smuzhiyun 		ret = snd_rawmidi_transmit(rt->out, rt->out_buffer + 4,
32*4882a593Smuzhiyun 				MIDI_BUFSIZE - 4);
33*4882a593Smuzhiyun 		if (ret > 0) { /* more data available, send next packet */
34*4882a593Smuzhiyun 			rt->out_buffer[1] = ret + 2;
35*4882a593Smuzhiyun 			rt->out_buffer[3] = rt->out_serial++;
36*4882a593Smuzhiyun 			urb->transfer_buffer_length = ret + 4;
37*4882a593Smuzhiyun 
38*4882a593Smuzhiyun 			ret = usb_submit_urb(urb, GFP_ATOMIC);
39*4882a593Smuzhiyun 			if (ret < 0)
40*4882a593Smuzhiyun 				dev_err(&urb->dev->dev,
41*4882a593Smuzhiyun 					"midi out urb submit failed: %d\n",
42*4882a593Smuzhiyun 					ret);
43*4882a593Smuzhiyun 		} else /* no more data to transmit */
44*4882a593Smuzhiyun 			rt->out = NULL;
45*4882a593Smuzhiyun 	}
46*4882a593Smuzhiyun 	spin_unlock_irqrestore(&rt->out_lock, flags);
47*4882a593Smuzhiyun }
48*4882a593Smuzhiyun 
usb6fire_midi_in_received(struct midi_runtime * rt,u8 * data,int length)49*4882a593Smuzhiyun static void usb6fire_midi_in_received(
50*4882a593Smuzhiyun 		struct midi_runtime *rt, u8 *data, int length)
51*4882a593Smuzhiyun {
52*4882a593Smuzhiyun 	unsigned long flags;
53*4882a593Smuzhiyun 
54*4882a593Smuzhiyun 	spin_lock_irqsave(&rt->in_lock, flags);
55*4882a593Smuzhiyun 	if (rt->in)
56*4882a593Smuzhiyun 		snd_rawmidi_receive(rt->in, data, length);
57*4882a593Smuzhiyun 	spin_unlock_irqrestore(&rt->in_lock, flags);
58*4882a593Smuzhiyun }
59*4882a593Smuzhiyun 
usb6fire_midi_out_open(struct snd_rawmidi_substream * alsa_sub)60*4882a593Smuzhiyun static int usb6fire_midi_out_open(struct snd_rawmidi_substream *alsa_sub)
61*4882a593Smuzhiyun {
62*4882a593Smuzhiyun 	return 0;
63*4882a593Smuzhiyun }
64*4882a593Smuzhiyun 
usb6fire_midi_out_close(struct snd_rawmidi_substream * alsa_sub)65*4882a593Smuzhiyun static int usb6fire_midi_out_close(struct snd_rawmidi_substream *alsa_sub)
66*4882a593Smuzhiyun {
67*4882a593Smuzhiyun 	return 0;
68*4882a593Smuzhiyun }
69*4882a593Smuzhiyun 
usb6fire_midi_out_trigger(struct snd_rawmidi_substream * alsa_sub,int up)70*4882a593Smuzhiyun static void usb6fire_midi_out_trigger(
71*4882a593Smuzhiyun 		struct snd_rawmidi_substream *alsa_sub, int up)
72*4882a593Smuzhiyun {
73*4882a593Smuzhiyun 	struct midi_runtime *rt = alsa_sub->rmidi->private_data;
74*4882a593Smuzhiyun 	struct urb *urb = &rt->out_urb;
75*4882a593Smuzhiyun 	__s8 ret;
76*4882a593Smuzhiyun 	unsigned long flags;
77*4882a593Smuzhiyun 
78*4882a593Smuzhiyun 	spin_lock_irqsave(&rt->out_lock, flags);
79*4882a593Smuzhiyun 	if (up) { /* start transfer */
80*4882a593Smuzhiyun 		if (rt->out) { /* we are already transmitting so just return */
81*4882a593Smuzhiyun 			spin_unlock_irqrestore(&rt->out_lock, flags);
82*4882a593Smuzhiyun 			return;
83*4882a593Smuzhiyun 		}
84*4882a593Smuzhiyun 
85*4882a593Smuzhiyun 		ret = snd_rawmidi_transmit(alsa_sub, rt->out_buffer + 4,
86*4882a593Smuzhiyun 				MIDI_BUFSIZE - 4);
87*4882a593Smuzhiyun 		if (ret > 0) {
88*4882a593Smuzhiyun 			rt->out_buffer[1] = ret + 2;
89*4882a593Smuzhiyun 			rt->out_buffer[3] = rt->out_serial++;
90*4882a593Smuzhiyun 			urb->transfer_buffer_length = ret + 4;
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun 			ret = usb_submit_urb(urb, GFP_ATOMIC);
93*4882a593Smuzhiyun 			if (ret < 0)
94*4882a593Smuzhiyun 				dev_err(&urb->dev->dev,
95*4882a593Smuzhiyun 					"midi out urb submit failed: %d\n",
96*4882a593Smuzhiyun 					ret);
97*4882a593Smuzhiyun 			else
98*4882a593Smuzhiyun 				rt->out = alsa_sub;
99*4882a593Smuzhiyun 		}
100*4882a593Smuzhiyun 	} else if (rt->out == alsa_sub)
101*4882a593Smuzhiyun 		rt->out = NULL;
102*4882a593Smuzhiyun 	spin_unlock_irqrestore(&rt->out_lock, flags);
103*4882a593Smuzhiyun }
104*4882a593Smuzhiyun 
usb6fire_midi_out_drain(struct snd_rawmidi_substream * alsa_sub)105*4882a593Smuzhiyun static void usb6fire_midi_out_drain(struct snd_rawmidi_substream *alsa_sub)
106*4882a593Smuzhiyun {
107*4882a593Smuzhiyun 	struct midi_runtime *rt = alsa_sub->rmidi->private_data;
108*4882a593Smuzhiyun 	int retry = 0;
109*4882a593Smuzhiyun 
110*4882a593Smuzhiyun 	while (rt->out && retry++ < 100)
111*4882a593Smuzhiyun 		msleep(10);
112*4882a593Smuzhiyun }
113*4882a593Smuzhiyun 
usb6fire_midi_in_open(struct snd_rawmidi_substream * alsa_sub)114*4882a593Smuzhiyun static int usb6fire_midi_in_open(struct snd_rawmidi_substream *alsa_sub)
115*4882a593Smuzhiyun {
116*4882a593Smuzhiyun 	return 0;
117*4882a593Smuzhiyun }
118*4882a593Smuzhiyun 
usb6fire_midi_in_close(struct snd_rawmidi_substream * alsa_sub)119*4882a593Smuzhiyun static int usb6fire_midi_in_close(struct snd_rawmidi_substream *alsa_sub)
120*4882a593Smuzhiyun {
121*4882a593Smuzhiyun 	return 0;
122*4882a593Smuzhiyun }
123*4882a593Smuzhiyun 
usb6fire_midi_in_trigger(struct snd_rawmidi_substream * alsa_sub,int up)124*4882a593Smuzhiyun static void usb6fire_midi_in_trigger(
125*4882a593Smuzhiyun 		struct snd_rawmidi_substream *alsa_sub, int up)
126*4882a593Smuzhiyun {
127*4882a593Smuzhiyun 	struct midi_runtime *rt = alsa_sub->rmidi->private_data;
128*4882a593Smuzhiyun 	unsigned long flags;
129*4882a593Smuzhiyun 
130*4882a593Smuzhiyun 	spin_lock_irqsave(&rt->in_lock, flags);
131*4882a593Smuzhiyun 	if (up)
132*4882a593Smuzhiyun 		rt->in = alsa_sub;
133*4882a593Smuzhiyun 	else
134*4882a593Smuzhiyun 		rt->in = NULL;
135*4882a593Smuzhiyun 	spin_unlock_irqrestore(&rt->in_lock, flags);
136*4882a593Smuzhiyun }
137*4882a593Smuzhiyun 
138*4882a593Smuzhiyun static const struct snd_rawmidi_ops out_ops = {
139*4882a593Smuzhiyun 	.open = usb6fire_midi_out_open,
140*4882a593Smuzhiyun 	.close = usb6fire_midi_out_close,
141*4882a593Smuzhiyun 	.trigger = usb6fire_midi_out_trigger,
142*4882a593Smuzhiyun 	.drain = usb6fire_midi_out_drain
143*4882a593Smuzhiyun };
144*4882a593Smuzhiyun 
145*4882a593Smuzhiyun static const struct snd_rawmidi_ops in_ops = {
146*4882a593Smuzhiyun 	.open = usb6fire_midi_in_open,
147*4882a593Smuzhiyun 	.close = usb6fire_midi_in_close,
148*4882a593Smuzhiyun 	.trigger = usb6fire_midi_in_trigger
149*4882a593Smuzhiyun };
150*4882a593Smuzhiyun 
usb6fire_midi_init(struct sfire_chip * chip)151*4882a593Smuzhiyun int usb6fire_midi_init(struct sfire_chip *chip)
152*4882a593Smuzhiyun {
153*4882a593Smuzhiyun 	int ret;
154*4882a593Smuzhiyun 	struct midi_runtime *rt = kzalloc(sizeof(struct midi_runtime),
155*4882a593Smuzhiyun 			GFP_KERNEL);
156*4882a593Smuzhiyun 	struct comm_runtime *comm_rt = chip->comm;
157*4882a593Smuzhiyun 
158*4882a593Smuzhiyun 	if (!rt)
159*4882a593Smuzhiyun 		return -ENOMEM;
160*4882a593Smuzhiyun 
161*4882a593Smuzhiyun 	rt->out_buffer = kzalloc(MIDI_BUFSIZE, GFP_KERNEL);
162*4882a593Smuzhiyun 	if (!rt->out_buffer) {
163*4882a593Smuzhiyun 		kfree(rt);
164*4882a593Smuzhiyun 		return -ENOMEM;
165*4882a593Smuzhiyun 	}
166*4882a593Smuzhiyun 
167*4882a593Smuzhiyun 	rt->chip = chip;
168*4882a593Smuzhiyun 	rt->in_received = usb6fire_midi_in_received;
169*4882a593Smuzhiyun 	rt->out_buffer[0] = 0x80; /* 'send midi' command */
170*4882a593Smuzhiyun 	rt->out_buffer[1] = 0x00; /* size of data */
171*4882a593Smuzhiyun 	rt->out_buffer[2] = 0x00; /* always 0 */
172*4882a593Smuzhiyun 	spin_lock_init(&rt->in_lock);
173*4882a593Smuzhiyun 	spin_lock_init(&rt->out_lock);
174*4882a593Smuzhiyun 
175*4882a593Smuzhiyun 	comm_rt->init_urb(comm_rt, &rt->out_urb, rt->out_buffer, rt,
176*4882a593Smuzhiyun 			usb6fire_midi_out_handler);
177*4882a593Smuzhiyun 
178*4882a593Smuzhiyun 	ret = snd_rawmidi_new(chip->card, "6FireUSB", 0, 1, 1, &rt->instance);
179*4882a593Smuzhiyun 	if (ret < 0) {
180*4882a593Smuzhiyun 		kfree(rt->out_buffer);
181*4882a593Smuzhiyun 		kfree(rt);
182*4882a593Smuzhiyun 		dev_err(&chip->dev->dev, "unable to create midi.\n");
183*4882a593Smuzhiyun 		return ret;
184*4882a593Smuzhiyun 	}
185*4882a593Smuzhiyun 	rt->instance->private_data = rt;
186*4882a593Smuzhiyun 	strcpy(rt->instance->name, "DMX6FireUSB MIDI");
187*4882a593Smuzhiyun 	rt->instance->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
188*4882a593Smuzhiyun 			SNDRV_RAWMIDI_INFO_INPUT |
189*4882a593Smuzhiyun 			SNDRV_RAWMIDI_INFO_DUPLEX;
190*4882a593Smuzhiyun 	snd_rawmidi_set_ops(rt->instance, SNDRV_RAWMIDI_STREAM_OUTPUT,
191*4882a593Smuzhiyun 			&out_ops);
192*4882a593Smuzhiyun 	snd_rawmidi_set_ops(rt->instance, SNDRV_RAWMIDI_STREAM_INPUT,
193*4882a593Smuzhiyun 			&in_ops);
194*4882a593Smuzhiyun 
195*4882a593Smuzhiyun 	chip->midi = rt;
196*4882a593Smuzhiyun 	return 0;
197*4882a593Smuzhiyun }
198*4882a593Smuzhiyun 
usb6fire_midi_abort(struct sfire_chip * chip)199*4882a593Smuzhiyun void usb6fire_midi_abort(struct sfire_chip *chip)
200*4882a593Smuzhiyun {
201*4882a593Smuzhiyun 	struct midi_runtime *rt = chip->midi;
202*4882a593Smuzhiyun 
203*4882a593Smuzhiyun 	if (rt)
204*4882a593Smuzhiyun 		usb_poison_urb(&rt->out_urb);
205*4882a593Smuzhiyun }
206*4882a593Smuzhiyun 
usb6fire_midi_destroy(struct sfire_chip * chip)207*4882a593Smuzhiyun void usb6fire_midi_destroy(struct sfire_chip *chip)
208*4882a593Smuzhiyun {
209*4882a593Smuzhiyun 	struct midi_runtime *rt = chip->midi;
210*4882a593Smuzhiyun 
211*4882a593Smuzhiyun 	kfree(rt->out_buffer);
212*4882a593Smuzhiyun 	kfree(rt);
213*4882a593Smuzhiyun 	chip->midi = NULL;
214*4882a593Smuzhiyun }
215