linux/sound/pci/ca0106/ca_midi.c
<<
>>
Prefs
   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@perex.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
 310