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        snd_assert(midi->dev_id, return -ENXIO);
 129        spin_lock_irqsave(&midi->open_lock, flags);
 130        midi->midi_mode |= CA_MIDI_MODE_INPUT;
 131        midi->substream_input = substream;
 132        if (!(midi->midi_mode & CA_MIDI_MODE_OUTPUT)) {
 133                spin_unlock_irqrestore(&midi->open_lock, flags);
 134                ca_midi_cmd(midi, midi->reset, 1);
 135                ca_midi_cmd(midi, midi->enter_uart, 1);
 136        } else {
 137                spin_unlock_irqrestore(&midi->open_lock, flags);
 138        }
 139        return 0;
 140}
 141
 142static int ca_midi_output_open(struct snd_rawmidi_substream *substream)
 143{
 144        struct snd_ca_midi *midi = substream->rmidi->private_data;
 145        unsigned long flags;
 146
 147        snd_assert(midi->dev_id, return -ENXIO);
 148        spin_lock_irqsave(&midi->open_lock, flags);
 149        midi->midi_mode |= CA_MIDI_MODE_OUTPUT;
 150        midi->substream_output = substream;
 151        if (!(midi->midi_mode & CA_MIDI_MODE_INPUT)) {
 152                spin_unlock_irqrestore(&midi->open_lock, flags);
 153                ca_midi_cmd(midi, midi->reset, 1);
 154                ca_midi_cmd(midi, midi->enter_uart, 1);
 155        } else {
 156                spin_unlock_irqrestore(&midi->open_lock, flags);
 157        }
 158        return 0;
 159}
 160
 161static int ca_midi_input_close(struct snd_rawmidi_substream *substream)
 162{
 163        struct snd_ca_midi *midi = substream->rmidi->private_data;
 164        unsigned long flags;
 165
 166        snd_assert(midi->dev_id, return -ENXIO);
 167        spin_lock_irqsave(&midi->open_lock, flags);
 168        midi->interrupt_disable(midi,midi->rx_enable);
 169        midi->midi_mode &= ~CA_MIDI_MODE_INPUT;
 170        midi->substream_input = NULL;
 171        if (!(midi->midi_mode & CA_MIDI_MODE_OUTPUT)) {
 172                spin_unlock_irqrestore(&midi->open_lock, flags);
 173                ca_midi_cmd(midi, midi->reset, 0);
 174        } else {
 175                spin_unlock_irqrestore(&midi->open_lock, flags);
 176        }
 177        return 0;
 178}
 179
 180static int ca_midi_output_close(struct snd_rawmidi_substream *substream)
 181{
 182        struct snd_ca_midi *midi = substream->rmidi->private_data;
 183        unsigned long flags;
 184        snd_assert(midi->dev_id, return -ENXIO);
 185        
 186        spin_lock_irqsave(&midi->open_lock, flags);
 187
 188        midi->interrupt_disable(midi,midi->tx_enable);
 189        midi->midi_mode &= ~CA_MIDI_MODE_OUTPUT;
 190        midi->substream_output = NULL;
 191        
 192        if (!(midi->midi_mode & CA_MIDI_MODE_INPUT)) {
 193                spin_unlock_irqrestore(&midi->open_lock, flags);
 194                ca_midi_cmd(midi, midi->reset, 0);
 195        } else {
 196                spin_unlock_irqrestore(&midi->open_lock, flags);
 197        }
 198        return 0;
 199}
 200
 201static void ca_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
 202{
 203        struct snd_ca_midi *midi = substream->rmidi->private_data;
 204        snd_assert(midi->dev_id, return);
 205
 206        if (up) {
 207                midi->interrupt_enable(midi,midi->rx_enable);
 208        } else {
 209                midi->interrupt_disable(midi, midi->rx_enable);
 210        }
 211}
 212
 213static void ca_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
 214{
 215        struct snd_ca_midi *midi = substream->rmidi->private_data;
 216        unsigned long flags;
 217
 218        snd_assert(midi->dev_id, return);
 219
 220        if (up) {
 221                int max = 4;
 222                unsigned char byte;
 223
 224                spin_lock_irqsave(&midi->output_lock, flags);
 225        
 226                /* try to send some amount of bytes here before interrupts */
 227                while (max > 0) {
 228                        if (ca_midi_output_ready(midi)) {
 229                                if (!(midi->midi_mode & CA_MIDI_MODE_OUTPUT) ||
 230                                    snd_rawmidi_transmit(substream, &byte, 1) != 1) {
 231                                        /* no more data */
 232                                        spin_unlock_irqrestore(&midi->output_lock, flags);
 233                                        return;
 234                                }
 235                                ca_midi_write_data(midi, byte);
 236                                max--;
 237                        } else {
 238                                break;
 239                        }
 240                }
 241
 242                spin_unlock_irqrestore(&midi->output_lock, flags);
 243                midi->interrupt_enable(midi,midi->tx_enable);
 244
 245        } else {
 246                midi->interrupt_disable(midi,midi->tx_enable);
 247        }
 248}
 249
 250static struct snd_rawmidi_ops ca_midi_output =
 251{
 252        .open =         ca_midi_output_open,
 253        .close =        ca_midi_output_close,
 254        .trigger =      ca_midi_output_trigger,
 255};
 256
 257static struct snd_rawmidi_ops ca_midi_input =
 258{
 259        .open =         ca_midi_input_open,
 260        .close =        ca_midi_input_close,
 261        .trigger =      ca_midi_input_trigger,
 262};
 263
 264static void ca_midi_free(struct snd_ca_midi *midi)
 265{
 266        midi->interrupt = NULL;
 267        midi->interrupt_enable = NULL;
 268        midi->interrupt_disable = NULL;
 269        midi->read = NULL;
 270        midi->write = NULL;
 271        midi->get_dev_id_card = NULL;
 272        midi->get_dev_id_port = NULL;
 273        midi->rmidi = NULL;
 274}
 275
 276static void ca_rmidi_free(struct snd_rawmidi *rmidi)
 277{
 278        ca_midi_free(rmidi->private_data);
 279}
 280
 281int __devinit ca_midi_init(void *dev_id, struct snd_ca_midi *midi, int device, char *name)
 282{
 283        struct snd_rawmidi *rmidi;
 284        int err;
 285
 286        if ((err = snd_rawmidi_new(midi->get_dev_id_card(midi->dev_id), name, device, 1, 1, &rmidi)) < 0)
 287                return err;
 288
 289        midi->dev_id = dev_id;
 290        midi->interrupt = ca_midi_interrupt;
 291
 292        spin_lock_init(&midi->open_lock);
 293        spin_lock_init(&midi->input_lock);
 294        spin_lock_init(&midi->output_lock);
 295
 296        strcpy(rmidi->name, name);
 297        snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &ca_midi_output);
 298        snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &ca_midi_input);
 299        rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT |
 300                             SNDRV_RAWMIDI_INFO_INPUT |
 301                             SNDRV_RAWMIDI_INFO_DUPLEX;
 302        rmidi->private_data = midi;
 303        rmidi->private_free = ca_rmidi_free;
 304        
 305        midi->rmidi = rmidi;
 306        return 0;
 307}
 308
 309
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.