FILE
ca_midi.c
File preview with syntax highlighting.
linux-6.19.14/sound/pci/ca0106/ca_midi.c
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 scoped_guard(spinlock, &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 scoped_guard(spinlock, &midi->output_lock) { 61 if ((status & midi->ipr_tx) && ca_midi_output_ready(midi)) { 62 if (midi->substream_output && 63 snd_rawmidi_transmit(midi->substream_output, &byte, 1) == 1) { 64 ca_midi_write_data(midi, byte); 65 } else { 66 midi->interrupt_disable(midi, midi->tx_enable); 67 } 68 } 69 } 70} 71 72static void ca_midi_cmd(struct snd_ca_midi *midi, unsigned char cmd, int ack) 73{ 74 int timeout, ok; 75 76 scoped_guard(spinlock_irqsave, &midi->input_lock) { 77 ca_midi_write_data(midi, 0x00); 78 /* ca_midi_clear_rx(midi); */ 79 80 ca_midi_write_cmd(midi, cmd); 81 if (ack) { 82 ok = 0; 83 timeout = 10000; 84 while (!ok && timeout-- > 0) { 85 if (ca_midi_input_avail(midi)) { 86 if (ca_midi_read_data(midi) == midi->ack) 87 ok = 1; 88 } 89 } 90 if (!ok && ca_midi_read_data(midi) == midi->ack) 91 ok = 1; 92 } else { 93 ok = 1; 94 } 95 } 96 if (!ok) 97 pr_err("ca_midi_cmd: 0x%x failed at 0x%x (status = 0x%x, data = 0x%x)!!!\n", 98 cmd, 99 midi->get_dev_id_port(midi->dev_id), 100 ca_midi_read_stat(midi), 101 ca_midi_read_data(midi)); 102} 103 104static int ca_midi_input_open(struct snd_rawmidi_substream *substream) 105{ 106 struct snd_ca_midi *midi = substream->rmidi->private_data; 107 108 if (snd_BUG_ON(!midi->dev_id)) 109 return -ENXIO; 110 scoped_guard(spinlock_irqsave, &midi->open_lock) { 111 midi->midi_mode |= CA_MIDI_MODE_INPUT; 112 midi->substream_input = substream; 113 if (midi->midi_mode & CA_MIDI_MODE_OUTPUT) 114 return 0; 115 } 116 ca_midi_cmd(midi, midi->reset, 1); 117 ca_midi_cmd(midi, midi->enter_uart, 1); 118 return 0; 119} 120 121static int ca_midi_output_open(struct snd_rawmidi_substream *substream) 122{ 123 struct snd_ca_midi *midi = substream->rmidi->private_data; 124 125 if (snd_BUG_ON(!midi->dev_id)) 126 return -ENXIO; 127 scoped_guard(spinlock_irqsave, &midi->open_lock) { 128 midi->midi_mode |= CA_MIDI_MODE_OUTPUT; 129 midi->substream_output = substream; 130 if (midi->midi_mode & CA_MIDI_MODE_INPUT) 131 return 0; 132 } 133 ca_midi_cmd(midi, midi->reset, 1); 134 ca_midi_cmd(midi, midi->enter_uart, 1); 135 return 0; 136} 137 138static int ca_midi_input_close(struct snd_rawmidi_substream *substream) 139{ 140 struct snd_ca_midi *midi = substream->rmidi->private_data; 141 142 if (snd_BUG_ON(!midi->dev_id)) 143 return -ENXIO; 144 scoped_guard(spinlock_irqsave, &midi->open_lock) { 145 midi->interrupt_disable(midi, midi->rx_enable); 146 midi->midi_mode &= ~CA_MIDI_MODE_INPUT; 147 midi->substream_input = NULL; 148 if (midi->midi_mode & CA_MIDI_MODE_OUTPUT) 149 return 0; 150 } 151 ca_midi_cmd(midi, midi->reset, 0); 152 return 0; 153} 154 155static int ca_midi_output_close(struct snd_rawmidi_substream *substream) 156{ 157 struct snd_ca_midi *midi = substream->rmidi->private_data; 158 159 if (snd_BUG_ON(!midi->dev_id)) 160 return -ENXIO; 161 162 scoped_guard(spinlock_irqsave, &midi->open_lock) { 163 midi->interrupt_disable(midi, midi->tx_enable); 164 midi->midi_mode &= ~CA_MIDI_MODE_OUTPUT; 165 midi->substream_output = NULL; 166 if (midi->midi_mode & CA_MIDI_MODE_INPUT) 167 return 0; 168 } 169 ca_midi_cmd(midi, midi->reset, 0); 170 return 0; 171} 172 173static void ca_midi_input_trigger(struct snd_rawmidi_substream *substream, int up) 174{ 175 struct snd_ca_midi *midi = substream->rmidi->private_data; 176 177 if (snd_BUG_ON(!midi->dev_id)) 178 return; 179 180 if (up) { 181 midi->interrupt_enable(midi,midi->rx_enable); 182 } else { 183 midi->interrupt_disable(midi, midi->rx_enable); 184 } 185} 186 187static void ca_midi_output_trigger(struct snd_rawmidi_substream *substream, int up) 188{ 189 struct snd_ca_midi *midi = substream->rmidi->private_data; 190 191 if (snd_BUG_ON(!midi->dev_id)) 192 return; 193 194 if (up) { 195 int max = 4; 196 unsigned char byte; 197 198 scoped_guard(spinlock_irqsave, &midi->output_lock) { 199 200 /* try to send some amount of bytes here before interrupts */ 201 while (max > 0) { 202 if (ca_midi_output_ready(midi)) { 203 if (!(midi->midi_mode & CA_MIDI_MODE_OUTPUT) || 204 snd_rawmidi_transmit(substream, &byte, 1) != 1) { 205 /* no more data */ 206 return; 207 } 208 ca_midi_write_data(midi, byte); 209 max--; 210 } else { 211 break; 212 } 213 } 214 } 215 midi->interrupt_enable(midi,midi->tx_enable); 216 217 } else { 218 midi->interrupt_disable(midi,midi->tx_enable); 219 } 220} 221 222static const struct snd_rawmidi_ops ca_midi_output = 223{ 224 .open = ca_midi_output_open, 225 .close = ca_midi_output_close, 226 .trigger = ca_midi_output_trigger, 227}; 228 229static const struct snd_rawmidi_ops ca_midi_input = 230{ 231 .open = ca_midi_input_open, 232 .close = ca_midi_input_close, 233 .trigger = ca_midi_input_trigger, 234}; 235 236static void ca_midi_free(struct snd_ca_midi *midi) 237{ 238 midi->interrupt = NULL; 239 midi->interrupt_enable = NULL; 240 midi->interrupt_disable = NULL; 241 midi->read = NULL; 242 midi->write = NULL; 243 midi->get_dev_id_card = NULL; 244 midi->get_dev_id_port = NULL; 245 midi->rmidi = NULL; 246} 247 248static void ca_rmidi_free(struct snd_rawmidi *rmidi) 249{ 250 ca_midi_free(rmidi->private_data); 251} 252 253int ca_midi_init(void *dev_id, struct snd_ca_midi *midi, int device, char *name) 254{ 255 struct snd_rawmidi *rmidi; 256 int err; 257 258 err = snd_rawmidi_new(midi->get_dev_id_card(midi->dev_id), name, device, 1, 1, &rmidi); 259 if (err < 0) 260 return err; 261 262 midi->dev_id = dev_id; 263 midi->interrupt = ca_midi_interrupt; 264 265 spin_lock_init(&midi->open_lock); 266 spin_lock_init(&midi->input_lock); 267 spin_lock_init(&midi->output_lock); 268 269 strscpy(rmidi->name, name); 270 snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &ca_midi_output); 271 snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &ca_midi_input); 272 rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | 273 SNDRV_RAWMIDI_INFO_INPUT | 274 SNDRV_RAWMIDI_INFO_DUPLEX; 275 rmidi->private_data = midi; 276 rmidi->private_free = ca_rmidi_free; 277 278 midi->rmidi = rmidi; 279 return 0; 280} 281
~/
Read-only file preview.