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