Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/sound/pci/ca0106/ca_midi.c
10817 views
1
/*
2
* Copyright 10/16/2005 Tilman Kranz <[email protected]>
3
* Creative Audio MIDI, for the CA0106 Driver
4
* Version: 0.0.1
5
*
6
* Changelog:
7
* Implementation is based on mpu401 and emu10k1x and
8
* tested with ca0106.
9
* mpu401: Copyright (c) by Jaroslav Kysela <[email protected]>
10
* emu10k1x: Copyright (c) by Francisco Moraes <[email protected]>
11
*
12
* This program is free software; you can redistribute it and/or modify
13
* it under the terms of the GNU General Public License as published by
14
* the Free Software Foundation; either version 2 of the License, or
15
* (at your option) any later version.
16
*
17
* This program is distributed in the hope that it will be useful,
18
* but WITHOUT ANY WARRANTY; without even the implied warranty of
19
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
* GNU General Public License for more details.
21
*
22
* You should have received a copy of the GNU General Public License
23
* along with this program; if not, write to the Free Software
24
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25
*
26
*
27
*/
28
29
#include <linux/spinlock.h>
30
#include <sound/core.h>
31
#include <sound/rawmidi.h>
32
33
#include "ca_midi.h"
34
35
#define ca_midi_write_data(midi, data) midi->write(midi, data, 0)
36
#define ca_midi_write_cmd(midi, data) midi->write(midi, data, 1)
37
#define ca_midi_read_data(midi) midi->read(midi, 0)
38
#define ca_midi_read_stat(midi) midi->read(midi, 1)
39
#define ca_midi_input_avail(midi) (!(ca_midi_read_stat(midi) & midi->input_avail))
40
#define ca_midi_output_ready(midi) (!(ca_midi_read_stat(midi) & midi->output_ready))
41
42
static void ca_midi_clear_rx(struct snd_ca_midi *midi)
43
{
44
int timeout = 100000;
45
for (; timeout > 0 && ca_midi_input_avail(midi); timeout--)
46
ca_midi_read_data(midi);
47
#ifdef CONFIG_SND_DEBUG
48
if (timeout <= 0)
49
snd_printk(KERN_ERR "ca_midi_clear_rx: timeout (status = 0x%x)\n",
50
ca_midi_read_stat(midi));
51
#endif
52
}
53
54
static void ca_midi_interrupt(struct snd_ca_midi *midi, unsigned int status)
55
{
56
unsigned char byte;
57
58
if (midi->rmidi == NULL) {
59
midi->interrupt_disable(midi,midi->tx_enable | midi->rx_enable);
60
return;
61
}
62
63
spin_lock(&midi->input_lock);
64
if ((status & midi->ipr_rx) && ca_midi_input_avail(midi)) {
65
if (!(midi->midi_mode & CA_MIDI_MODE_INPUT)) {
66
ca_midi_clear_rx(midi);
67
} else {
68
byte = ca_midi_read_data(midi);
69
if(midi->substream_input)
70
snd_rawmidi_receive(midi->substream_input, &byte, 1);
71
72
73
}
74
}
75
spin_unlock(&midi->input_lock);
76
77
spin_lock(&midi->output_lock);
78
if ((status & midi->ipr_tx) && ca_midi_output_ready(midi)) {
79
if (midi->substream_output &&
80
snd_rawmidi_transmit(midi->substream_output, &byte, 1) == 1) {
81
ca_midi_write_data(midi, byte);
82
} else {
83
midi->interrupt_disable(midi,midi->tx_enable);
84
}
85
}
86
spin_unlock(&midi->output_lock);
87
88
}
89
90
static void ca_midi_cmd(struct snd_ca_midi *midi, unsigned char cmd, int ack)
91
{
92
unsigned long flags;
93
int timeout, ok;
94
95
spin_lock_irqsave(&midi->input_lock, flags);
96
ca_midi_write_data(midi, 0x00);
97
/* ca_midi_clear_rx(midi); */
98
99
ca_midi_write_cmd(midi, cmd);
100
if (ack) {
101
ok = 0;
102
timeout = 10000;
103
while (!ok && timeout-- > 0) {
104
if (ca_midi_input_avail(midi)) {
105
if (ca_midi_read_data(midi) == midi->ack)
106
ok = 1;
107
}
108
}
109
if (!ok && ca_midi_read_data(midi) == midi->ack)
110
ok = 1;
111
} else {
112
ok = 1;
113
}
114
spin_unlock_irqrestore(&midi->input_lock, flags);
115
if (!ok)
116
snd_printk(KERN_ERR "ca_midi_cmd: 0x%x failed at 0x%x (status = 0x%x, data = 0x%x)!!!\n",
117
cmd,
118
midi->get_dev_id_port(midi->dev_id),
119
ca_midi_read_stat(midi),
120
ca_midi_read_data(midi));
121
}
122
123
static int ca_midi_input_open(struct snd_rawmidi_substream *substream)
124
{
125
struct snd_ca_midi *midi = substream->rmidi->private_data;
126
unsigned long flags;
127
128
if (snd_BUG_ON(!midi->dev_id))
129
return -ENXIO;
130
spin_lock_irqsave(&midi->open_lock, flags);
131
midi->midi_mode |= CA_MIDI_MODE_INPUT;
132
midi->substream_input = substream;
133
if (!(midi->midi_mode & CA_MIDI_MODE_OUTPUT)) {
134
spin_unlock_irqrestore(&midi->open_lock, flags);
135
ca_midi_cmd(midi, midi->reset, 1);
136
ca_midi_cmd(midi, midi->enter_uart, 1);
137
} else {
138
spin_unlock_irqrestore(&midi->open_lock, flags);
139
}
140
return 0;
141
}
142
143
static int ca_midi_output_open(struct snd_rawmidi_substream *substream)
144
{
145
struct snd_ca_midi *midi = substream->rmidi->private_data;
146
unsigned long flags;
147
148
if (snd_BUG_ON(!midi->dev_id))
149
return -ENXIO;
150
spin_lock_irqsave(&midi->open_lock, flags);
151
midi->midi_mode |= CA_MIDI_MODE_OUTPUT;
152
midi->substream_output = substream;
153
if (!(midi->midi_mode & CA_MIDI_MODE_INPUT)) {
154
spin_unlock_irqrestore(&midi->open_lock, flags);
155
ca_midi_cmd(midi, midi->reset, 1);
156
ca_midi_cmd(midi, midi->enter_uart, 1);
157
} else {
158
spin_unlock_irqrestore(&midi->open_lock, flags);
159
}
160
return 0;
161
}
162
163
static int ca_midi_input_close(struct snd_rawmidi_substream *substream)
164
{
165
struct snd_ca_midi *midi = substream->rmidi->private_data;
166
unsigned long flags;
167
168
if (snd_BUG_ON(!midi->dev_id))
169
return -ENXIO;
170
spin_lock_irqsave(&midi->open_lock, flags);
171
midi->interrupt_disable(midi,midi->rx_enable);
172
midi->midi_mode &= ~CA_MIDI_MODE_INPUT;
173
midi->substream_input = NULL;
174
if (!(midi->midi_mode & CA_MIDI_MODE_OUTPUT)) {
175
spin_unlock_irqrestore(&midi->open_lock, flags);
176
ca_midi_cmd(midi, midi->reset, 0);
177
} else {
178
spin_unlock_irqrestore(&midi->open_lock, flags);
179
}
180
return 0;
181
}
182
183
static int ca_midi_output_close(struct snd_rawmidi_substream *substream)
184
{
185
struct snd_ca_midi *midi = substream->rmidi->private_data;
186
unsigned long flags;
187
188
if (snd_BUG_ON(!midi->dev_id))
189
return -ENXIO;
190
191
spin_lock_irqsave(&midi->open_lock, flags);
192
193
midi->interrupt_disable(midi,midi->tx_enable);
194
midi->midi_mode &= ~CA_MIDI_MODE_OUTPUT;
195
midi->substream_output = NULL;
196
197
if (!(midi->midi_mode & CA_MIDI_MODE_INPUT)) {
198
spin_unlock_irqrestore(&midi->open_lock, flags);
199
ca_midi_cmd(midi, midi->reset, 0);
200
} else {
201
spin_unlock_irqrestore(&midi->open_lock, flags);
202
}
203
return 0;
204
}
205
206
static void ca_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
207
{
208
struct snd_ca_midi *midi = substream->rmidi->private_data;
209
210
if (snd_BUG_ON(!midi->dev_id))
211
return;
212
213
if (up) {
214
midi->interrupt_enable(midi,midi->rx_enable);
215
} else {
216
midi->interrupt_disable(midi, midi->rx_enable);
217
}
218
}
219
220
static void ca_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
221
{
222
struct snd_ca_midi *midi = substream->rmidi->private_data;
223
unsigned long flags;
224
225
if (snd_BUG_ON(!midi->dev_id))
226
return;
227
228
if (up) {
229
int max = 4;
230
unsigned char byte;
231
232
spin_lock_irqsave(&midi->output_lock, flags);
233
234
/* try to send some amount of bytes here before interrupts */
235
while (max > 0) {
236
if (ca_midi_output_ready(midi)) {
237
if (!(midi->midi_mode & CA_MIDI_MODE_OUTPUT) ||
238
snd_rawmidi_transmit(substream, &byte, 1) != 1) {
239
/* no more data */
240
spin_unlock_irqrestore(&midi->output_lock, flags);
241
return;
242
}
243
ca_midi_write_data(midi, byte);
244
max--;
245
} else {
246
break;
247
}
248
}
249
250
spin_unlock_irqrestore(&midi->output_lock, flags);
251
midi->interrupt_enable(midi,midi->tx_enable);
252
253
} else {
254
midi->interrupt_disable(midi,midi->tx_enable);
255
}
256
}
257
258
static struct snd_rawmidi_ops ca_midi_output =
259
{
260
.open = ca_midi_output_open,
261
.close = ca_midi_output_close,
262
.trigger = ca_midi_output_trigger,
263
};
264
265
static struct snd_rawmidi_ops ca_midi_input =
266
{
267
.open = ca_midi_input_open,
268
.close = ca_midi_input_close,
269
.trigger = ca_midi_input_trigger,
270
};
271
272
static void ca_midi_free(struct snd_ca_midi *midi)
273
{
274
midi->interrupt = NULL;
275
midi->interrupt_enable = NULL;
276
midi->interrupt_disable = NULL;
277
midi->read = NULL;
278
midi->write = NULL;
279
midi->get_dev_id_card = NULL;
280
midi->get_dev_id_port = NULL;
281
midi->rmidi = NULL;
282
}
283
284
static void ca_rmidi_free(struct snd_rawmidi *rmidi)
285
{
286
ca_midi_free(rmidi->private_data);
287
}
288
289
int __devinit ca_midi_init(void *dev_id, struct snd_ca_midi *midi, int device, char *name)
290
{
291
struct snd_rawmidi *rmidi;
292
int err;
293
294
if ((err = snd_rawmidi_new(midi->get_dev_id_card(midi->dev_id), name, device, 1, 1, &rmidi)) < 0)
295
return err;
296
297
midi->dev_id = dev_id;
298
midi->interrupt = ca_midi_interrupt;
299
300
spin_lock_init(&midi->open_lock);
301
spin_lock_init(&midi->input_lock);
302
spin_lock_init(&midi->output_lock);
303
304
strcpy(rmidi->name, name);
305
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &ca_midi_output);
306
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &ca_midi_input);
307
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT |
308
SNDRV_RAWMIDI_INFO_INPUT |
309
SNDRV_RAWMIDI_INFO_DUPLEX;
310
rmidi->private_data = midi;
311
rmidi->private_free = ca_rmidi_free;
312
313
midi->rmidi = rmidi;
314
return 0;
315
}
316
317
318