linux/sound/isa/sb/sb8_midi.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
   4 *  Routines for control of SoundBlaster cards - MIDI interface
   5 *
   6 * --
   7 *
   8 * Sun May  9 22:54:38 BST 1999 George David Morrison <gdm@gedamo.demon.co.uk>
   9 *   Fixed typo in snd_sb8dsp_midi_new_device which prevented midi from 
  10 *   working.
  11 *
  12 * Sun May 11 12:34:56 UTC 2003 Clemens Ladisch <clemens@ladisch.de>
  13 *   Added full duplex UART mode for DSP version 2.0 and later.
  14 */
  15
  16#include <linux/io.h>
  17#include <linux/time.h>
  18#include <sound/core.h>
  19#include <sound/sb.h>
  20
  21
  22irqreturn_t snd_sb8dsp_midi_interrupt(struct snd_sb *chip)
  23{
  24        struct snd_rawmidi *rmidi;
  25        int max = 64;
  26        char byte;
  27
  28        if (!chip)
  29                return IRQ_NONE;
  30        
  31        rmidi = chip->rmidi;
  32        if (!rmidi) {
  33                inb(SBP(chip, DATA_AVAIL));     /* ack interrupt */
  34                return IRQ_NONE;
  35        }
  36
  37        spin_lock(&chip->midi_input_lock);
  38        while (max-- > 0) {
  39                if (inb(SBP(chip, DATA_AVAIL)) & 0x80) {
  40                        byte = inb(SBP(chip, READ));
  41                        if (chip->open & SB_OPEN_MIDI_INPUT_TRIGGER) {
  42                                snd_rawmidi_receive(chip->midi_substream_input, &byte, 1);
  43                        }
  44                }
  45        }
  46        spin_unlock(&chip->midi_input_lock);
  47        return IRQ_HANDLED;
  48}
  49
  50static int snd_sb8dsp_midi_input_open(struct snd_rawmidi_substream *substream)
  51{
  52        unsigned long flags;
  53        struct snd_sb *chip;
  54        unsigned int valid_open_flags;
  55
  56        chip = substream->rmidi->private_data;
  57        valid_open_flags = chip->hardware >= SB_HW_20
  58                ? SB_OPEN_MIDI_OUTPUT | SB_OPEN_MIDI_OUTPUT_TRIGGER : 0;
  59        spin_lock_irqsave(&chip->open_lock, flags);
  60        if (chip->open & ~valid_open_flags) {
  61                spin_unlock_irqrestore(&chip->open_lock, flags);
  62                return -EAGAIN;
  63        }
  64        chip->open |= SB_OPEN_MIDI_INPUT;
  65        chip->midi_substream_input = substream;
  66        if (!(chip->open & SB_OPEN_MIDI_OUTPUT)) {
  67                spin_unlock_irqrestore(&chip->open_lock, flags);
  68                snd_sbdsp_reset(chip);          /* reset DSP */
  69                if (chip->hardware >= SB_HW_20)
  70                        snd_sbdsp_command(chip, SB_DSP_MIDI_UART_IRQ);
  71        } else {
  72                spin_unlock_irqrestore(&chip->open_lock, flags);
  73        }
  74        return 0;
  75}
  76
  77static int snd_sb8dsp_midi_output_open(struct snd_rawmidi_substream *substream)
  78{
  79        unsigned long flags;
  80        struct snd_sb *chip;
  81        unsigned int valid_open_flags;
  82
  83        chip = substream->rmidi->private_data;
  84        valid_open_flags = chip->hardware >= SB_HW_20
  85                ? SB_OPEN_MIDI_INPUT | SB_OPEN_MIDI_INPUT_TRIGGER : 0;
  86        spin_lock_irqsave(&chip->open_lock, flags);
  87        if (chip->open & ~valid_open_flags) {
  88                spin_unlock_irqrestore(&chip->open_lock, flags);
  89                return -EAGAIN;
  90        }
  91        chip->open |= SB_OPEN_MIDI_OUTPUT;
  92        chip->midi_substream_output = substream;
  93        if (!(chip->open & SB_OPEN_MIDI_INPUT)) {
  94                spin_unlock_irqrestore(&chip->open_lock, flags);
  95                snd_sbdsp_reset(chip);          /* reset DSP */
  96                if (chip->hardware >= SB_HW_20)
  97                        snd_sbdsp_command(chip, SB_DSP_MIDI_UART_IRQ);
  98        } else {
  99                spin_unlock_irqrestore(&chip->open_lock, flags);
 100        }
 101        return 0;
 102}
 103
 104static int snd_sb8dsp_midi_input_close(struct snd_rawmidi_substream *substream)
 105{
 106        unsigned long flags;
 107        struct snd_sb *chip;
 108
 109        chip = substream->rmidi->private_data;
 110        spin_lock_irqsave(&chip->open_lock, flags);
 111        chip->open &= ~(SB_OPEN_MIDI_INPUT | SB_OPEN_MIDI_INPUT_TRIGGER);
 112        chip->midi_substream_input = NULL;
 113        if (!(chip->open & SB_OPEN_MIDI_OUTPUT)) {
 114                spin_unlock_irqrestore(&chip->open_lock, flags);
 115                snd_sbdsp_reset(chip);          /* reset DSP */
 116        } else {
 117                spin_unlock_irqrestore(&chip->open_lock, flags);
 118        }
 119        return 0;
 120}
 121
 122static int snd_sb8dsp_midi_output_close(struct snd_rawmidi_substream *substream)
 123{
 124        unsigned long flags;
 125        struct snd_sb *chip;
 126
 127        chip = substream->rmidi->private_data;
 128        del_timer_sync(&chip->midi_timer);
 129        spin_lock_irqsave(&chip->open_lock, flags);
 130        chip->open &= ~(SB_OPEN_MIDI_OUTPUT | SB_OPEN_MIDI_OUTPUT_TRIGGER);
 131        chip->midi_substream_output = NULL;
 132        if (!(chip->open & SB_OPEN_MIDI_INPUT)) {
 133                spin_unlock_irqrestore(&chip->open_lock, flags);
 134                snd_sbdsp_reset(chip);          /* reset DSP */
 135        } else {
 136                spin_unlock_irqrestore(&chip->open_lock, flags);
 137        }
 138        return 0;
 139}
 140
 141static void snd_sb8dsp_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
 142{
 143        unsigned long flags;
 144        struct snd_sb *chip;
 145
 146        chip = substream->rmidi->private_data;
 147        spin_lock_irqsave(&chip->open_lock, flags);
 148        if (up) {
 149                if (!(chip->open & SB_OPEN_MIDI_INPUT_TRIGGER)) {
 150                        if (chip->hardware < SB_HW_20)
 151                                snd_sbdsp_command(chip, SB_DSP_MIDI_INPUT_IRQ);
 152                        chip->open |= SB_OPEN_MIDI_INPUT_TRIGGER;
 153                }
 154        } else {
 155                if (chip->open & SB_OPEN_MIDI_INPUT_TRIGGER) {
 156                        if (chip->hardware < SB_HW_20)
 157                                snd_sbdsp_command(chip, SB_DSP_MIDI_INPUT_IRQ);
 158                        chip->open &= ~SB_OPEN_MIDI_INPUT_TRIGGER;
 159                }
 160        }
 161        spin_unlock_irqrestore(&chip->open_lock, flags);
 162}
 163
 164static void snd_sb8dsp_midi_output_write(struct snd_rawmidi_substream *substream)
 165{
 166        unsigned long flags;
 167        struct snd_sb *chip;
 168        char byte;
 169        int max = 32;
 170
 171        /* how big is Tx FIFO? */
 172        chip = substream->rmidi->private_data;
 173        while (max-- > 0) {
 174                spin_lock_irqsave(&chip->open_lock, flags);
 175                if (snd_rawmidi_transmit_peek(substream, &byte, 1) != 1) {
 176                        chip->open &= ~SB_OPEN_MIDI_OUTPUT_TRIGGER;
 177                        del_timer(&chip->midi_timer);
 178                        spin_unlock_irqrestore(&chip->open_lock, flags);
 179                        break;
 180                }
 181                if (chip->hardware >= SB_HW_20) {
 182                        int timeout = 8;
 183                        while ((inb(SBP(chip, STATUS)) & 0x80) != 0 && --timeout > 0)
 184                                ;
 185                        if (timeout == 0) {
 186                                /* Tx FIFO full - try again later */
 187                                spin_unlock_irqrestore(&chip->open_lock, flags);
 188                                break;
 189                        }
 190                        outb(byte, SBP(chip, WRITE));
 191                } else {
 192                        snd_sbdsp_command(chip, SB_DSP_MIDI_OUTPUT);
 193                        snd_sbdsp_command(chip, byte);
 194                }
 195                snd_rawmidi_transmit_ack(substream, 1);
 196                spin_unlock_irqrestore(&chip->open_lock, flags);
 197        }
 198}
 199
 200static void snd_sb8dsp_midi_output_timer(struct timer_list *t)
 201{
 202        struct snd_sb *chip = from_timer(chip, t, midi_timer);
 203        struct snd_rawmidi_substream *substream = chip->midi_substream_output;
 204        unsigned long flags;
 205
 206        spin_lock_irqsave(&chip->open_lock, flags);
 207        mod_timer(&chip->midi_timer, 1 + jiffies);
 208        spin_unlock_irqrestore(&chip->open_lock, flags);        
 209        snd_sb8dsp_midi_output_write(substream);
 210}
 211
 212static void snd_sb8dsp_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
 213{
 214        unsigned long flags;
 215        struct snd_sb *chip;
 216
 217        chip = substream->rmidi->private_data;
 218        spin_lock_irqsave(&chip->open_lock, flags);
 219        if (up) {
 220                if (!(chip->open & SB_OPEN_MIDI_OUTPUT_TRIGGER)) {
 221                        mod_timer(&chip->midi_timer, 1 + jiffies);
 222                        chip->open |= SB_OPEN_MIDI_OUTPUT_TRIGGER;
 223                }
 224        } else {
 225                if (chip->open & SB_OPEN_MIDI_OUTPUT_TRIGGER) {
 226                        chip->open &= ~SB_OPEN_MIDI_OUTPUT_TRIGGER;
 227                }
 228        }
 229        spin_unlock_irqrestore(&chip->open_lock, flags);
 230
 231        if (up)
 232                snd_sb8dsp_midi_output_write(substream);
 233}
 234
 235static const struct snd_rawmidi_ops snd_sb8dsp_midi_output =
 236{
 237        .open =         snd_sb8dsp_midi_output_open,
 238        .close =        snd_sb8dsp_midi_output_close,
 239        .trigger =      snd_sb8dsp_midi_output_trigger,
 240};
 241
 242static const struct snd_rawmidi_ops snd_sb8dsp_midi_input =
 243{
 244        .open =         snd_sb8dsp_midi_input_open,
 245        .close =        snd_sb8dsp_midi_input_close,
 246        .trigger =      snd_sb8dsp_midi_input_trigger,
 247};
 248
 249int snd_sb8dsp_midi(struct snd_sb *chip, int device)
 250{
 251        struct snd_rawmidi *rmidi;
 252        int err;
 253
 254        err = snd_rawmidi_new(chip->card, "SB8 MIDI", device, 1, 1, &rmidi);
 255        if (err < 0)
 256                return err;
 257        strcpy(rmidi->name, "SB8 MIDI");
 258        snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_sb8dsp_midi_output);
 259        snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_sb8dsp_midi_input);
 260        rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT;
 261        if (chip->hardware >= SB_HW_20)
 262                rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
 263        rmidi->private_data = chip;
 264        timer_setup(&chip->midi_timer, snd_sb8dsp_midi_output_timer, 0);
 265        chip->rmidi = rmidi;
 266        return 0;
 267}
 268