File Index
Files and directories
Sign in
FILE

ca_midi.c

File preview with syntax highlighting.

linux-6.19.14/sound/pci/ca0106/ca_midi.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  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.
 10 *    mpu401: Copyright (c) by Jaroslav Kysela <perex@perex.cz>
 11 *    emu10k1x: Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
 12 */
 13
 14#include <linux/spinlock.h>
 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
 27static void ca_midi_clear_rx(struct snd_ca_midi *midi)
 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)
 34		pr_err("ca_midi_clear_rx: timeout (status = 0x%x)\n",
 35			   ca_midi_read_stat(midi));
 36#endif
 37}
 38
 39static void ca_midi_interrupt(struct snd_ca_midi *midi, unsigned int status)
 40{
 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	scoped_guard(spinlock, &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	scoped_guard(spinlock, &midi->output_lock) {
 61		if ((status & midi->ipr_tx) && ca_midi_output_ready(midi)) {
 62			if (midi->substream_output &&
 63			    snd_rawmidi_transmit(midi->substream_output, &byte, 1) == 1) {
 64				ca_midi_write_data(midi, byte);
 65			} else {
 66				midi->interrupt_disable(midi, midi->tx_enable);
 67			}
 68		}
 69	}
 70}
 71
 72static void ca_midi_cmd(struct snd_ca_midi *midi, unsigned char cmd, int ack)
 73{
 74	int timeout, ok;
 75
 76	scoped_guard(spinlock_irqsave, &midi->input_lock) {
 77		ca_midi_write_data(midi, 0x00);
 78		/* ca_midi_clear_rx(midi); */
 79
 80		ca_midi_write_cmd(midi, cmd);
 81		if (ack) {
 82			ok = 0;
 83			timeout = 10000;
 84			while (!ok && timeout-- > 0) {
 85				if (ca_midi_input_avail(midi)) {
 86					if (ca_midi_read_data(midi) == midi->ack)
 87						ok = 1;
 88				}
 89			}
 90			if (!ok && ca_midi_read_data(midi) == midi->ack)
 91				ok = 1;
 92		} else {
 93			ok = 1;
 94		}
 95	}
 96	if (!ok)
 97		pr_err("ca_midi_cmd: 0x%x failed at 0x%x (status = 0x%x, data = 0x%x)!!!\n",
 98			   cmd,
 99			   midi->get_dev_id_port(midi->dev_id),
100			   ca_midi_read_stat(midi),
101			   ca_midi_read_data(midi));
102}
103
104static int ca_midi_input_open(struct snd_rawmidi_substream *substream)
105{
106	struct snd_ca_midi *midi = substream->rmidi->private_data;
107	
108	if (snd_BUG_ON(!midi->dev_id))
109		return -ENXIO;
110	scoped_guard(spinlock_irqsave, &midi->open_lock) {
111		midi->midi_mode |= CA_MIDI_MODE_INPUT;
112		midi->substream_input = substream;
113		if (midi->midi_mode & CA_MIDI_MODE_OUTPUT)
114			return 0;
115	}
116	ca_midi_cmd(midi, midi->reset, 1);
117	ca_midi_cmd(midi, midi->enter_uart, 1);
118	return 0;
119}
120
121static int ca_midi_output_open(struct snd_rawmidi_substream *substream)
122{
123	struct snd_ca_midi *midi = substream->rmidi->private_data;
124
125	if (snd_BUG_ON(!midi->dev_id))
126		return -ENXIO;
127	scoped_guard(spinlock_irqsave, &midi->open_lock) {
128		midi->midi_mode |= CA_MIDI_MODE_OUTPUT;
129		midi->substream_output = substream;
130		if (midi->midi_mode & CA_MIDI_MODE_INPUT)
131			return 0;
132	}
133	ca_midi_cmd(midi, midi->reset, 1);
134	ca_midi_cmd(midi, midi->enter_uart, 1);
135	return 0;
136}
137
138static int ca_midi_input_close(struct snd_rawmidi_substream *substream)
139{
140	struct snd_ca_midi *midi = substream->rmidi->private_data;
141
142	if (snd_BUG_ON(!midi->dev_id))
143		return -ENXIO;
144	scoped_guard(spinlock_irqsave, &midi->open_lock) {
145		midi->interrupt_disable(midi, midi->rx_enable);
146		midi->midi_mode &= ~CA_MIDI_MODE_INPUT;
147		midi->substream_input = NULL;
148		if (midi->midi_mode & CA_MIDI_MODE_OUTPUT)
149			return 0;
150	}
151	ca_midi_cmd(midi, midi->reset, 0);
152	return 0;
153}
154
155static int ca_midi_output_close(struct snd_rawmidi_substream *substream)
156{
157	struct snd_ca_midi *midi = substream->rmidi->private_data;
158
159	if (snd_BUG_ON(!midi->dev_id))
160		return -ENXIO;
161	
162	scoped_guard(spinlock_irqsave, &midi->open_lock) {
163		midi->interrupt_disable(midi, midi->tx_enable);
164		midi->midi_mode &= ~CA_MIDI_MODE_OUTPUT;
165		midi->substream_output = NULL;
166		if (midi->midi_mode & CA_MIDI_MODE_INPUT)
167			return 0;
168	}
169	ca_midi_cmd(midi, midi->reset, 0);
170	return 0;
171}
172
173static void ca_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
174{
175	struct snd_ca_midi *midi = substream->rmidi->private_data;
176
177	if (snd_BUG_ON(!midi->dev_id))
178		return;
179
180	if (up) {
181		midi->interrupt_enable(midi,midi->rx_enable);
182	} else {
183		midi->interrupt_disable(midi, midi->rx_enable);
184	}
185}
186
187static void ca_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
188{
189	struct snd_ca_midi *midi = substream->rmidi->private_data;
190
191	if (snd_BUG_ON(!midi->dev_id))
192		return;
193
194	if (up) {
195		int max = 4;
196		unsigned char byte;
197
198		scoped_guard(spinlock_irqsave, &midi->output_lock) {
199	
200			/* try to send some amount of bytes here before interrupts */
201			while (max > 0) {
202				if (ca_midi_output_ready(midi)) {
203					if (!(midi->midi_mode & CA_MIDI_MODE_OUTPUT) ||
204					    snd_rawmidi_transmit(substream, &byte, 1) != 1) {
205						/* no more data */
206						return;
207					}
208					ca_midi_write_data(midi, byte);
209					max--;
210				} else {
211					break;
212				}
213			}
214		}
215		midi->interrupt_enable(midi,midi->tx_enable);
216
217	} else {
218		midi->interrupt_disable(midi,midi->tx_enable);
219	}
220}
221
222static const struct snd_rawmidi_ops ca_midi_output =
223{
224	.open =		ca_midi_output_open,
225	.close =	ca_midi_output_close,
226	.trigger =	ca_midi_output_trigger,
227};
228
229static const struct snd_rawmidi_ops ca_midi_input =
230{
231	.open =		ca_midi_input_open,
232	.close =	ca_midi_input_close,
233	.trigger =	ca_midi_input_trigger,
234};
235
236static void ca_midi_free(struct snd_ca_midi *midi)
237{
238	midi->interrupt = NULL;
239	midi->interrupt_enable = NULL;
240	midi->interrupt_disable = NULL;
241	midi->read = NULL;
242	midi->write = NULL;
243	midi->get_dev_id_card = NULL;
244	midi->get_dev_id_port = NULL;
245	midi->rmidi = NULL;
246}
247
248static void ca_rmidi_free(struct snd_rawmidi *rmidi)
249{
250	ca_midi_free(rmidi->private_data);
251}
252
253int ca_midi_init(void *dev_id, struct snd_ca_midi *midi, int device, char *name)
254{
255	struct snd_rawmidi *rmidi;
256	int err;
257
258	err = snd_rawmidi_new(midi->get_dev_id_card(midi->dev_id), name, device, 1, 1, &rmidi);
259	if (err < 0)
260		return err;
261
262	midi->dev_id = dev_id;
263	midi->interrupt = ca_midi_interrupt;
264
265	spin_lock_init(&midi->open_lock);
266	spin_lock_init(&midi->input_lock);
267	spin_lock_init(&midi->output_lock);
268
269	strscpy(rmidi->name, name);
270	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &ca_midi_output);
271	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &ca_midi_input);
272	rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT |
273	                     SNDRV_RAWMIDI_INFO_INPUT |
274	                     SNDRV_RAWMIDI_INFO_DUPLEX;
275	rmidi->private_data = midi;
276	rmidi->private_free = ca_rmidi_free;
277	
278	midi->rmidi = rmidi;
279	return 0;
280}
281
~/ Read-only file preview.