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