linux/sound/pci/ca0106/ca_midi.c
<<
>>
Prefs
   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        if ((err = snd_rawmidi_new(midi->get_dev_id_card(midi->dev_id), name, device, 1, 1, &rmidi)) < 0)
 280                return err;
 281
 282        midi->dev_id = dev_id;
 283        midi->interrupt = ca_midi_interrupt;
 284
 285        spin_lock_init(&midi->open_lock);
 286        spin_lock_init(&midi->input_lock);
 287        spin_lock_init(&midi->output_lock);
 288
 289        strcpy(rmidi->name, name);
 290        snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &ca_midi_output);
 291        snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &ca_midi_input);
 292        rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT |
 293                             SNDRV_RAWMIDI_INFO_INPUT |
 294                             SNDRV_RAWMIDI_INFO_DUPLEX;
 295        rmidi->private_data = midi;
 296        rmidi->private_free = ca_rmidi_free;
 297        
 298        midi->rmidi = rmidi;
 299        return 0;
 300}
 301
 302