linux/sound/isa/msnd/msnd_midi.c
<<
>>
Prefs
   1/*
   2 *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
   3 *  Copyright (c) 2009 by Krzysztof Helt
   4 *  Routines for control of MPU-401 in UART mode
   5 *
   6 *  MPU-401 supports UART mode which is not capable generate transmit
   7 *  interrupts thus output is done via polling. Also, if irq < 0, then
   8 *  input is done also via polling. Do not expect good performance.
   9 *
  10 *
  11 *   This program is free software; you can redistribute it and/or modify
  12 *   it under the terms of the GNU General Public License as published by
  13 *   the Free Software Foundation; either version 2 of the License, or
  14 *   (at your option) any later version.
  15 *
  16 *   This program is distributed in the hope that it will be useful,
  17 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
  18 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19 *   GNU General Public License for more details.
  20 *
  21 *   You should have received a copy of the GNU General Public License
  22 *   along with this program; if not, write to the Free Software
  23 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  24 *
  25 */
  26
  27#include <linux/io.h>
  28#include <linux/slab.h>
  29#include <linux/delay.h>
  30#include <linux/ioport.h>
  31#include <linux/errno.h>
  32#include <linux/export.h>
  33#include <sound/core.h>
  34#include <sound/rawmidi.h>
  35
  36#include "msnd.h"
  37
  38#define MSNDMIDI_MODE_BIT_INPUT         0
  39#define MSNDMIDI_MODE_BIT_OUTPUT                1
  40#define MSNDMIDI_MODE_BIT_INPUT_TRIGGER 2
  41#define MSNDMIDI_MODE_BIT_OUTPUT_TRIGGER        3
  42
  43struct snd_msndmidi {
  44        struct snd_msnd *dev;
  45
  46        unsigned long mode;             /* MSNDMIDI_MODE_XXXX */
  47
  48        struct snd_rawmidi_substream *substream_input;
  49
  50        spinlock_t input_lock;
  51};
  52
  53/*
  54 * input/output open/close - protected by open_mutex in rawmidi.c
  55 */
  56static int snd_msndmidi_input_open(struct snd_rawmidi_substream *substream)
  57{
  58        struct snd_msndmidi *mpu;
  59
  60        snd_printdd("snd_msndmidi_input_open()\n");
  61
  62        mpu = substream->rmidi->private_data;
  63
  64        mpu->substream_input = substream;
  65
  66        snd_msnd_enable_irq(mpu->dev);
  67
  68        snd_msnd_send_dsp_cmd(mpu->dev, HDEX_MIDI_IN_START);
  69        set_bit(MSNDMIDI_MODE_BIT_INPUT, &mpu->mode);
  70        return 0;
  71}
  72
  73static int snd_msndmidi_input_close(struct snd_rawmidi_substream *substream)
  74{
  75        struct snd_msndmidi *mpu;
  76
  77        mpu = substream->rmidi->private_data;
  78        snd_msnd_send_dsp_cmd(mpu->dev, HDEX_MIDI_IN_STOP);
  79        clear_bit(MSNDMIDI_MODE_BIT_INPUT, &mpu->mode);
  80        mpu->substream_input = NULL;
  81        snd_msnd_disable_irq(mpu->dev);
  82        return 0;
  83}
  84
  85static void snd_msndmidi_input_drop(struct snd_msndmidi *mpu)
  86{
  87        u16 tail;
  88
  89        tail = readw(mpu->dev->MIDQ + JQS_wTail);
  90        writew(tail, mpu->dev->MIDQ + JQS_wHead);
  91}
  92
  93/*
  94 * trigger input
  95 */
  96static void snd_msndmidi_input_trigger(struct snd_rawmidi_substream *substream,
  97                                        int up)
  98{
  99        unsigned long flags;
 100        struct snd_msndmidi *mpu;
 101
 102        snd_printdd("snd_msndmidi_input_trigger(, %i)\n", up);
 103
 104        mpu = substream->rmidi->private_data;
 105        spin_lock_irqsave(&mpu->input_lock, flags);
 106        if (up) {
 107                if (!test_and_set_bit(MSNDMIDI_MODE_BIT_INPUT_TRIGGER,
 108                                      &mpu->mode))
 109                        snd_msndmidi_input_drop(mpu);
 110        } else {
 111                clear_bit(MSNDMIDI_MODE_BIT_INPUT_TRIGGER, &mpu->mode);
 112        }
 113        spin_unlock_irqrestore(&mpu->input_lock, flags);
 114        if (up)
 115                snd_msndmidi_input_read(mpu);
 116}
 117
 118void snd_msndmidi_input_read(void *mpuv)
 119{
 120        unsigned long flags;
 121        struct snd_msndmidi *mpu = mpuv;
 122        void *pwMIDQData = mpu->dev->mappedbase + MIDQ_DATA_BUFF;
 123        u16 head, tail, size;
 124
 125        spin_lock_irqsave(&mpu->input_lock, flags);
 126        head = readw(mpu->dev->MIDQ + JQS_wHead);
 127        tail = readw(mpu->dev->MIDQ + JQS_wTail);
 128        size = readw(mpu->dev->MIDQ + JQS_wSize);
 129        if (head > size || tail > size)
 130                goto out;
 131        while (head != tail) {
 132                unsigned char val = readw(pwMIDQData + 2 * head);
 133
 134                if (test_bit(MSNDMIDI_MODE_BIT_INPUT_TRIGGER, &mpu->mode))
 135                        snd_rawmidi_receive(mpu->substream_input, &val, 1);
 136                if (++head > size)
 137                        head = 0;
 138                writew(head, mpu->dev->MIDQ + JQS_wHead);
 139        }
 140 out:
 141        spin_unlock_irqrestore(&mpu->input_lock, flags);
 142}
 143EXPORT_SYMBOL(snd_msndmidi_input_read);
 144
 145static const struct snd_rawmidi_ops snd_msndmidi_input = {
 146        .open =         snd_msndmidi_input_open,
 147        .close =        snd_msndmidi_input_close,
 148        .trigger =      snd_msndmidi_input_trigger,
 149};
 150
 151static void snd_msndmidi_free(struct snd_rawmidi *rmidi)
 152{
 153        struct snd_msndmidi *mpu = rmidi->private_data;
 154        kfree(mpu);
 155}
 156
 157int snd_msndmidi_new(struct snd_card *card, int device)
 158{
 159        struct snd_msnd *chip = card->private_data;
 160        struct snd_msndmidi *mpu;
 161        struct snd_rawmidi *rmidi;
 162        int err;
 163
 164        err = snd_rawmidi_new(card, "MSND-MIDI", device, 1, 1, &rmidi);
 165        if (err < 0)
 166                return err;
 167        mpu = kzalloc(sizeof(*mpu), GFP_KERNEL);
 168        if (mpu == NULL) {
 169                snd_device_free(card, rmidi);
 170                return -ENOMEM;
 171        }
 172        mpu->dev = chip;
 173        chip->msndmidi_mpu = mpu;
 174        rmidi->private_data = mpu;
 175        rmidi->private_free = snd_msndmidi_free;
 176        spin_lock_init(&mpu->input_lock);
 177        strcpy(rmidi->name, "MSND MIDI");
 178        snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
 179                            &snd_msndmidi_input);
 180        rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
 181        return 0;
 182}
 183