1 /* 2 * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 3 * Routines for control of SoundBlaster cards - MIDI interface 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 * 19 * -- 20 * 21 * Sun May 9 22:54:38 BST 1999 George David Morrison <gdm@gedamo.demon.co.uk> 22 * Fixed typo in snd_sb8dsp_midi_new_device which prevented midi from 23 * working. 24 * 25 * Sun May 11 12:34:56 UTC 2003 Clemens Ladisch <clemens@ladisch.de> 26 * Added full duplex UART mode for DSP version 2.0 and later. 27 */ 28 29 #include <linux/io.h> 30 #include <linux/time.h> 31 #include <sound/core.h> 32 #include <sound/sb.h> 33 34 35 irqreturn_t snd_sb8dsp_midi_interrupt(struct snd_sb *chip) 36 { 37 struct snd_rawmidi *rmidi; 38 int max = 64; 39 char byte; 40 41 if (!chip) 42 return IRQ_NONE; 43 44 rmidi = chip->rmidi; 45 if (!rmidi) { 46 inb(SBP(chip, DATA_AVAIL)); /* ack interrupt */ 47 return IRQ_NONE; 48 } 49 50 spin_lock(&chip->midi_input_lock); 51 while (max-- > 0) { 52 if (inb(SBP(chip, DATA_AVAIL)) & 0x80) { 53 byte = inb(SBP(chip, READ)); 54 if (chip->open & SB_OPEN_MIDI_INPUT_TRIGGER) { 55 snd_rawmidi_receive(chip->midi_substream_input, &byte, 1); 56 } 57 } 58 } 59 spin_unlock(&chip->midi_input_lock); 60 return IRQ_HANDLED; 61 } 62 63 static int snd_sb8dsp_midi_input_open(struct snd_rawmidi_substream *substream) 64 { 65 unsigned long flags; 66 struct snd_sb *chip; 67 unsigned int valid_open_flags; 68 69 chip = substream->rmidi->private_data; 70 valid_open_flags = chip->hardware >= SB_HW_20 71 ? SB_OPEN_MIDI_OUTPUT | SB_OPEN_MIDI_OUTPUT_TRIGGER : 0; 72 spin_lock_irqsave(&chip->open_lock, flags); 73 if (chip->open & ~valid_open_flags) { 74 spin_unlock_irqrestore(&chip->open_lock, flags); 75 return -EAGAIN; 76 } 77 chip->open |= SB_OPEN_MIDI_INPUT; 78 chip->midi_substream_input = substream; 79 if (!(chip->open & SB_OPEN_MIDI_OUTPUT)) { 80 spin_unlock_irqrestore(&chip->open_lock, flags); 81 snd_sbdsp_reset(chip); /* reset DSP */ 82 if (chip->hardware >= SB_HW_20) 83 snd_sbdsp_command(chip, SB_DSP_MIDI_UART_IRQ); 84 } else { 85 spin_unlock_irqrestore(&chip->open_lock, flags); 86 } 87 return 0; 88 } 89 90 static int snd_sb8dsp_midi_output_open(struct snd_rawmidi_substream *substream) 91 { 92 unsigned long flags; 93 struct snd_sb *chip; 94 unsigned int valid_open_flags; 95 96 chip = substream->rmidi->private_data; 97 valid_open_flags = chip->hardware >= SB_HW_20 98 ? SB_OPEN_MIDI_INPUT | SB_OPEN_MIDI_INPUT_TRIGGER : 0; 99 spin_lock_irqsave(&chip->open_lock, flags); 100 if (chip->open & ~valid_open_flags) { 101 spin_unlock_irqrestore(&chip->open_lock, flags); 102 return -EAGAIN; 103 } 104 chip->open |= SB_OPEN_MIDI_OUTPUT; 105 chip->midi_substream_output = substream; 106 if (!(chip->open & SB_OPEN_MIDI_INPUT)) { 107 spin_unlock_irqrestore(&chip->open_lock, flags); 108 snd_sbdsp_reset(chip); /* reset DSP */ 109 if (chip->hardware >= SB_HW_20) 110 snd_sbdsp_command(chip, SB_DSP_MIDI_UART_IRQ); 111 } else { 112 spin_unlock_irqrestore(&chip->open_lock, flags); 113 } 114 return 0; 115 } 116 117 static int snd_sb8dsp_midi_input_close(struct snd_rawmidi_substream *substream) 118 { 119 unsigned long flags; 120 struct snd_sb *chip; 121 122 chip = substream->rmidi->private_data; 123 spin_lock_irqsave(&chip->open_lock, flags); 124 chip->open &= ~(SB_OPEN_MIDI_INPUT | SB_OPEN_MIDI_INPUT_TRIGGER); 125 chip->midi_substream_input = NULL; 126 if (!(chip->open & SB_OPEN_MIDI_OUTPUT)) { 127 spin_unlock_irqrestore(&chip->open_lock, flags); 128 snd_sbdsp_reset(chip); /* reset DSP */ 129 } else { 130 spin_unlock_irqrestore(&chip->open_lock, flags); 131 } 132 return 0; 133 } 134 135 static int snd_sb8dsp_midi_output_close(struct snd_rawmidi_substream *substream) 136 { 137 unsigned long flags; 138 struct snd_sb *chip; 139 140 chip = substream->rmidi->private_data; 141 del_timer_sync(&chip->midi_timer); 142 spin_lock_irqsave(&chip->open_lock, flags); 143 chip->open &= ~(SB_OPEN_MIDI_OUTPUT | SB_OPEN_MIDI_OUTPUT_TRIGGER); 144 chip->midi_substream_output = NULL; 145 if (!(chip->open & SB_OPEN_MIDI_INPUT)) { 146 spin_unlock_irqrestore(&chip->open_lock, flags); 147 snd_sbdsp_reset(chip); /* reset DSP */ 148 } else { 149 spin_unlock_irqrestore(&chip->open_lock, flags); 150 } 151 return 0; 152 } 153 154 static void snd_sb8dsp_midi_input_trigger(struct snd_rawmidi_substream *substream, int up) 155 { 156 unsigned long flags; 157 struct snd_sb *chip; 158 159 chip = substream->rmidi->private_data; 160 spin_lock_irqsave(&chip->open_lock, flags); 161 if (up) { 162 if (!(chip->open & SB_OPEN_MIDI_INPUT_TRIGGER)) { 163 if (chip->hardware < SB_HW_20) 164 snd_sbdsp_command(chip, SB_DSP_MIDI_INPUT_IRQ); 165 chip->open |= SB_OPEN_MIDI_INPUT_TRIGGER; 166 } 167 } else { 168 if (chip->open & SB_OPEN_MIDI_INPUT_TRIGGER) { 169 if (chip->hardware < SB_HW_20) 170 snd_sbdsp_command(chip, SB_DSP_MIDI_INPUT_IRQ); 171 chip->open &= ~SB_OPEN_MIDI_INPUT_TRIGGER; 172 } 173 } 174 spin_unlock_irqrestore(&chip->open_lock, flags); 175 } 176 177 static void snd_sb8dsp_midi_output_write(struct snd_rawmidi_substream *substream) 178 { 179 unsigned long flags; 180 struct snd_sb *chip; 181 char byte; 182 int max = 32; 183 184 /* how big is Tx FIFO? */ 185 chip = substream->rmidi->private_data; 186 while (max-- > 0) { 187 spin_lock_irqsave(&chip->open_lock, flags); 188 if (snd_rawmidi_transmit_peek(substream, &byte, 1) != 1) { 189 chip->open &= ~SB_OPEN_MIDI_OUTPUT_TRIGGER; 190 del_timer(&chip->midi_timer); 191 spin_unlock_irqrestore(&chip->open_lock, flags); 192 break; 193 } 194 if (chip->hardware >= SB_HW_20) { 195 int timeout = 8; 196 while ((inb(SBP(chip, STATUS)) & 0x80) != 0 && --timeout > 0) 197 ; 198 if (timeout == 0) { 199 /* Tx FIFO full - try again later */ 200 spin_unlock_irqrestore(&chip->open_lock, flags); 201 break; 202 } 203 outb(byte, SBP(chip, WRITE)); 204 } else { 205 snd_sbdsp_command(chip, SB_DSP_MIDI_OUTPUT); 206 snd_sbdsp_command(chip, byte); 207 } 208 snd_rawmidi_transmit_ack(substream, 1); 209 spin_unlock_irqrestore(&chip->open_lock, flags); 210 } 211 } 212 213 static void snd_sb8dsp_midi_output_timer(struct timer_list *t) 214 { 215 struct snd_sb *chip = from_timer(chip, t, midi_timer); 216 struct snd_rawmidi_substream *substream = chip->midi_substream_output; 217 unsigned long flags; 218 219 spin_lock_irqsave(&chip->open_lock, flags); 220 mod_timer(&chip->midi_timer, 1 + jiffies); 221 spin_unlock_irqrestore(&chip->open_lock, flags); 222 snd_sb8dsp_midi_output_write(substream); 223 } 224 225 static void snd_sb8dsp_midi_output_trigger(struct snd_rawmidi_substream *substream, int up) 226 { 227 unsigned long flags; 228 struct snd_sb *chip; 229 230 chip = substream->rmidi->private_data; 231 spin_lock_irqsave(&chip->open_lock, flags); 232 if (up) { 233 if (!(chip->open & SB_OPEN_MIDI_OUTPUT_TRIGGER)) { 234 mod_timer(&chip->midi_timer, 1 + jiffies); 235 chip->open |= SB_OPEN_MIDI_OUTPUT_TRIGGER; 236 } 237 } else { 238 if (chip->open & SB_OPEN_MIDI_OUTPUT_TRIGGER) { 239 chip->open &= ~SB_OPEN_MIDI_OUTPUT_TRIGGER; 240 } 241 } 242 spin_unlock_irqrestore(&chip->open_lock, flags); 243 244 if (up) 245 snd_sb8dsp_midi_output_write(substream); 246 } 247 248 static const struct snd_rawmidi_ops snd_sb8dsp_midi_output = 249 { 250 .open = snd_sb8dsp_midi_output_open, 251 .close = snd_sb8dsp_midi_output_close, 252 .trigger = snd_sb8dsp_midi_output_trigger, 253 }; 254 255 static const struct snd_rawmidi_ops snd_sb8dsp_midi_input = 256 { 257 .open = snd_sb8dsp_midi_input_open, 258 .close = snd_sb8dsp_midi_input_close, 259 .trigger = snd_sb8dsp_midi_input_trigger, 260 }; 261 262 int snd_sb8dsp_midi(struct snd_sb *chip, int device) 263 { 264 struct snd_rawmidi *rmidi; 265 int err; 266 267 if ((err = snd_rawmidi_new(chip->card, "SB8 MIDI", device, 1, 1, &rmidi)) < 0) 268 return err; 269 strcpy(rmidi->name, "SB8 MIDI"); 270 snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_sb8dsp_midi_output); 271 snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_sb8dsp_midi_input); 272 rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT; 273 if (chip->hardware >= SB_HW_20) 274 rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX; 275 rmidi->private_data = chip; 276 timer_setup(&chip->midi_timer, snd_sb8dsp_midi_output_timer, 0); 277 chip->rmidi = rmidi; 278 return 0; 279 } 280