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 <asm/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 spin_lock_irqsave(&chip->open_lock, flags); 142 chip->open &= ~(SB_OPEN_MIDI_OUTPUT | SB_OPEN_MIDI_OUTPUT_TRIGGER); 143 chip->midi_substream_output = NULL; 144 if (!(chip->open & SB_OPEN_MIDI_INPUT)) { 145 spin_unlock_irqrestore(&chip->open_lock, flags); 146 snd_sbdsp_reset(chip); /* reset DSP */ 147 } else { 148 spin_unlock_irqrestore(&chip->open_lock, flags); 149 } 150 return 0; 151 } 152 153 static void snd_sb8dsp_midi_input_trigger(struct snd_rawmidi_substream *substream, int up) 154 { 155 unsigned long flags; 156 struct snd_sb *chip; 157 158 chip = substream->rmidi->private_data; 159 spin_lock_irqsave(&chip->open_lock, flags); 160 if (up) { 161 if (!(chip->open & SB_OPEN_MIDI_INPUT_TRIGGER)) { 162 if (chip->hardware < SB_HW_20) 163 snd_sbdsp_command(chip, SB_DSP_MIDI_INPUT_IRQ); 164 chip->open |= SB_OPEN_MIDI_INPUT_TRIGGER; 165 } 166 } else { 167 if (chip->open & SB_OPEN_MIDI_INPUT_TRIGGER) { 168 if (chip->hardware < SB_HW_20) 169 snd_sbdsp_command(chip, SB_DSP_MIDI_INPUT_IRQ); 170 chip->open &= ~SB_OPEN_MIDI_INPUT_TRIGGER; 171 } 172 } 173 spin_unlock_irqrestore(&chip->open_lock, flags); 174 } 175 176 static void snd_sb8dsp_midi_output_write(struct snd_rawmidi_substream *substream) 177 { 178 unsigned long flags; 179 struct snd_sb *chip; 180 char byte; 181 int max = 32; 182 183 /* how big is Tx FIFO? */ 184 chip = substream->rmidi->private_data; 185 while (max-- > 0) { 186 spin_lock_irqsave(&chip->open_lock, flags); 187 if (snd_rawmidi_transmit_peek(substream, &byte, 1) != 1) { 188 chip->open &= ~SB_OPEN_MIDI_OUTPUT_TRIGGER; 189 del_timer(&chip->midi_timer); 190 spin_unlock_irqrestore(&chip->open_lock, flags); 191 break; 192 } 193 if (chip->hardware >= SB_HW_20) { 194 int timeout = 8; 195 while ((inb(SBP(chip, STATUS)) & 0x80) != 0 && --timeout > 0) 196 ; 197 if (timeout == 0) { 198 /* Tx FIFO full - try again later */ 199 spin_unlock_irqrestore(&chip->open_lock, flags); 200 break; 201 } 202 outb(byte, SBP(chip, WRITE)); 203 } else { 204 snd_sbdsp_command(chip, SB_DSP_MIDI_OUTPUT); 205 snd_sbdsp_command(chip, byte); 206 } 207 snd_rawmidi_transmit_ack(substream, 1); 208 spin_unlock_irqrestore(&chip->open_lock, flags); 209 } 210 } 211 212 static void snd_sb8dsp_midi_output_timer(unsigned long data) 213 { 214 struct snd_rawmidi_substream *substream = (struct snd_rawmidi_substream *) data; 215 struct snd_sb * chip = substream->rmidi->private_data; 216 unsigned long flags; 217 218 spin_lock_irqsave(&chip->open_lock, flags); 219 chip->midi_timer.expires = 1 + jiffies; 220 add_timer(&chip->midi_timer); 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 init_timer(&chip->midi_timer); 235 chip->midi_timer.function = snd_sb8dsp_midi_output_timer; 236 chip->midi_timer.data = (unsigned long) substream; 237 chip->midi_timer.expires = 1 + jiffies; 238 add_timer(&chip->midi_timer); 239 chip->open |= SB_OPEN_MIDI_OUTPUT_TRIGGER; 240 } 241 } else { 242 if (chip->open & SB_OPEN_MIDI_OUTPUT_TRIGGER) { 243 chip->open &= ~SB_OPEN_MIDI_OUTPUT_TRIGGER; 244 } 245 } 246 spin_unlock_irqrestore(&chip->open_lock, flags); 247 248 if (up) 249 snd_sb8dsp_midi_output_write(substream); 250 } 251 252 static struct snd_rawmidi_ops snd_sb8dsp_midi_output = 253 { 254 .open = snd_sb8dsp_midi_output_open, 255 .close = snd_sb8dsp_midi_output_close, 256 .trigger = snd_sb8dsp_midi_output_trigger, 257 }; 258 259 static struct snd_rawmidi_ops snd_sb8dsp_midi_input = 260 { 261 .open = snd_sb8dsp_midi_input_open, 262 .close = snd_sb8dsp_midi_input_close, 263 .trigger = snd_sb8dsp_midi_input_trigger, 264 }; 265 266 int snd_sb8dsp_midi(struct snd_sb *chip, int device, struct snd_rawmidi ** rrawmidi) 267 { 268 struct snd_rawmidi *rmidi; 269 int err; 270 271 if (rrawmidi) 272 *rrawmidi = NULL; 273 if ((err = snd_rawmidi_new(chip->card, "SB8 MIDI", device, 1, 1, &rmidi)) < 0) 274 return err; 275 strcpy(rmidi->name, "SB8 MIDI"); 276 snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_sb8dsp_midi_output); 277 snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_sb8dsp_midi_input); 278 rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT; 279 if (chip->hardware >= SB_HW_20) 280 rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX; 281 rmidi->private_data = chip; 282 chip->rmidi = rmidi; 283 if (rrawmidi) 284 *rrawmidi = rmidi; 285 return 0; 286 } 287