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