268 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright (c) by Jaroslav Kysela <perex@perex.cz>4 * Routines for control of SoundBlaster cards - MIDI interface5 *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/time.h>18#include <sound/core.h>19#include <sound/sb.h>20 21 22irqreturn_t snd_sb8dsp_midi_interrupt(struct snd_sb *chip)23{24 struct snd_rawmidi *rmidi;25 int max = 64;26 char byte;27 28 if (!chip)29 return IRQ_NONE;30 31 rmidi = chip->rmidi;32 if (!rmidi) {33 inb(SBP(chip, DATA_AVAIL)); /* ack interrupt */34 return IRQ_NONE;35 }36 37 spin_lock(&chip->midi_input_lock);38 while (max-- > 0) {39 if (inb(SBP(chip, DATA_AVAIL)) & 0x80) {40 byte = inb(SBP(chip, READ));41 if (chip->open & SB_OPEN_MIDI_INPUT_TRIGGER) {42 snd_rawmidi_receive(chip->midi_substream_input, &byte, 1);43 }44 }45 }46 spin_unlock(&chip->midi_input_lock);47 return IRQ_HANDLED;48}49 50static int snd_sb8dsp_midi_input_open(struct snd_rawmidi_substream *substream)51{52 unsigned long flags;53 struct snd_sb *chip;54 unsigned int valid_open_flags;55 56 chip = substream->rmidi->private_data;57 valid_open_flags = chip->hardware >= SB_HW_2058 ? SB_OPEN_MIDI_OUTPUT | SB_OPEN_MIDI_OUTPUT_TRIGGER : 0;59 spin_lock_irqsave(&chip->open_lock, flags);60 if (chip->open & ~valid_open_flags) {61 spin_unlock_irqrestore(&chip->open_lock, flags);62 return -EAGAIN;63 }64 chip->open |= SB_OPEN_MIDI_INPUT;65 chip->midi_substream_input = substream;66 if (!(chip->open & SB_OPEN_MIDI_OUTPUT)) {67 spin_unlock_irqrestore(&chip->open_lock, flags);68 snd_sbdsp_reset(chip); /* reset DSP */69 if (chip->hardware >= SB_HW_20)70 snd_sbdsp_command(chip, SB_DSP_MIDI_UART_IRQ);71 } else {72 spin_unlock_irqrestore(&chip->open_lock, flags);73 }74 return 0;75}76 77static int snd_sb8dsp_midi_output_open(struct snd_rawmidi_substream *substream)78{79 unsigned long flags;80 struct snd_sb *chip;81 unsigned int valid_open_flags;82 83 chip = substream->rmidi->private_data;84 valid_open_flags = chip->hardware >= SB_HW_2085 ? SB_OPEN_MIDI_INPUT | SB_OPEN_MIDI_INPUT_TRIGGER : 0;86 spin_lock_irqsave(&chip->open_lock, flags);87 if (chip->open & ~valid_open_flags) {88 spin_unlock_irqrestore(&chip->open_lock, flags);89 return -EAGAIN;90 }91 chip->open |= SB_OPEN_MIDI_OUTPUT;92 chip->midi_substream_output = substream;93 if (!(chip->open & SB_OPEN_MIDI_INPUT)) {94 spin_unlock_irqrestore(&chip->open_lock, flags);95 snd_sbdsp_reset(chip); /* reset DSP */96 if (chip->hardware >= SB_HW_20)97 snd_sbdsp_command(chip, SB_DSP_MIDI_UART_IRQ);98 } else {99 spin_unlock_irqrestore(&chip->open_lock, flags);100 }101 return 0;102}103 104static int snd_sb8dsp_midi_input_close(struct snd_rawmidi_substream *substream)105{106 unsigned long flags;107 struct snd_sb *chip;108 109 chip = substream->rmidi->private_data;110 spin_lock_irqsave(&chip->open_lock, flags);111 chip->open &= ~(SB_OPEN_MIDI_INPUT | SB_OPEN_MIDI_INPUT_TRIGGER);112 chip->midi_substream_input = NULL;113 if (!(chip->open & SB_OPEN_MIDI_OUTPUT)) {114 spin_unlock_irqrestore(&chip->open_lock, flags);115 snd_sbdsp_reset(chip); /* reset DSP */116 } else {117 spin_unlock_irqrestore(&chip->open_lock, flags);118 }119 return 0;120}121 122static int snd_sb8dsp_midi_output_close(struct snd_rawmidi_substream *substream)123{124 unsigned long flags;125 struct snd_sb *chip;126 127 chip = substream->rmidi->private_data;128 del_timer_sync(&chip->midi_timer);129 spin_lock_irqsave(&chip->open_lock, flags);130 chip->open &= ~(SB_OPEN_MIDI_OUTPUT | SB_OPEN_MIDI_OUTPUT_TRIGGER);131 chip->midi_substream_output = NULL;132 if (!(chip->open & SB_OPEN_MIDI_INPUT)) {133 spin_unlock_irqrestore(&chip->open_lock, flags);134 snd_sbdsp_reset(chip); /* reset DSP */135 } else {136 spin_unlock_irqrestore(&chip->open_lock, flags);137 }138 return 0;139}140 141static void snd_sb8dsp_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)142{143 unsigned long flags;144 struct snd_sb *chip;145 146 chip = substream->rmidi->private_data;147 spin_lock_irqsave(&chip->open_lock, flags);148 if (up) {149 if (!(chip->open & SB_OPEN_MIDI_INPUT_TRIGGER)) {150 if (chip->hardware < SB_HW_20)151 snd_sbdsp_command(chip, SB_DSP_MIDI_INPUT_IRQ);152 chip->open |= SB_OPEN_MIDI_INPUT_TRIGGER;153 }154 } else {155 if (chip->open & SB_OPEN_MIDI_INPUT_TRIGGER) {156 if (chip->hardware < SB_HW_20)157 snd_sbdsp_command(chip, SB_DSP_MIDI_INPUT_IRQ);158 chip->open &= ~SB_OPEN_MIDI_INPUT_TRIGGER;159 }160 }161 spin_unlock_irqrestore(&chip->open_lock, flags);162}163 164static void snd_sb8dsp_midi_output_write(struct snd_rawmidi_substream *substream)165{166 unsigned long flags;167 struct snd_sb *chip;168 char byte;169 int max = 32;170 171 /* how big is Tx FIFO? */172 chip = substream->rmidi->private_data;173 while (max-- > 0) {174 spin_lock_irqsave(&chip->open_lock, flags);175 if (snd_rawmidi_transmit_peek(substream, &byte, 1) != 1) {176 chip->open &= ~SB_OPEN_MIDI_OUTPUT_TRIGGER;177 del_timer(&chip->midi_timer);178 spin_unlock_irqrestore(&chip->open_lock, flags);179 break;180 }181 if (chip->hardware >= SB_HW_20) {182 int timeout = 8;183 while ((inb(SBP(chip, STATUS)) & 0x80) != 0 && --timeout > 0)184 ;185 if (timeout == 0) {186 /* Tx FIFO full - try again later */187 spin_unlock_irqrestore(&chip->open_lock, flags);188 break;189 }190 outb(byte, SBP(chip, WRITE));191 } else {192 snd_sbdsp_command(chip, SB_DSP_MIDI_OUTPUT);193 snd_sbdsp_command(chip, byte);194 }195 snd_rawmidi_transmit_ack(substream, 1);196 spin_unlock_irqrestore(&chip->open_lock, flags);197 }198}199 200static void snd_sb8dsp_midi_output_timer(struct timer_list *t)201{202 struct snd_sb *chip = from_timer(chip, t, midi_timer);203 struct snd_rawmidi_substream *substream = chip->midi_substream_output;204 unsigned long flags;205 206 spin_lock_irqsave(&chip->open_lock, flags);207 mod_timer(&chip->midi_timer, 1 + jiffies);208 spin_unlock_irqrestore(&chip->open_lock, flags); 209 snd_sb8dsp_midi_output_write(substream);210}211 212static void snd_sb8dsp_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)213{214 unsigned long flags;215 struct snd_sb *chip;216 217 chip = substream->rmidi->private_data;218 spin_lock_irqsave(&chip->open_lock, flags);219 if (up) {220 if (!(chip->open & SB_OPEN_MIDI_OUTPUT_TRIGGER)) {221 mod_timer(&chip->midi_timer, 1 + jiffies);222 chip->open |= SB_OPEN_MIDI_OUTPUT_TRIGGER;223 }224 } else {225 if (chip->open & SB_OPEN_MIDI_OUTPUT_TRIGGER) {226 chip->open &= ~SB_OPEN_MIDI_OUTPUT_TRIGGER;227 }228 }229 spin_unlock_irqrestore(&chip->open_lock, flags);230 231 if (up)232 snd_sb8dsp_midi_output_write(substream);233}234 235static const struct snd_rawmidi_ops snd_sb8dsp_midi_output =236{237 .open = snd_sb8dsp_midi_output_open,238 .close = snd_sb8dsp_midi_output_close,239 .trigger = snd_sb8dsp_midi_output_trigger,240};241 242static const struct snd_rawmidi_ops snd_sb8dsp_midi_input =243{244 .open = snd_sb8dsp_midi_input_open,245 .close = snd_sb8dsp_midi_input_close,246 .trigger = snd_sb8dsp_midi_input_trigger,247};248 249int snd_sb8dsp_midi(struct snd_sb *chip, int device)250{251 struct snd_rawmidi *rmidi;252 int err;253 254 err = snd_rawmidi_new(chip->card, "SB8 MIDI", device, 1, 1, &rmidi);255 if (err < 0)256 return err;257 strcpy(rmidi->name, "SB8 MIDI");258 snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_sb8dsp_midi_output);259 snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_sb8dsp_midi_input);260 rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT;261 if (chip->hardware >= SB_HW_20)262 rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;263 rmidi->private_data = chip;264 timer_setup(&chip->midi_timer, snd_sb8dsp_midi_output_timer, 0);265 chip->rmidi = rmidi;266 return 0;267}268