linux/sound/isa/msnd/msnd_midi.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
   4 *  Copyright (c) 2009 by Krzysztof Helt
   5 *  Routines for control of MPU-401 in UART mode
   6 *
   7 *  MPU-401 supports UART mode which is not capable generate transmit
   8 *  interrupts thus output is done via polling. Also, if irq < 0, then
   9 *  input is done also via polling. Do not expect good performance.
  10 */
  11
  12#include <linux/io.h>
  13#include <linux/slab.h>
  14#include <linux/delay.h>
  15#include <linux/ioport.h>
  16#include <linux/errno.h>
  17#include <linux/export.h>
  18#include <sound/core.h>
  19#include <sound/rawmidi.h>
  20
  21#include "msnd.h"
  22
  23#define MSNDMIDI_MODE_BIT_INPUT         0
  24#define MSNDMIDI_MODE_BIT_OUTPUT                1
  25#define MSNDMIDI_MODE_BIT_INPUT_TRIGGER 2
  26#define MSNDMIDI_MODE_BIT_OUTPUT_TRIGGER        3
  27
  28struct snd_msndmidi {
  29        struct snd_msnd *dev;
  30
  31        unsigned long mode;             /* MSNDMIDI_MODE_XXXX */
  32
  33        struct snd_rawmidi_substream *substream_input;
  34
  35        spinlock_t input_lock;
  36};
  37
  38/*
  39 * input/output open/close - protected by open_mutex in rawmidi.c
  40 */
  41static int snd_msndmidi_input_open(struct snd_rawmidi_substream *substream)
  42{
  43        struct snd_msndmidi *mpu;
  44
  45        snd_printdd("snd_msndmidi_input_open()\n");
  46
  47        mpu = substream->rmidi->private_data;
  48
  49        mpu->substream_input = substream;
  50
  51        snd_msnd_enable_irq(mpu->dev);
  52
  53        snd_msnd_send_dsp_cmd(mpu->dev, HDEX_MIDI_IN_START);
  54        set_bit(MSNDMIDI_MODE_BIT_INPUT, &mpu->mode);
  55        return 0;
  56}
  57
  58static int snd_msndmidi_input_close(struct snd_rawmidi_substream *substream)
  59{
  60        struct snd_msndmidi *mpu;
  61
  62        mpu = substream->rmidi->private_data;
  63        snd_msnd_send_dsp_cmd(mpu->dev, HDEX_MIDI_IN_STOP);
  64        clear_bit(MSNDMIDI_MODE_BIT_INPUT, &mpu->mode);
  65        mpu->substream_input = NULL;
  66        snd_msnd_disable_irq(mpu->dev);
  67        return 0;
  68}
  69
  70static void snd_msndmidi_input_drop(struct snd_msndmidi *mpu)
  71{
  72        u16 tail;
  73
  74        tail = readw(mpu->dev->MIDQ + JQS_wTail);
  75        writew(tail, mpu->dev->MIDQ + JQS_wHead);
  76}
  77
  78/*
  79 * trigger input
  80 */
  81static void snd_msndmidi_input_trigger(struct snd_rawmidi_substream *substream,
  82                                        int up)
  83{
  84        unsigned long flags;
  85        struct snd_msndmidi *mpu;
  86
  87        snd_printdd("snd_msndmidi_input_trigger(, %i)\n", up);
  88
  89        mpu = substream->rmidi->private_data;
  90        spin_lock_irqsave(&mpu->input_lock, flags);
  91        if (up) {
  92                if (!test_and_set_bit(MSNDMIDI_MODE_BIT_INPUT_TRIGGER,
  93                                      &mpu->mode))
  94                        snd_msndmidi_input_drop(mpu);
  95        } else {
  96                clear_bit(MSNDMIDI_MODE_BIT_INPUT_TRIGGER, &mpu->mode);
  97        }
  98        spin_unlock_irqrestore(&mpu->input_lock, flags);
  99        if (up)
 100                snd_msndmidi_input_read(mpu);
 101}
 102
 103void snd_msndmidi_input_read(void *mpuv)
 104{
 105        unsigned long flags;
 106        struct snd_msndmidi *mpu = mpuv;
 107        void __iomem *pwMIDQData = mpu->dev->mappedbase + MIDQ_DATA_BUFF;
 108        u16 head, tail, size;
 109
 110        spin_lock_irqsave(&mpu->input_lock, flags);
 111        head = readw(mpu->dev->MIDQ + JQS_wHead);
 112        tail = readw(mpu->dev->MIDQ + JQS_wTail);
 113        size = readw(mpu->dev->MIDQ + JQS_wSize);
 114        if (head > size || tail > size)
 115                goto out;
 116        while (head != tail) {
 117                unsigned char val = readw(pwMIDQData + 2 * head);
 118
 119                if (test_bit(MSNDMIDI_MODE_BIT_INPUT_TRIGGER, &mpu->mode))
 120                        snd_rawmidi_receive(mpu->substream_input, &val, 1);
 121                if (++head > size)
 122                        head = 0;
 123                writew(head, mpu->dev->MIDQ + JQS_wHead);
 124        }
 125 out:
 126        spin_unlock_irqrestore(&mpu->input_lock, flags);
 127}
 128EXPORT_SYMBOL(snd_msndmidi_input_read);
 129
 130static const struct snd_rawmidi_ops snd_msndmidi_input = {
 131        .open =         snd_msndmidi_input_open,
 132        .close =        snd_msndmidi_input_close,
 133        .trigger =      snd_msndmidi_input_trigger,
 134};
 135
 136static void snd_msndmidi_free(struct snd_rawmidi *rmidi)
 137{
 138        struct snd_msndmidi *mpu = rmidi->private_data;
 139        kfree(mpu);
 140}
 141
 142int snd_msndmidi_new(struct snd_card *card, int device)
 143{
 144        struct snd_msnd *chip = card->private_data;
 145        struct snd_msndmidi *mpu;
 146        struct snd_rawmidi *rmidi;
 147        int err;
 148
 149        err = snd_rawmidi_new(card, "MSND-MIDI", device, 1, 1, &rmidi);
 150        if (err < 0)
 151                return err;
 152        mpu = kzalloc(sizeof(*mpu), GFP_KERNEL);
 153        if (mpu == NULL) {
 154                snd_device_free(card, rmidi);
 155                return -ENOMEM;
 156        }
 157        mpu->dev = chip;
 158        chip->msndmidi_mpu = mpu;
 159        rmidi->private_data = mpu;
 160        rmidi->private_free = snd_msndmidi_free;
 161        spin_lock_init(&mpu->input_lock);
 162        strcpy(rmidi->name, "MSND MIDI");
 163        snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
 164                            &snd_msndmidi_input);
 165        rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
 166        return 0;
 167}
 168