]>
Commit | Line | Data |
---|---|---|
1a59d1b8 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
8a5afd29 JCD |
2 | /* |
3 | * Copyright 10/16/2005 Tilman Kranz <tilde@tk-sls.de> | |
4 | * Creative Audio MIDI, for the CA0106 Driver | |
5 | * Version: 0.0.1 | |
6 | * | |
7 | * Changelog: | |
8 | * Implementation is based on mpu401 and emu10k1x and | |
9 | * tested with ca0106. | |
c1017a4c | 10 | * mpu401: Copyright (c) by Jaroslav Kysela <perex@perex.cz> |
7b4260f2 | 11 | * emu10k1x: Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com> |
8a5afd29 JCD |
12 | */ |
13 | ||
14 | #include <linux/spinlock.h> | |
8a5afd29 JCD |
15 | #include <sound/core.h> |
16 | #include <sound/rawmidi.h> | |
17 | ||
18 | #include "ca_midi.h" | |
19 | ||
20 | #define ca_midi_write_data(midi, data) midi->write(midi, data, 0) | |
21 | #define ca_midi_write_cmd(midi, data) midi->write(midi, data, 1) | |
22 | #define ca_midi_read_data(midi) midi->read(midi, 0) | |
23 | #define ca_midi_read_stat(midi) midi->read(midi, 1) | |
24 | #define ca_midi_input_avail(midi) (!(ca_midi_read_stat(midi) & midi->input_avail)) | |
25 | #define ca_midi_output_ready(midi) (!(ca_midi_read_stat(midi) & midi->output_ready)) | |
26 | ||
e4a3d145 | 27 | static void ca_midi_clear_rx(struct snd_ca_midi *midi) |
8a5afd29 JCD |
28 | { |
29 | int timeout = 100000; | |
30 | for (; timeout > 0 && ca_midi_input_avail(midi); timeout--) | |
31 | ca_midi_read_data(midi); | |
32 | #ifdef CONFIG_SND_DEBUG | |
33 | if (timeout <= 0) | |
74103227 | 34 | pr_err("ca_midi_clear_rx: timeout (status = 0x%x)\n", |
e4a3d145 | 35 | ca_midi_read_stat(midi)); |
8a5afd29 JCD |
36 | #endif |
37 | } | |
38 | ||
e4a3d145 TI |
39 | static void ca_midi_interrupt(struct snd_ca_midi *midi, unsigned int status) |
40 | { | |
8a5afd29 JCD |
41 | unsigned char byte; |
42 | ||
43 | if (midi->rmidi == NULL) { | |
44 | midi->interrupt_disable(midi,midi->tx_enable | midi->rx_enable); | |
45 | return; | |
46 | } | |
47 | ||
48 | spin_lock(&midi->input_lock); | |
49 | if ((status & midi->ipr_rx) && ca_midi_input_avail(midi)) { | |
50 | if (!(midi->midi_mode & CA_MIDI_MODE_INPUT)) { | |
51 | ca_midi_clear_rx(midi); | |
52 | } else { | |
53 | byte = ca_midi_read_data(midi); | |
54 | if(midi->substream_input) | |
55 | snd_rawmidi_receive(midi->substream_input, &byte, 1); | |
56 | ||
57 | ||
58 | } | |
59 | } | |
60 | spin_unlock(&midi->input_lock); | |
61 | ||
62 | spin_lock(&midi->output_lock); | |
63 | if ((status & midi->ipr_tx) && ca_midi_output_ready(midi)) { | |
64 | if (midi->substream_output && | |
65 | snd_rawmidi_transmit(midi->substream_output, &byte, 1) == 1) { | |
66 | ca_midi_write_data(midi, byte); | |
67 | } else { | |
68 | midi->interrupt_disable(midi,midi->tx_enable); | |
69 | } | |
70 | } | |
71 | spin_unlock(&midi->output_lock); | |
72 | ||
73 | } | |
74 | ||
e4a3d145 | 75 | static void ca_midi_cmd(struct snd_ca_midi *midi, unsigned char cmd, int ack) |
8a5afd29 JCD |
76 | { |
77 | unsigned long flags; | |
78 | int timeout, ok; | |
79 | ||
80 | spin_lock_irqsave(&midi->input_lock, flags); | |
81 | ca_midi_write_data(midi, 0x00); | |
82 | /* ca_midi_clear_rx(midi); */ | |
83 | ||
84 | ca_midi_write_cmd(midi, cmd); | |
85 | if (ack) { | |
86 | ok = 0; | |
87 | timeout = 10000; | |
88 | while (!ok && timeout-- > 0) { | |
89 | if (ca_midi_input_avail(midi)) { | |
90 | if (ca_midi_read_data(midi) == midi->ack) | |
91 | ok = 1; | |
92 | } | |
93 | } | |
94 | if (!ok && ca_midi_read_data(midi) == midi->ack) | |
95 | ok = 1; | |
96 | } else { | |
97 | ok = 1; | |
98 | } | |
99 | spin_unlock_irqrestore(&midi->input_lock, flags); | |
100 | if (!ok) | |
74103227 | 101 | pr_err("ca_midi_cmd: 0x%x failed at 0x%x (status = 0x%x, data = 0x%x)!!!\n", |
8a5afd29 JCD |
102 | cmd, |
103 | midi->get_dev_id_port(midi->dev_id), | |
104 | ca_midi_read_stat(midi), | |
105 | ca_midi_read_data(midi)); | |
106 | } | |
107 | ||
e4a3d145 | 108 | static int ca_midi_input_open(struct snd_rawmidi_substream *substream) |
8a5afd29 | 109 | { |
e4a3d145 | 110 | struct snd_ca_midi *midi = substream->rmidi->private_data; |
8a5afd29 JCD |
111 | unsigned long flags; |
112 | ||
da3cec35 TI |
113 | if (snd_BUG_ON(!midi->dev_id)) |
114 | return -ENXIO; | |
8a5afd29 JCD |
115 | spin_lock_irqsave(&midi->open_lock, flags); |
116 | midi->midi_mode |= CA_MIDI_MODE_INPUT; | |
117 | midi->substream_input = substream; | |
118 | if (!(midi->midi_mode & CA_MIDI_MODE_OUTPUT)) { | |
119 | spin_unlock_irqrestore(&midi->open_lock, flags); | |
120 | ca_midi_cmd(midi, midi->reset, 1); | |
121 | ca_midi_cmd(midi, midi->enter_uart, 1); | |
122 | } else { | |
123 | spin_unlock_irqrestore(&midi->open_lock, flags); | |
124 | } | |
125 | return 0; | |
126 | } | |
127 | ||
e4a3d145 | 128 | static int ca_midi_output_open(struct snd_rawmidi_substream *substream) |
8a5afd29 | 129 | { |
e4a3d145 | 130 | struct snd_ca_midi *midi = substream->rmidi->private_data; |
8a5afd29 JCD |
131 | unsigned long flags; |
132 | ||
da3cec35 TI |
133 | if (snd_BUG_ON(!midi->dev_id)) |
134 | return -ENXIO; | |
8a5afd29 JCD |
135 | spin_lock_irqsave(&midi->open_lock, flags); |
136 | midi->midi_mode |= CA_MIDI_MODE_OUTPUT; | |
137 | midi->substream_output = substream; | |
138 | if (!(midi->midi_mode & CA_MIDI_MODE_INPUT)) { | |
139 | spin_unlock_irqrestore(&midi->open_lock, flags); | |
140 | ca_midi_cmd(midi, midi->reset, 1); | |
141 | ca_midi_cmd(midi, midi->enter_uart, 1); | |
142 | } else { | |
143 | spin_unlock_irqrestore(&midi->open_lock, flags); | |
144 | } | |
145 | return 0; | |
146 | } | |
147 | ||
e4a3d145 | 148 | static int ca_midi_input_close(struct snd_rawmidi_substream *substream) |
8a5afd29 | 149 | { |
e4a3d145 | 150 | struct snd_ca_midi *midi = substream->rmidi->private_data; |
8a5afd29 JCD |
151 | unsigned long flags; |
152 | ||
da3cec35 TI |
153 | if (snd_BUG_ON(!midi->dev_id)) |
154 | return -ENXIO; | |
8a5afd29 JCD |
155 | spin_lock_irqsave(&midi->open_lock, flags); |
156 | midi->interrupt_disable(midi,midi->rx_enable); | |
157 | midi->midi_mode &= ~CA_MIDI_MODE_INPUT; | |
158 | midi->substream_input = NULL; | |
159 | if (!(midi->midi_mode & CA_MIDI_MODE_OUTPUT)) { | |
160 | spin_unlock_irqrestore(&midi->open_lock, flags); | |
161 | ca_midi_cmd(midi, midi->reset, 0); | |
162 | } else { | |
163 | spin_unlock_irqrestore(&midi->open_lock, flags); | |
164 | } | |
165 | return 0; | |
166 | } | |
167 | ||
e4a3d145 | 168 | static int ca_midi_output_close(struct snd_rawmidi_substream *substream) |
8a5afd29 | 169 | { |
e4a3d145 | 170 | struct snd_ca_midi *midi = substream->rmidi->private_data; |
8a5afd29 | 171 | unsigned long flags; |
da3cec35 TI |
172 | |
173 | if (snd_BUG_ON(!midi->dev_id)) | |
174 | return -ENXIO; | |
8a5afd29 JCD |
175 | |
176 | spin_lock_irqsave(&midi->open_lock, flags); | |
177 | ||
178 | midi->interrupt_disable(midi,midi->tx_enable); | |
179 | midi->midi_mode &= ~CA_MIDI_MODE_OUTPUT; | |
180 | midi->substream_output = NULL; | |
181 | ||
182 | if (!(midi->midi_mode & CA_MIDI_MODE_INPUT)) { | |
183 | spin_unlock_irqrestore(&midi->open_lock, flags); | |
184 | ca_midi_cmd(midi, midi->reset, 0); | |
185 | } else { | |
186 | spin_unlock_irqrestore(&midi->open_lock, flags); | |
187 | } | |
188 | return 0; | |
189 | } | |
190 | ||
e4a3d145 | 191 | static void ca_midi_input_trigger(struct snd_rawmidi_substream *substream, int up) |
8a5afd29 | 192 | { |
e4a3d145 | 193 | struct snd_ca_midi *midi = substream->rmidi->private_data; |
da3cec35 TI |
194 | |
195 | if (snd_BUG_ON(!midi->dev_id)) | |
196 | return; | |
8a5afd29 JCD |
197 | |
198 | if (up) { | |
199 | midi->interrupt_enable(midi,midi->rx_enable); | |
200 | } else { | |
201 | midi->interrupt_disable(midi, midi->rx_enable); | |
202 | } | |
203 | } | |
204 | ||
e4a3d145 | 205 | static void ca_midi_output_trigger(struct snd_rawmidi_substream *substream, int up) |
8a5afd29 | 206 | { |
e4a3d145 | 207 | struct snd_ca_midi *midi = substream->rmidi->private_data; |
8a5afd29 JCD |
208 | unsigned long flags; |
209 | ||
da3cec35 TI |
210 | if (snd_BUG_ON(!midi->dev_id)) |
211 | return; | |
8a5afd29 JCD |
212 | |
213 | if (up) { | |
214 | int max = 4; | |
215 | unsigned char byte; | |
216 | ||
217 | spin_lock_irqsave(&midi->output_lock, flags); | |
218 | ||
219 | /* try to send some amount of bytes here before interrupts */ | |
220 | while (max > 0) { | |
221 | if (ca_midi_output_ready(midi)) { | |
222 | if (!(midi->midi_mode & CA_MIDI_MODE_OUTPUT) || | |
223 | snd_rawmidi_transmit(substream, &byte, 1) != 1) { | |
224 | /* no more data */ | |
225 | spin_unlock_irqrestore(&midi->output_lock, flags); | |
226 | return; | |
227 | } | |
228 | ca_midi_write_data(midi, byte); | |
229 | max--; | |
230 | } else { | |
231 | break; | |
232 | } | |
233 | } | |
234 | ||
235 | spin_unlock_irqrestore(&midi->output_lock, flags); | |
236 | midi->interrupt_enable(midi,midi->tx_enable); | |
237 | ||
238 | } else { | |
239 | midi->interrupt_disable(midi,midi->tx_enable); | |
240 | } | |
241 | } | |
242 | ||
485885b9 | 243 | static const struct snd_rawmidi_ops ca_midi_output = |
8a5afd29 JCD |
244 | { |
245 | .open = ca_midi_output_open, | |
246 | .close = ca_midi_output_close, | |
247 | .trigger = ca_midi_output_trigger, | |
248 | }; | |
249 | ||
485885b9 | 250 | static const struct snd_rawmidi_ops ca_midi_input = |
8a5afd29 JCD |
251 | { |
252 | .open = ca_midi_input_open, | |
253 | .close = ca_midi_input_close, | |
254 | .trigger = ca_midi_input_trigger, | |
255 | }; | |
256 | ||
e4a3d145 TI |
257 | static void ca_midi_free(struct snd_ca_midi *midi) |
258 | { | |
8a5afd29 JCD |
259 | midi->interrupt = NULL; |
260 | midi->interrupt_enable = NULL; | |
261 | midi->interrupt_disable = NULL; | |
262 | midi->read = NULL; | |
263 | midi->write = NULL; | |
264 | midi->get_dev_id_card = NULL; | |
265 | midi->get_dev_id_port = NULL; | |
266 | midi->rmidi = NULL; | |
267 | } | |
268 | ||
e4a3d145 | 269 | static void ca_rmidi_free(struct snd_rawmidi *rmidi) |
8a5afd29 | 270 | { |
e4a3d145 | 271 | ca_midi_free(rmidi->private_data); |
8a5afd29 JCD |
272 | } |
273 | ||
e23e7a14 | 274 | int ca_midi_init(void *dev_id, struct snd_ca_midi *midi, int device, char *name) |
8a5afd29 | 275 | { |
e4a3d145 | 276 | struct snd_rawmidi *rmidi; |
8a5afd29 JCD |
277 | int err; |
278 | ||
279 | if ((err = snd_rawmidi_new(midi->get_dev_id_card(midi->dev_id), name, device, 1, 1, &rmidi)) < 0) | |
280 | return err; | |
281 | ||
282 | midi->dev_id = dev_id; | |
283 | midi->interrupt = ca_midi_interrupt; | |
284 | ||
285 | spin_lock_init(&midi->open_lock); | |
286 | spin_lock_init(&midi->input_lock); | |
287 | spin_lock_init(&midi->output_lock); | |
288 | ||
289 | strcpy(rmidi->name, name); | |
290 | snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &ca_midi_output); | |
291 | snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &ca_midi_input); | |
292 | rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | | |
293 | SNDRV_RAWMIDI_INFO_INPUT | | |
294 | SNDRV_RAWMIDI_INFO_DUPLEX; | |
295 | rmidi->private_data = midi; | |
296 | rmidi->private_free = ca_rmidi_free; | |
297 | ||
298 | midi->rmidi = rmidi; | |
299 | return 0; | |
300 | } | |
301 |