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 pr_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 pr_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 if (snd_BUG_ON(!midi->dev_id)) 129 return -ENXIO; 130 spin_lock_irqsave(&midi->open_lock, flags); 131 midi->midi_mode |= CA_MIDI_MODE_INPUT; 132 midi->substream_input = substream; 133 if (!(midi->midi_mode & CA_MIDI_MODE_OUTPUT)) { 134 spin_unlock_irqrestore(&midi->open_lock, flags); 135 ca_midi_cmd(midi, midi->reset, 1); 136 ca_midi_cmd(midi, midi->enter_uart, 1); 137 } else { 138 spin_unlock_irqrestore(&midi->open_lock, flags); 139 } 140 return 0; 141 } 142 143 static int ca_midi_output_open(struct snd_rawmidi_substream *substream) 144 { 145 struct snd_ca_midi *midi = substream->rmidi->private_data; 146 unsigned long flags; 147 148 if (snd_BUG_ON(!midi->dev_id)) 149 return -ENXIO; 150 spin_lock_irqsave(&midi->open_lock, flags); 151 midi->midi_mode |= CA_MIDI_MODE_OUTPUT; 152 midi->substream_output = substream; 153 if (!(midi->midi_mode & CA_MIDI_MODE_INPUT)) { 154 spin_unlock_irqrestore(&midi->open_lock, flags); 155 ca_midi_cmd(midi, midi->reset, 1); 156 ca_midi_cmd(midi, midi->enter_uart, 1); 157 } else { 158 spin_unlock_irqrestore(&midi->open_lock, flags); 159 } 160 return 0; 161 } 162 163 static int ca_midi_input_close(struct snd_rawmidi_substream *substream) 164 { 165 struct snd_ca_midi *midi = substream->rmidi->private_data; 166 unsigned long flags; 167 168 if (snd_BUG_ON(!midi->dev_id)) 169 return -ENXIO; 170 spin_lock_irqsave(&midi->open_lock, flags); 171 midi->interrupt_disable(midi,midi->rx_enable); 172 midi->midi_mode &= ~CA_MIDI_MODE_INPUT; 173 midi->substream_input = NULL; 174 if (!(midi->midi_mode & CA_MIDI_MODE_OUTPUT)) { 175 spin_unlock_irqrestore(&midi->open_lock, flags); 176 ca_midi_cmd(midi, midi->reset, 0); 177 } else { 178 spin_unlock_irqrestore(&midi->open_lock, flags); 179 } 180 return 0; 181 } 182 183 static int ca_midi_output_close(struct snd_rawmidi_substream *substream) 184 { 185 struct snd_ca_midi *midi = substream->rmidi->private_data; 186 unsigned long flags; 187 188 if (snd_BUG_ON(!midi->dev_id)) 189 return -ENXIO; 190 191 spin_lock_irqsave(&midi->open_lock, flags); 192 193 midi->interrupt_disable(midi,midi->tx_enable); 194 midi->midi_mode &= ~CA_MIDI_MODE_OUTPUT; 195 midi->substream_output = NULL; 196 197 if (!(midi->midi_mode & CA_MIDI_MODE_INPUT)) { 198 spin_unlock_irqrestore(&midi->open_lock, flags); 199 ca_midi_cmd(midi, midi->reset, 0); 200 } else { 201 spin_unlock_irqrestore(&midi->open_lock, flags); 202 } 203 return 0; 204 } 205 206 static void ca_midi_input_trigger(struct snd_rawmidi_substream *substream, int up) 207 { 208 struct snd_ca_midi *midi = substream->rmidi->private_data; 209 210 if (snd_BUG_ON(!midi->dev_id)) 211 return; 212 213 if (up) { 214 midi->interrupt_enable(midi,midi->rx_enable); 215 } else { 216 midi->interrupt_disable(midi, midi->rx_enable); 217 } 218 } 219 220 static void ca_midi_output_trigger(struct snd_rawmidi_substream *substream, int up) 221 { 222 struct snd_ca_midi *midi = substream->rmidi->private_data; 223 unsigned long flags; 224 225 if (snd_BUG_ON(!midi->dev_id)) 226 return; 227 228 if (up) { 229 int max = 4; 230 unsigned char byte; 231 232 spin_lock_irqsave(&midi->output_lock, flags); 233 234 /* try to send some amount of bytes here before interrupts */ 235 while (max > 0) { 236 if (ca_midi_output_ready(midi)) { 237 if (!(midi->midi_mode & CA_MIDI_MODE_OUTPUT) || 238 snd_rawmidi_transmit(substream, &byte, 1) != 1) { 239 /* no more data */ 240 spin_unlock_irqrestore(&midi->output_lock, flags); 241 return; 242 } 243 ca_midi_write_data(midi, byte); 244 max--; 245 } else { 246 break; 247 } 248 } 249 250 spin_unlock_irqrestore(&midi->output_lock, flags); 251 midi->interrupt_enable(midi,midi->tx_enable); 252 253 } else { 254 midi->interrupt_disable(midi,midi->tx_enable); 255 } 256 } 257 258 static const struct snd_rawmidi_ops ca_midi_output = 259 { 260 .open = ca_midi_output_open, 261 .close = ca_midi_output_close, 262 .trigger = ca_midi_output_trigger, 263 }; 264 265 static const struct snd_rawmidi_ops ca_midi_input = 266 { 267 .open = ca_midi_input_open, 268 .close = ca_midi_input_close, 269 .trigger = ca_midi_input_trigger, 270 }; 271 272 static void ca_midi_free(struct snd_ca_midi *midi) 273 { 274 midi->interrupt = NULL; 275 midi->interrupt_enable = NULL; 276 midi->interrupt_disable = NULL; 277 midi->read = NULL; 278 midi->write = NULL; 279 midi->get_dev_id_card = NULL; 280 midi->get_dev_id_port = NULL; 281 midi->rmidi = NULL; 282 } 283 284 static void ca_rmidi_free(struct snd_rawmidi *rmidi) 285 { 286 ca_midi_free(rmidi->private_data); 287 } 288 289 int ca_midi_init(void *dev_id, struct snd_ca_midi *midi, int device, char *name) 290 { 291 struct snd_rawmidi *rmidi; 292 int err; 293 294 if ((err = snd_rawmidi_new(midi->get_dev_id_card(midi->dev_id), name, device, 1, 1, &rmidi)) < 0) 295 return err; 296 297 midi->dev_id = dev_id; 298 midi->interrupt = ca_midi_interrupt; 299 300 spin_lock_init(&midi->open_lock); 301 spin_lock_init(&midi->input_lock); 302 spin_lock_init(&midi->output_lock); 303 304 strcpy(rmidi->name, name); 305 snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &ca_midi_output); 306 snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &ca_midi_input); 307 rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | 308 SNDRV_RAWMIDI_INFO_INPUT | 309 SNDRV_RAWMIDI_INFO_DUPLEX; 310 rmidi->private_data = midi; 311 rmidi->private_free = ca_rmidi_free; 312 313 midi->rmidi = rmidi; 314 return 0; 315 } 316 317