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