sb8_midi.c (7645B)
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/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_20 58 ? 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_20 85 ? 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}