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