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@suse.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/driver.h> 31 #include <sound/core.h> 32 #include <sound/rawmidi.h> 33 34 #include "ca_midi.h" 35 36 #define ca_midi_write_data(midi, data) midi->write(midi, data, 0) 37 #define ca_midi_write_cmd(midi, data) midi->write(midi, data, 1) 38 #define ca_midi_read_data(midi) midi->read(midi, 0) 39 #define ca_midi_read_stat(midi) midi->read(midi, 1) 40 #define ca_midi_input_avail(midi) (!(ca_midi_read_stat(midi) & midi->input_avail)) 41 #define ca_midi_output_ready(midi) (!(ca_midi_read_stat(midi) & midi->output_ready)) 42 43 static void ca_midi_clear_rx(ca_midi_t *midi) 44 { 45 int timeout = 100000; 46 for (; timeout > 0 && ca_midi_input_avail(midi); timeout--) 47 ca_midi_read_data(midi); 48 #ifdef CONFIG_SND_DEBUG 49 if (timeout <= 0) 50 snd_printk(KERN_ERR "ca_midi_clear_rx: timeout (status = 0x%x)\n", ca_midi_read_stat(midi)); 51 #endif 52 } 53 54 static void ca_midi_interrupt(ca_midi_t *midi, unsigned int status) { 55 unsigned char byte; 56 57 if (midi->rmidi == NULL) { 58 midi->interrupt_disable(midi,midi->tx_enable | midi->rx_enable); 59 return; 60 } 61 62 spin_lock(&midi->input_lock); 63 if ((status & midi->ipr_rx) && ca_midi_input_avail(midi)) { 64 if (!(midi->midi_mode & CA_MIDI_MODE_INPUT)) { 65 ca_midi_clear_rx(midi); 66 } else { 67 byte = ca_midi_read_data(midi); 68 if(midi->substream_input) 69 snd_rawmidi_receive(midi->substream_input, &byte, 1); 70 71 72 } 73 } 74 spin_unlock(&midi->input_lock); 75 76 spin_lock(&midi->output_lock); 77 if ((status & midi->ipr_tx) && ca_midi_output_ready(midi)) { 78 if (midi->substream_output && 79 snd_rawmidi_transmit(midi->substream_output, &byte, 1) == 1) { 80 ca_midi_write_data(midi, byte); 81 } else { 82 midi->interrupt_disable(midi,midi->tx_enable); 83 } 84 } 85 spin_unlock(&midi->output_lock); 86 87 } 88 89 static void ca_midi_cmd(ca_midi_t *midi, unsigned char cmd, int ack) 90 { 91 unsigned long flags; 92 int timeout, ok; 93 94 spin_lock_irqsave(&midi->input_lock, flags); 95 ca_midi_write_data(midi, 0x00); 96 /* ca_midi_clear_rx(midi); */ 97 98 ca_midi_write_cmd(midi, cmd); 99 if (ack) { 100 ok = 0; 101 timeout = 10000; 102 while (!ok && timeout-- > 0) { 103 if (ca_midi_input_avail(midi)) { 104 if (ca_midi_read_data(midi) == midi->ack) 105 ok = 1; 106 } 107 } 108 if (!ok && ca_midi_read_data(midi) == midi->ack) 109 ok = 1; 110 } else { 111 ok = 1; 112 } 113 spin_unlock_irqrestore(&midi->input_lock, flags); 114 if (!ok) 115 snd_printk(KERN_ERR "ca_midi_cmd: 0x%x failed at 0x%x (status = 0x%x, data = 0x%x)!!!\n", 116 cmd, 117 midi->get_dev_id_port(midi->dev_id), 118 ca_midi_read_stat(midi), 119 ca_midi_read_data(midi)); 120 } 121 122 static int ca_midi_input_open(snd_rawmidi_substream_t * substream) 123 { 124 ca_midi_t *midi = (ca_midi_t *)substream->rmidi->private_data; 125 unsigned long flags; 126 127 snd_assert(midi->dev_id, return -ENXIO); 128 spin_lock_irqsave(&midi->open_lock, flags); 129 midi->midi_mode |= CA_MIDI_MODE_INPUT; 130 midi->substream_input = substream; 131 if (!(midi->midi_mode & CA_MIDI_MODE_OUTPUT)) { 132 spin_unlock_irqrestore(&midi->open_lock, flags); 133 ca_midi_cmd(midi, midi->reset, 1); 134 ca_midi_cmd(midi, midi->enter_uart, 1); 135 } else { 136 spin_unlock_irqrestore(&midi->open_lock, flags); 137 } 138 return 0; 139 } 140 141 static int ca_midi_output_open(snd_rawmidi_substream_t * substream) 142 { 143 ca_midi_t *midi = (ca_midi_t *)substream->rmidi->private_data; 144 unsigned long flags; 145 146 snd_assert(midi->dev_id, return -ENXIO); 147 spin_lock_irqsave(&midi->open_lock, flags); 148 midi->midi_mode |= CA_MIDI_MODE_OUTPUT; 149 midi->substream_output = substream; 150 if (!(midi->midi_mode & CA_MIDI_MODE_INPUT)) { 151 spin_unlock_irqrestore(&midi->open_lock, flags); 152 ca_midi_cmd(midi, midi->reset, 1); 153 ca_midi_cmd(midi, midi->enter_uart, 1); 154 } else { 155 spin_unlock_irqrestore(&midi->open_lock, flags); 156 } 157 return 0; 158 } 159 160 static int ca_midi_input_close(snd_rawmidi_substream_t * substream) 161 { 162 ca_midi_t *midi = (ca_midi_t *)substream->rmidi->private_data; 163 unsigned long flags; 164 165 snd_assert(midi->dev_id, return -ENXIO); 166 spin_lock_irqsave(&midi->open_lock, flags); 167 midi->interrupt_disable(midi,midi->rx_enable); 168 midi->midi_mode &= ~CA_MIDI_MODE_INPUT; 169 midi->substream_input = NULL; 170 if (!(midi->midi_mode & CA_MIDI_MODE_OUTPUT)) { 171 spin_unlock_irqrestore(&midi->open_lock, flags); 172 ca_midi_cmd(midi, midi->reset, 0); 173 } else { 174 spin_unlock_irqrestore(&midi->open_lock, flags); 175 } 176 return 0; 177 } 178 179 static int ca_midi_output_close(snd_rawmidi_substream_t * substream) 180 { 181 ca_midi_t *midi = (ca_midi_t *)substream->rmidi->private_data; 182 unsigned long flags; 183 snd_assert(midi->dev_id, return -ENXIO); 184 185 spin_lock_irqsave(&midi->open_lock, flags); 186 187 midi->interrupt_disable(midi,midi->tx_enable); 188 midi->midi_mode &= ~CA_MIDI_MODE_OUTPUT; 189 midi->substream_output = NULL; 190 191 if (!(midi->midi_mode & CA_MIDI_MODE_INPUT)) { 192 spin_unlock_irqrestore(&midi->open_lock, flags); 193 ca_midi_cmd(midi, midi->reset, 0); 194 } else { 195 spin_unlock_irqrestore(&midi->open_lock, flags); 196 } 197 return 0; 198 } 199 200 static void ca_midi_input_trigger(snd_rawmidi_substream_t * substream, int up) 201 { 202 ca_midi_t *midi = (ca_midi_t *)substream->rmidi->private_data; 203 snd_assert(midi->dev_id, return); 204 205 if (up) { 206 midi->interrupt_enable(midi,midi->rx_enable); 207 } else { 208 midi->interrupt_disable(midi, midi->rx_enable); 209 } 210 } 211 212 static void ca_midi_output_trigger(snd_rawmidi_substream_t * substream, int up) 213 { 214 ca_midi_t *midi = (ca_midi_t *)substream->rmidi->private_data; 215 unsigned long flags; 216 217 snd_assert(midi->dev_id, return); 218 219 if (up) { 220 int max = 4; 221 unsigned char byte; 222 223 spin_lock_irqsave(&midi->output_lock, flags); 224 225 /* try to send some amount of bytes here before interrupts */ 226 while (max > 0) { 227 if (ca_midi_output_ready(midi)) { 228 if (!(midi->midi_mode & CA_MIDI_MODE_OUTPUT) || 229 snd_rawmidi_transmit(substream, &byte, 1) != 1) { 230 /* no more data */ 231 spin_unlock_irqrestore(&midi->output_lock, flags); 232 return; 233 } 234 ca_midi_write_data(midi, byte); 235 max--; 236 } else { 237 break; 238 } 239 } 240 241 spin_unlock_irqrestore(&midi->output_lock, flags); 242 midi->interrupt_enable(midi,midi->tx_enable); 243 244 } else { 245 midi->interrupt_disable(midi,midi->tx_enable); 246 } 247 } 248 249 static snd_rawmidi_ops_t ca_midi_output = 250 { 251 .open = ca_midi_output_open, 252 .close = ca_midi_output_close, 253 .trigger = ca_midi_output_trigger, 254 }; 255 256 static snd_rawmidi_ops_t ca_midi_input = 257 { 258 .open = ca_midi_input_open, 259 .close = ca_midi_input_close, 260 .trigger = ca_midi_input_trigger, 261 }; 262 263 static void ca_midi_free(ca_midi_t *midi) { 264 midi->interrupt = NULL; 265 midi->interrupt_enable = NULL; 266 midi->interrupt_disable = NULL; 267 midi->read = NULL; 268 midi->write = NULL; 269 midi->get_dev_id_card = NULL; 270 midi->get_dev_id_port = NULL; 271 midi->rmidi = NULL; 272 } 273 274 static void ca_rmidi_free(snd_rawmidi_t *rmidi) 275 { 276 ca_midi_free((ca_midi_t *)rmidi->private_data); 277 } 278 279 int __devinit ca_midi_init(void *dev_id, ca_midi_t *midi, int device, char *name) 280 { 281 snd_rawmidi_t *rmidi; 282 int err; 283 284 if ((err = snd_rawmidi_new(midi->get_dev_id_card(midi->dev_id), name, device, 1, 1, &rmidi)) < 0) 285 return err; 286 287 midi->dev_id = dev_id; 288 midi->interrupt = ca_midi_interrupt; 289 290 spin_lock_init(&midi->open_lock); 291 spin_lock_init(&midi->input_lock); 292 spin_lock_init(&midi->output_lock); 293 294 strcpy(rmidi->name, name); 295 snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &ca_midi_output); 296 snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &ca_midi_input); 297 rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | 298 SNDRV_RAWMIDI_INFO_INPUT | 299 SNDRV_RAWMIDI_INFO_DUPLEX; 300 rmidi->private_data = midi; 301 rmidi->private_free = ca_rmidi_free; 302 303 midi->rmidi = rmidi; 304 return 0; 305 } 306 307