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 36irqreturn_t snd_sb8dsp_midi_interrupt(struct snd_sb *chip) 37{ 38 struct snd_rawmidi *rmidi; 39 int max = 64; 40 char byte; 41 42 if (!chip) 43 return IRQ_NONE; 44 45 rmidi = chip->rmidi; 46 if (!rmidi) { 47 inb(SBP(chip, DATA_AVAIL)); /* ack interrupt */ 48 return IRQ_NONE; 49 } 50 51 spin_lock(&chip->midi_input_lock); 52 while (max-- > 0) { 53 if (inb(SBP(chip, DATA_AVAIL)) & 0x80) { 54 byte = inb(SBP(chip, READ)); 55 if (chip->open & SB_OPEN_MIDI_INPUT_TRIGGER) { 56 snd_rawmidi_receive(chip->midi_substream_input, &byte, 1); 57 } 58 } 59 } 60 spin_unlock(&chip->midi_input_lock); 61 return IRQ_HANDLED; 62} 63 64static int snd_sb8dsp_midi_input_open(struct snd_rawmidi_substream *substream) 65{ 66 unsigned long flags; 67 struct snd_sb *chip; 68 unsigned int valid_open_flags; 69 70 chip = substream->rmidi->private_data; 71 valid_open_flags = chip->hardware >= SB_HW_20 72 ? SB_OPEN_MIDI_OUTPUT | SB_OPEN_MIDI_OUTPUT_TRIGGER : 0; 73 spin_lock_irqsave(&chip->open_lock, flags); 74 if (chip->open & ~valid_open_flags) { 75 spin_unlock_irqrestore(&chip->open_lock, flags); 76 return -EAGAIN; 77 } 78 chip->open |= SB_OPEN_MIDI_INPUT; 79 chip->midi_substream_input = substream; 80 if (!(chip->open & SB_OPEN_MIDI_OUTPUT)) { 81 spin_unlock_irqrestore(&chip->open_lock, flags); 82 snd_sbdsp_reset(chip); /* reset DSP */ 83 if (chip->hardware >= SB_HW_20) 84 snd_sbdsp_command(chip, SB_DSP_MIDI_UART_IRQ); 85 } else { 86 spin_unlock_irqrestore(&chip->open_lock, flags); 87 } 88 return 0; 89} 90 91static int snd_sb8dsp_midi_output_open(struct snd_rawmidi_substream *substream) 92{ 93 unsigned long flags; 94 struct snd_sb *chip; 95 unsigned int valid_open_flags; 96 97 chip = substream->rmidi->private_data; 98 valid_open_flags = chip->hardware >= SB_HW_20 99 ? SB_OPEN_MIDI_INPUT | SB_OPEN_MIDI_INPUT_TRIGGER : 0; 100 spin_lock_irqsave(&chip->open_lock, flags); 101 if (chip->open & ~valid_open_flags) { 102 spin_unlock_irqrestore(&chip->open_lock, flags); 103 return -EAGAIN; 104 } 105 chip->open |= SB_OPEN_MIDI_OUTPUT; 106 chip->midi_substream_output = substream; 107 if (!(chip->open & SB_OPEN_MIDI_INPUT)) { 108 spin_unlock_irqrestore(&chip->open_lock, flags); 109 snd_sbdsp_reset(chip); /* reset DSP */ 110 if (chip->hardware >= SB_HW_20) 111 snd_sbdsp_command(chip, SB_DSP_MIDI_UART_IRQ); 112 } else { 113 spin_unlock_irqrestore(&chip->open_lock, flags); 114 } 115 return 0; 116} 117 118static int snd_sb8dsp_midi_input_close(struct snd_rawmidi_substream *substream) 119{ 120 unsigned long flags; 121 struct snd_sb *chip; 122 123 chip = substream->rmidi->private_data; 124 spin_lock_irqsave(&chip->open_lock, flags); 125 chip->open &= ~(SB_OPEN_MIDI_INPUT | SB_OPEN_MIDI_INPUT_TRIGGER); 126 chip->midi_substream_input = NULL; 127 if (!(chip->open & SB_OPEN_MIDI_OUTPUT)) { 128 spin_unlock_irqrestore(&chip->open_lock, flags); 129 snd_sbdsp_reset(chip); /* reset DSP */ 130 } else { 131 spin_unlock_irqrestore(&chip->open_lock, flags); 132 } 133 return 0; 134} 135 136static int snd_sb8dsp_midi_output_close(struct snd_rawmidi_substream *substream) 137{ 138 unsigned long flags; 139 struct snd_sb *chip; 140 141 chip = substream->rmidi->private_data; 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 154static 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 177static 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 213static void snd_sb8dsp_midi_output_timer(unsigned long data) 214{ 215 struct snd_rawmidi_substream *substream = (struct snd_rawmidi_substream *) data; 216 struct snd_sb * chip = substream->rmidi->private_data; 217 unsigned long flags; 218 219 spin_lock_irqsave(&chip->open_lock, flags); 220 chip->midi_timer.expires = 1 + jiffies; 221 add_timer(&chip->midi_timer); 222 spin_unlock_irqrestore(&chip->open_lock, flags); 223 snd_sb8dsp_midi_output_write(substream); 224} 225 226static void snd_sb8dsp_midi_output_trigger(struct snd_rawmidi_substream *substream, int up) 227{ 228 unsigned long flags; 229 struct snd_sb *chip; 230 231 chip = substream->rmidi->private_data; 232 spin_lock_irqsave(&chip->open_lock, flags); 233 if (up) { 234 if (!(chip->open & SB_OPEN_MIDI_OUTPUT_TRIGGER)) { 235 init_timer(&chip->midi_timer); 236 chip->midi_timer.function = snd_sb8dsp_midi_output_timer; 237 chip->midi_timer.data = (unsigned long) substream; 238 chip->midi_timer.expires = 1 + jiffies; 239 add_timer(&chip->midi_timer); 240 chip->open |= SB_OPEN_MIDI_OUTPUT_TRIGGER; 241 } 242 } else { 243 if (chip->open & SB_OPEN_MIDI_OUTPUT_TRIGGER) { 244 chip->open &= ~SB_OPEN_MIDI_OUTPUT_TRIGGER; 245 } 246 } 247 spin_unlock_irqrestore(&chip->open_lock, flags); 248 249 if (up) 250 snd_sb8dsp_midi_output_write(substream); 251} 252 253static struct snd_rawmidi_ops snd_sb8dsp_midi_output = 254{ 255 .open = snd_sb8dsp_midi_output_open, 256 .close = snd_sb8dsp_midi_output_close, 257 .trigger = snd_sb8dsp_midi_output_trigger, 258}; 259 260static struct snd_rawmidi_ops snd_sb8dsp_midi_input = 261{ 262 .open = snd_sb8dsp_midi_input_open, 263 .close = snd_sb8dsp_midi_input_close, 264 .trigger = snd_sb8dsp_midi_input_trigger, 265}; 266 267int snd_sb8dsp_midi(struct snd_sb *chip, int device, struct snd_rawmidi ** rrawmidi) 268{ 269 struct snd_rawmidi *rmidi; 270 int err; 271 272 if (rrawmidi) 273 *rrawmidi = NULL; 274 if ((err = snd_rawmidi_new(chip->card, "SB8 MIDI", device, 1, 1, &rmidi)) < 0) 275 return err; 276 strcpy(rmidi->name, "SB8 MIDI"); 277 snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_sb8dsp_midi_output); 278 snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_sb8dsp_midi_input); 279 rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT; 280 if (chip->hardware >= SB_HW_20) 281 rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX; 282 rmidi->private_data = chip; 283 chip->rmidi = rmidi; 284 if (rrawmidi) 285 *rrawmidi = rmidi; 286 return 0; 287} 288