File Index
Files and directories
Sign in
FILE

sb8_midi.c

File preview with syntax highlighting.

linux-6.19.14/sound/isa/sb/sb8_midi.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
  4 *  Routines for control of SoundBlaster cards - MIDI interface
  5 *
  6 * --
  7 *
  8 * Sun May  9 22:54:38 BST 1999 George David Morrison <gdm@gedamo.demon.co.uk>
  9 *   Fixed typo in snd_sb8dsp_midi_new_device which prevented midi from 
 10 *   working.
 11 *
 12 * Sun May 11 12:34:56 UTC 2003 Clemens Ladisch <clemens@ladisch.de>
 13 *   Added full duplex UART mode for DSP version 2.0 and later.
 14 */
 15
 16#include <linux/io.h>
 17#include <linux/string.h>
 18#include <linux/time.h>
 19#include <sound/core.h>
 20#include <sound/sb.h>
 21
 22
 23irqreturn_t snd_sb8dsp_midi_interrupt(struct snd_sb *chip)
 24{
 25	struct snd_rawmidi *rmidi;
 26	int max = 64;
 27	char byte;
 28
 29	if (!chip)
 30		return IRQ_NONE;
 31	
 32	rmidi = chip->rmidi;
 33	if (!rmidi) {
 34		inb(SBP(chip, DATA_AVAIL));	/* ack interrupt */
 35		return IRQ_NONE;
 36	}
 37
 38	guard(spinlock)(&chip->midi_input_lock);
 39	while (max-- > 0) {
 40		if (inb(SBP(chip, DATA_AVAIL)) & 0x80) {
 41			byte = inb(SBP(chip, READ));
 42			if (chip->open & SB_OPEN_MIDI_INPUT_TRIGGER) {
 43				snd_rawmidi_receive(chip->midi_substream_input, &byte, 1);
 44			}
 45		}
 46	}
 47	return IRQ_HANDLED;
 48}
 49
 50static int snd_sb8dsp_midi_input_open(struct snd_rawmidi_substream *substream)
 51{
 52	struct snd_sb *chip;
 53	unsigned int valid_open_flags;
 54
 55	chip = substream->rmidi->private_data;
 56	valid_open_flags = chip->hardware >= SB_HW_20
 57		? SB_OPEN_MIDI_OUTPUT | SB_OPEN_MIDI_OUTPUT_TRIGGER : 0;
 58	scoped_guard(spinlock_irqsave, &chip->open_lock) {
 59		if (chip->open & ~valid_open_flags)
 60			return -EAGAIN;
 61		chip->open |= SB_OPEN_MIDI_INPUT;
 62		chip->midi_substream_input = substream;
 63		if (chip->open & SB_OPEN_MIDI_OUTPUT)
 64			return 0;
 65	}
 66	snd_sbdsp_reset(chip);		/* reset DSP */
 67	if (chip->hardware >= SB_HW_20)
 68		snd_sbdsp_command(chip, SB_DSP_MIDI_UART_IRQ);
 69	return 0;
 70}
 71
 72static int snd_sb8dsp_midi_output_open(struct snd_rawmidi_substream *substream)
 73{
 74	struct snd_sb *chip;
 75	unsigned int valid_open_flags;
 76
 77	chip = substream->rmidi->private_data;
 78	valid_open_flags = chip->hardware >= SB_HW_20
 79		? SB_OPEN_MIDI_INPUT | SB_OPEN_MIDI_INPUT_TRIGGER : 0;
 80	scoped_guard(spinlock_irqsave, &chip->open_lock) {
 81		if (chip->open & ~valid_open_flags)
 82			return -EAGAIN;
 83		chip->open |= SB_OPEN_MIDI_OUTPUT;
 84		chip->midi_substream_output = substream;
 85		if (chip->open & SB_OPEN_MIDI_INPUT)
 86			return 0;
 87	}
 88	snd_sbdsp_reset(chip);		/* reset DSP */
 89	if (chip->hardware >= SB_HW_20)
 90		snd_sbdsp_command(chip, SB_DSP_MIDI_UART_IRQ);
 91	return 0;
 92}
 93
 94static int snd_sb8dsp_midi_input_close(struct snd_rawmidi_substream *substream)
 95{
 96	struct snd_sb *chip;
 97
 98	chip = substream->rmidi->private_data;
 99	scoped_guard(spinlock_irqsave, &chip->open_lock) {
100		chip->open &= ~(SB_OPEN_MIDI_INPUT | SB_OPEN_MIDI_INPUT_TRIGGER);
101		chip->midi_substream_input = NULL;
102		if (chip->open & SB_OPEN_MIDI_OUTPUT)
103			return 0;
104	}
105	snd_sbdsp_reset(chip);		/* reset DSP */
106	return 0;
107}
108
109static int snd_sb8dsp_midi_output_close(struct snd_rawmidi_substream *substream)
110{
111	struct snd_sb *chip;
112
113	chip = substream->rmidi->private_data;
114	timer_delete_sync(&chip->midi_timer);
115	scoped_guard(spinlock_irqsave, &chip->open_lock) {
116		chip->open &= ~(SB_OPEN_MIDI_OUTPUT | SB_OPEN_MIDI_OUTPUT_TRIGGER);
117		chip->midi_substream_output = NULL;
118		if (chip->open & SB_OPEN_MIDI_INPUT)
119			return 0;
120	}
121	snd_sbdsp_reset(chip);		/* reset DSP */
122	return 0;
123}
124
125static void snd_sb8dsp_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
126{
127	struct snd_sb *chip;
128
129	chip = substream->rmidi->private_data;
130	guard(spinlock_irqsave)(&chip->open_lock);
131	if (up) {
132		if (!(chip->open & SB_OPEN_MIDI_INPUT_TRIGGER)) {
133			if (chip->hardware < SB_HW_20)
134				snd_sbdsp_command(chip, SB_DSP_MIDI_INPUT_IRQ);
135			chip->open |= SB_OPEN_MIDI_INPUT_TRIGGER;
136		}
137	} else {
138		if (chip->open & SB_OPEN_MIDI_INPUT_TRIGGER) {
139			if (chip->hardware < SB_HW_20)
140				snd_sbdsp_command(chip, SB_DSP_MIDI_INPUT_IRQ);
141			chip->open &= ~SB_OPEN_MIDI_INPUT_TRIGGER;
142		}
143	}
144}
145
146static void snd_sb8dsp_midi_output_write(struct snd_rawmidi_substream *substream)
147{
148	struct snd_sb *chip;
149	char byte;
150	int max = 32;
151
152	/* how big is Tx FIFO? */
153	chip = substream->rmidi->private_data;
154	while (max-- > 0) {
155		guard(spinlock_irqsave)(&chip->open_lock);
156		if (snd_rawmidi_transmit_peek(substream, &byte, 1) != 1) {
157			chip->open &= ~SB_OPEN_MIDI_OUTPUT_TRIGGER;
158			timer_delete(&chip->midi_timer);
159			break;
160		}
161		if (chip->hardware >= SB_HW_20) {
162			int timeout = 8;
163			while ((inb(SBP(chip, STATUS)) & 0x80) != 0 && --timeout > 0)
164				;
165			if (timeout == 0) {
166				/* Tx FIFO full - try again later */
167				break;
168			}
169			outb(byte, SBP(chip, WRITE));
170		} else {
171			snd_sbdsp_command(chip, SB_DSP_MIDI_OUTPUT);
172			snd_sbdsp_command(chip, byte);
173		}
174		snd_rawmidi_transmit_ack(substream, 1);
175	}
176}
177
178static void snd_sb8dsp_midi_output_timer(struct timer_list *t)
179{
180	struct snd_sb *chip = timer_container_of(chip, t, midi_timer);
181	struct snd_rawmidi_substream *substream = chip->midi_substream_output;
182
183	scoped_guard(spinlock_irqsave, &chip->open_lock) {
184		mod_timer(&chip->midi_timer, 1 + jiffies);
185	}
186	snd_sb8dsp_midi_output_write(substream);
187}
188
189static void snd_sb8dsp_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
190{
191	struct snd_sb *chip;
192
193	chip = substream->rmidi->private_data;
194	scoped_guard(spinlock_irqsave, &chip->open_lock) {
195		if (up) {
196			if (!(chip->open & SB_OPEN_MIDI_OUTPUT_TRIGGER)) {
197				mod_timer(&chip->midi_timer, 1 + jiffies);
198				chip->open |= SB_OPEN_MIDI_OUTPUT_TRIGGER;
199			}
200		} else {
201			if (chip->open & SB_OPEN_MIDI_OUTPUT_TRIGGER) {
202				chip->open &= ~SB_OPEN_MIDI_OUTPUT_TRIGGER;
203			}
204		}
205	}
206
207	if (up)
208		snd_sb8dsp_midi_output_write(substream);
209}
210
211static const struct snd_rawmidi_ops snd_sb8dsp_midi_output =
212{
213	.open =		snd_sb8dsp_midi_output_open,
214	.close =	snd_sb8dsp_midi_output_close,
215	.trigger =	snd_sb8dsp_midi_output_trigger,
216};
217
218static const struct snd_rawmidi_ops snd_sb8dsp_midi_input =
219{
220	.open =		snd_sb8dsp_midi_input_open,
221	.close =	snd_sb8dsp_midi_input_close,
222	.trigger =	snd_sb8dsp_midi_input_trigger,
223};
224
225int snd_sb8dsp_midi(struct snd_sb *chip, int device)
226{
227	struct snd_rawmidi *rmidi;
228	int err;
229
230	err = snd_rawmidi_new(chip->card, "SB8 MIDI", device, 1, 1, &rmidi);
231	if (err < 0)
232		return err;
233	strscpy(rmidi->name, "SB8 MIDI");
234	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_sb8dsp_midi_output);
235	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_sb8dsp_midi_input);
236	rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT;
237	if (chip->hardware >= SB_HW_20)
238		rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
239	rmidi->private_data = chip;
240	timer_setup(&chip->midi_timer, snd_sb8dsp_midi_output_timer, 0);
241	chip->rmidi = rmidi;
242	return 0;
243}
~/ Read-only file preview.