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 35irqreturn_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 63static 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 90static 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 117static 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 135static 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 153static 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 176static 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 212static 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 mod_timer(&chip->midi_timer, 1 + jiffies); 220 spin_unlock_irqrestore(&chip->open_lock, flags); 221 snd_sb8dsp_midi_output_write(substream); 222} 223 224static void snd_sb8dsp_midi_output_trigger(struct snd_rawmidi_substream *substream, int up) 225{ 226 unsigned long flags; 227 struct snd_sb *chip; 228 229 chip = substream->rmidi->private_data; 230 spin_lock_irqsave(&chip->open_lock, flags); 231 if (up) { 232 if (!(chip->open & SB_OPEN_MIDI_OUTPUT_TRIGGER)) { 233 setup_timer(&chip->midi_timer, 234 snd_sb8dsp_midi_output_timer, 235 (unsigned long) substream); 236 mod_timer(&chip->midi_timer, 1 + jiffies); 237 chip->open |= SB_OPEN_MIDI_OUTPUT_TRIGGER; 238 } 239 } else { 240 if (chip->open & SB_OPEN_MIDI_OUTPUT_TRIGGER) { 241 chip->open &= ~SB_OPEN_MIDI_OUTPUT_TRIGGER; 242 } 243 } 244 spin_unlock_irqrestore(&chip->open_lock, flags); 245 246 if (up) 247 snd_sb8dsp_midi_output_write(substream); 248} 249 250static struct snd_rawmidi_ops snd_sb8dsp_midi_output = 251{ 252 .open = snd_sb8dsp_midi_output_open, 253 .close = snd_sb8dsp_midi_output_close, 254 .trigger = snd_sb8dsp_midi_output_trigger, 255}; 256 257static struct snd_rawmidi_ops snd_sb8dsp_midi_input = 258{ 259 .open = snd_sb8dsp_midi_input_open, 260 .close = snd_sb8dsp_midi_input_close, 261 .trigger = snd_sb8dsp_midi_input_trigger, 262}; 263 264int snd_sb8dsp_midi(struct snd_sb *chip, int device) 265{ 266 struct snd_rawmidi *rmidi; 267 int err; 268 269 if ((err = snd_rawmidi_new(chip->card, "SB8 MIDI", device, 1, 1, &rmidi)) < 0) 270 return err; 271 strcpy(rmidi->name, "SB8 MIDI"); 272 snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_sb8dsp_midi_output); 273 snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_sb8dsp_midi_input); 274 rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT; 275 if (chip->hardware >= SB_HW_20) 276 rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX; 277 rmidi->private_data = chip; 278 chip->rmidi = rmidi; 279 return 0; 280} 281