1 /* 2 * Copyright 10/16/2005 Tilman Kranz <tilde@tk-sls.de> 3 * Creative Audio MIDI, for the CA0106 Driver 4 * Version: 0.0.1 5 * 6 * Changelog: 7 * Implementation is based on mpu401 and emu10k1x and 8 * tested with ca0106. 9 * mpu401: Copyright (c) by Jaroslav Kysela <perex@perex.cz> 10 * emu10k1x: Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com> 11 * 12 * This program is free software; you can redistribute it and/or modify 13 * it under the terms of the GNU General Public License as published by 14 * the Free Software Foundation; either version 2 of the License, or 15 * (at your option) any later version. 16 * 17 * This program is distributed in the hope that it will be useful, 18 * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 * GNU General Public License for more details. 21 * 22 * You should have received a copy of the GNU General Public License 23 * along with this program; if not, write to the Free Software 24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 25 * 26 * 27 */ 28 29 #include <linux/spinlock.h> 30 #include <sound/core.h> 31 #include <sound/rawmidi.h> 32 33 #include "ca_midi.h" 34 35 #define ca_midi_write_data(midi, data) midi->write(midi, data, 0) 36 #define ca_midi_write_cmd(midi, data) midi->write(midi, data, 1) 37 #define ca_midi_read_data(midi) midi->read(midi, 0) 38 #define ca_midi_read_stat(midi) midi->read(midi, 1) 39 #define ca_midi_input_avail(midi) (!(ca_midi_read_stat(midi) & midi->input_avail)) 40 #define ca_midi_output_ready(midi) (!(ca_midi_read_stat(midi) & midi->output_ready)) 41 42 static void ca_midi_clear_rx(struct snd_ca_midi *midi) 43 { 44 int timeout = 100000; 45 for (; timeout > 0 && ca_midi_input_avail(midi); timeout--) 46 ca_midi_read_data(midi); 47 #ifdef CONFIG_SND_DEBUG 48 if (timeout <= 0) 49 snd_printk(KERN_ERR "ca_midi_clear_rx: timeout (status = 0x%x)\n", 50 ca_midi_read_stat(midi)); 51 #endif 52 } 53 54 static void ca_midi_interrupt(struct snd_ca_midi *midi, unsigned int status) 55 { 56 unsigned char byte; 57 58 if (midi->rmidi == NULL) { 59 midi->interrupt_disable(midi,midi->tx_enable | midi->rx_enable); 60 return; 61 } 62 63 spin_lock(&midi->input_lock); 64 if ((status & midi->ipr_rx) && ca_midi_input_avail(midi)) { 65 if (!(midi->midi_mode & CA_MIDI_MODE_INPUT)) { 66 ca_midi_clear_rx(midi); 67 } else { 68 byte = ca_midi_read_data(midi); 69 if(midi->substream_input) 70 snd_rawmidi_receive(midi->substream_input, &byte, 1); 71 72 73 } 74 } 75 spin_unlock(&midi->input_lock); 76 77 spin_lock(&midi->output_lock); 78 if ((status & midi->ipr_tx) && ca_midi_output_ready(midi)) { 79 if (midi->substream_output && 80 snd_rawmidi_transmit(midi->substream_output, &byte, 1) == 1) { 81 ca_midi_write_data(midi, byte); 82 } else { 83 midi->interrupt_disable(midi,midi->tx_enable); 84 } 85 } 86 spin_unlock(&midi->output_lock); 87 88 } 89 90 static void ca_midi_cmd(struct snd_ca_midi *midi, unsigned char cmd, int ack) 91 { 92 unsigned long flags; 93 int timeout, ok; 94 95 spin_lock_irqsave(&midi->input_lock, flags); 96 ca_midi_write_data(midi, 0x00); 97 /* ca_midi_clear_rx(midi); */ 98 99 ca_midi_write_cmd(midi, cmd); 100 if (ack) { 101 ok = 0; 102 timeout = 10000; 103 while (!ok && timeout-- > 0) { 104 if (ca_midi_input_avail(midi)) { 105 if (ca_midi_read_data(midi) == midi->ack) 106 ok = 1; 107 } 108 } 109 if (!ok && ca_midi_read_data(midi) == midi->ack) 110 ok = 1; 111 } else { 112 ok = 1; 113 } 114 spin_unlock_irqrestore(&midi->input_lock, flags); 115 if (!ok) 116 snd_printk(KERN_ERR "ca_midi_cmd: 0x%x failed at 0x%x (status = 0x%x, data = 0x%x)!!!\n", 117 cmd, 118 midi->get_dev_id_port(midi->dev_id), 119 ca_midi_read_stat(midi), 120 ca_midi_read_data(midi)); 121 } 122 123 static int ca_midi_input_open(struct snd_rawmidi_substream *substream) 124 { 125 struct snd_ca_midi *midi = substream->rmidi->private_data; 126 unsigned long flags; 127 128 snd_assert(midi->dev_id, return -ENXIO); 129 spin_lock_irqsave(&midi->open_lock, flags); 130 midi->midi_mode |= CA_MIDI_MODE_INPUT; 131 midi->substream_input = substream; 132 if (!(midi->midi_mode & CA_MIDI_MODE_OUTPUT)) { 133 spin_unlock_irqrestore(&midi->open_lock, flags); 134 ca_midi_cmd(midi, midi->reset, 1); 135 ca_midi_cmd(midi, midi->enter_uart, 1); 136 } else { 137 spin_unlock_irqrestore(&midi->open_lock, flags); 138 } 139 return 0; 140 } 141 142 static int ca_midi_output_open(struct snd_rawmidi_substream *substream) 143 { 144 struct snd_ca_midi *midi = substream->rmidi->private_data; 145 unsigned long flags; 146 147 snd_assert(midi->dev_id, return -ENXIO); 148 spin_lock_irqsave(&midi->open_lock, flags); 149 midi->midi_mode |= CA_MIDI_MODE_OUTPUT; 150 midi->substream_output = substream; 151 if (!(midi->midi_mode & CA_MIDI_MODE_INPUT)) { 152 spin_unlock_irqrestore(&midi->open_lock, flags); 153 ca_midi_cmd(midi, midi->reset, 1); 154 ca_midi_cmd(midi, midi->enter_uart, 1); 155 } else { 156 spin_unlock_irqrestore(&midi->open_lock, flags); 157 } 158 return 0; 159 } 160 161 static int ca_midi_input_close(struct snd_rawmidi_substream *substream) 162 { 163 struct snd_ca_midi *midi = substream->rmidi->private_data; 164 unsigned long flags; 165 166 snd_assert(midi->dev_id, return -ENXIO); 167 spin_lock_irqsave(&midi->open_lock, flags); 168 midi->interrupt_disable(midi,midi->rx_enable); 169 midi->midi_mode &= ~CA_MIDI_MODE_INPUT; 170 midi->substream_input = NULL; 171 if (!(midi->midi_mode & CA_MIDI_MODE_OUTPUT)) { 172 spin_unlock_irqrestore(&midi->open_lock, flags); 173 ca_midi_cmd(midi, midi->reset, 0); 174 } else { 175 spin_unlock_irqrestore(&midi->open_lock, flags); 176 } 177 return 0; 178 } 179 180 static int ca_midi_output_close(struct snd_rawmidi_substream *substream) 181 { 182 struct snd_ca_midi *midi = substream->rmidi->private_data; 183 unsigned long flags; 184 snd_assert(midi->dev_id, return -ENXIO); 185 186 spin_lock_irqsave(&midi->open_lock, flags); 187 188 midi->interrupt_disable(midi,midi->tx_enable); 189 midi->midi_mode &= ~CA_MIDI_MODE_OUTPUT; 190 midi->substream_output = NULL; 191 192 if (!(midi->midi_mode & CA_MIDI_MODE_INPUT)) { 193 spin_unlock_irqrestore(&midi->open_lock, flags); 194 ca_midi_cmd(midi, midi->reset, 0); 195 } else { 196 spin_unlock_irqrestore(&midi->open_lock, flags); 197 } 198 return 0; 199 } 200 201 static void ca_midi_input_trigger(struct snd_rawmidi_substream *substream, int up) 202 { 203 struct snd_ca_midi *midi = substream->rmidi->private_data; 204 snd_assert(midi->dev_id, return); 205 206 if (up) { 207 midi->interrupt_enable(midi,midi->rx_enable); 208 } else { 209 midi->interrupt_disable(midi, midi->rx_enable); 210 } 211 } 212 213 static void ca_midi_output_trigger(struct snd_rawmidi_substream *substream, int up) 214 { 215 struct snd_ca_midi *midi = substream->rmidi->private_data; 216 unsigned long flags; 217 218 snd_assert(midi->dev_id, return); 219 220 if (up) { 221 int max = 4; 222 unsigned char byte; 223 224 spin_lock_irqsave(&midi->output_lock, flags); 225 226 /* try to send some amount of bytes here before interrupts */ 227 while (max > 0) { 228 if (ca_midi_output_ready(midi)) { 229 if (!(midi->midi_mode & CA_MIDI_MODE_OUTPUT) || 230 snd_rawmidi_transmit(substream, &byte, 1) != 1) { 231 /* no more data */ 232 spin_unlock_irqrestore(&midi->output_lock, flags); 233 return; 234 } 235 ca_midi_write_data(midi, byte); 236 max--; 237 } else { 238 break; 239 } 240 } 241 242 spin_unlock_irqrestore(&midi->output_lock, flags); 243 midi->interrupt_enable(midi,midi->tx_enable); 244 245 } else { 246 midi->interrupt_disable(midi,midi->tx_enable); 247 } 248 } 249 250 static struct snd_rawmidi_ops ca_midi_output = 251 { 252 .open = ca_midi_output_open, 253 .close = ca_midi_output_close, 254 .trigger = ca_midi_output_trigger, 255 }; 256 257 static struct snd_rawmidi_ops ca_midi_input = 258 { 259 .open = ca_midi_input_open, 260 .close = ca_midi_input_close, 261 .trigger = ca_midi_input_trigger, 262 }; 263 264 static void ca_midi_free(struct snd_ca_midi *midi) 265 { 266 midi->interrupt = NULL; 267 midi->interrupt_enable = NULL; 268 midi->interrupt_disable = NULL; 269 midi->read = NULL; 270 midi->write = NULL; 271 midi->get_dev_id_card = NULL; 272 midi->get_dev_id_port = NULL; 273 midi->rmidi = NULL; 274 } 275 276 static void ca_rmidi_free(struct snd_rawmidi *rmidi) 277 { 278 ca_midi_free(rmidi->private_data); 279 } 280 281 int __devinit ca_midi_init(void *dev_id, struct snd_ca_midi *midi, int device, char *name) 282 { 283 struct snd_rawmidi *rmidi; 284 int err; 285 286 if ((err = snd_rawmidi_new(midi->get_dev_id_card(midi->dev_id), name, device, 1, 1, &rmidi)) < 0) 287 return err; 288 289 midi->dev_id = dev_id; 290 midi->interrupt = ca_midi_interrupt; 291 292 spin_lock_init(&midi->open_lock); 293 spin_lock_init(&midi->input_lock); 294 spin_lock_init(&midi->output_lock); 295 296 strcpy(rmidi->name, name); 297 snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &ca_midi_output); 298 snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &ca_midi_input); 299 rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | 300 SNDRV_RAWMIDI_INFO_INPUT | 301 SNDRV_RAWMIDI_INFO_DUPLEX; 302 rmidi->private_data = midi; 303 rmidi->private_free = ca_rmidi_free; 304 305 midi->rmidi = rmidi; 306 return 0; 307 } 308 309