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