Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/usb/6fire/midi.c
26424 views
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 <[email protected]>
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
strscpy(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
}
215
216