Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/sound/usb/6fire/midi.c
10817 views
1
/*
2
* Linux driver for TerraTec DMX 6Fire USB
3
*
4
* Rawmidi driver
5
*
6
* Author: Torsten Schenk <[email protected]>
7
* Created: Jan 01, 2011
8
* Version: 0.3.0
9
* Copyright: (C) Torsten Schenk
10
*
11
* This program is free software; you can redistribute it and/or modify
12
* it under the terms of the GNU General Public License as published by
13
* the Free Software Foundation; either version 2 of the License, or
14
* (at your option) any later version.
15
*/
16
17
#include <sound/rawmidi.h>
18
19
#include "midi.h"
20
#include "chip.h"
21
#include "comm.h"
22
23
static void usb6fire_midi_out_handler(struct urb *urb)
24
{
25
struct midi_runtime *rt = urb->context;
26
int ret;
27
unsigned long flags;
28
29
spin_lock_irqsave(&rt->out_lock, flags);
30
31
if (rt->out) {
32
ret = snd_rawmidi_transmit(rt->out, rt->out_buffer + 4,
33
MIDI_BUFSIZE - 4);
34
if (ret > 0) { /* more data available, send next packet */
35
rt->out_buffer[1] = ret + 2;
36
rt->out_buffer[3] = rt->out_serial++;
37
urb->transfer_buffer_length = ret + 4;
38
39
ret = usb_submit_urb(urb, GFP_ATOMIC);
40
if (ret < 0)
41
snd_printk(KERN_ERR PREFIX "midi out urb "
42
"submit failed: %d\n", 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
snd_printk(KERN_ERR PREFIX "midi out urb "
95
"submit failed: %d\n", ret);
96
else
97
rt->out = alsa_sub;
98
}
99
} else if (rt->out == alsa_sub)
100
rt->out = NULL;
101
spin_unlock_irqrestore(&rt->out_lock, flags);
102
}
103
104
static void usb6fire_midi_out_drain(struct snd_rawmidi_substream *alsa_sub)
105
{
106
struct midi_runtime *rt = alsa_sub->rmidi->private_data;
107
int retry = 0;
108
109
while (rt->out && retry++ < 100)
110
msleep(10);
111
}
112
113
static int usb6fire_midi_in_open(struct snd_rawmidi_substream *alsa_sub)
114
{
115
return 0;
116
}
117
118
static int usb6fire_midi_in_close(struct snd_rawmidi_substream *alsa_sub)
119
{
120
return 0;
121
}
122
123
static void usb6fire_midi_in_trigger(
124
struct snd_rawmidi_substream *alsa_sub, int up)
125
{
126
struct midi_runtime *rt = alsa_sub->rmidi->private_data;
127
unsigned long flags;
128
129
spin_lock_irqsave(&rt->in_lock, flags);
130
if (up)
131
rt->in = alsa_sub;
132
else
133
rt->in = NULL;
134
spin_unlock_irqrestore(&rt->in_lock, flags);
135
}
136
137
static struct snd_rawmidi_ops out_ops = {
138
.open = usb6fire_midi_out_open,
139
.close = usb6fire_midi_out_close,
140
.trigger = usb6fire_midi_out_trigger,
141
.drain = usb6fire_midi_out_drain
142
};
143
144
static struct snd_rawmidi_ops in_ops = {
145
.open = usb6fire_midi_in_open,
146
.close = usb6fire_midi_in_close,
147
.trigger = usb6fire_midi_in_trigger
148
};
149
150
int __devinit usb6fire_midi_init(struct sfire_chip *chip)
151
{
152
int ret;
153
struct midi_runtime *rt = kzalloc(sizeof(struct midi_runtime),
154
GFP_KERNEL);
155
struct comm_runtime *comm_rt = chip->comm;
156
157
if (!rt)
158
return -ENOMEM;
159
160
rt->chip = chip;
161
rt->in_received = usb6fire_midi_in_received;
162
rt->out_buffer[0] = 0x80; /* 'send midi' command */
163
rt->out_buffer[1] = 0x00; /* size of data */
164
rt->out_buffer[2] = 0x00; /* always 0 */
165
spin_lock_init(&rt->in_lock);
166
spin_lock_init(&rt->out_lock);
167
168
comm_rt->init_urb(comm_rt, &rt->out_urb, rt->out_buffer, rt,
169
usb6fire_midi_out_handler);
170
171
ret = snd_rawmidi_new(chip->card, "6FireUSB", 0, 1, 1, &rt->instance);
172
if (ret < 0) {
173
kfree(rt);
174
snd_printk(KERN_ERR PREFIX "unable to create midi.\n");
175
return ret;
176
}
177
rt->instance->private_data = rt;
178
strcpy(rt->instance->name, "DMX6FireUSB MIDI");
179
rt->instance->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
180
SNDRV_RAWMIDI_INFO_INPUT |
181
SNDRV_RAWMIDI_INFO_DUPLEX;
182
snd_rawmidi_set_ops(rt->instance, SNDRV_RAWMIDI_STREAM_OUTPUT,
183
&out_ops);
184
snd_rawmidi_set_ops(rt->instance, SNDRV_RAWMIDI_STREAM_INPUT,
185
&in_ops);
186
187
chip->midi = rt;
188
return 0;
189
}
190
191
void usb6fire_midi_abort(struct sfire_chip *chip)
192
{
193
struct midi_runtime *rt = chip->midi;
194
195
if (rt)
196
usb_poison_urb(&rt->out_urb);
197
}
198
199
void usb6fire_midi_destroy(struct sfire_chip *chip)
200
{
201
kfree(chip->midi);
202
chip->midi = NULL;
203
}
204
205