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