/* linux/kernel/chr_drv/sound/pas2_midi.c

The low level driver for the PAS Midi Interface.

(C) 1992  Hannu Savolainen (hsavolai@cs.helsinki.fi) */

#include "sound_config.h"

#ifdef CONFIGURE_SOUNDCARD

#include <linux/types.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/fcntl.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/tty.h>
#include <linux/ctype.h>
#include <asm/io.h>
#include <asm/segment.h>
#include <asm/system.h>
#include <sys/kd.h>
#include <linux/wait.h>
#include <linux/soundcard.h>
#include "sound_calls.h"
#include "pas.h"

#if !defined(EXCLUDE_PAS) && !defined(EXCLUDE_MIDI)

#include "dev_table.h"

#define DEB(WHAT)		/* (WHAT) */
#define DEB1(WHAT)		/* (WHAT) */

/* DISABLE_INTR and ENABLE_INTR are used to disable or enable interrupts.
   These macros store the current flags to the (unsigned long) variable given
   as a parameter. RESTORE_INTR returns the interrupt ebable bit to state
   before DISABLE_INTR or ENABLE_INTR */

#define DISABLE_INTR(flags)	__asm__ __volatile__("pushfl ; popl %0 ; cli":"=r" (flags));
#define ENABLE_INTR(flags)	__asm__ __volatile__("pushfl ; popl %0 ; sti":"=r" (flags));
#define RESTORE_INTR(flags)	__asm__ __volatile__("pushl %0 ; popfl": :"r" (flags));

static int midi_busy = 0;
static int ofifo_bytes = 0;

static int
pas_midi_open (int dev, int mode)
{
  int err;
  unsigned long flags;
  unsigned char ctrl;


  if (midi_busy)
    return -EBUSY;

  /* Reset input and output FIFO pointers */
  pas_write (M_C_RESET_INPUT_FIFO | M_C_RESET_OUTPUT_FIFO,
	     MIDI_CONTROL);

  DISABLE_INTR (flags);

  if ((err = pas_set_intr (I_M_MIDI_IRQ_ENABLE)) < 0)
    return err;

  /* Enable input available and output FIFO empty interrupts */

  ctrl = 0;
  if (mode == O_RDONLY || mode == O_RDWR)
    {
      ctrl |= M_C_ENA_INPUT_IRQ;	/* Enable input */
    }

  if (mode == O_WRONLY || mode == O_RDWR)
    {
      ctrl |= M_C_ENA_OUTPUT_IRQ;	/* Enable output */
    }

  pas_write (ctrl,
	     MIDI_CONTROL);

  /* Acknowledge any pending interrupts */

  pas_write (0xff, MIDI_STATUS);
  ofifo_bytes = 0;

  RESTORE_INTR (flags);

  midi_busy = 1;
  return 0;
}

static void
pas_midi_close (int dev)
{

  /* Reset FIFO pointers, disable intrs */
  pas_write (M_C_RESET_INPUT_FIFO | M_C_RESET_OUTPUT_FIFO, MIDI_CONTROL);

  pas_remove_intr (I_M_MIDI_IRQ_ENABLE);
  midi_busy = 0;
}

static int
pas_midi_out (int dev, unsigned char midi_byte)
{
  int fifo_space;

  fifo_space = pas_read (MIDI_FIFO_STATUS) & 0xf0 >> 4;

  if (fifo_space == 0 & ofifo_bytes > 14)	/* Fifo full */
    return 0;			/* Upper layer will call again */

  ofifo_bytes++;
  pas_write (midi_byte, MIDI_DATA);

  return 1;
}

static int
pas_midi_start_read (int dev)
{
  return 0;
}

static int
pas_midi_end_read (int dev)
{
  return 0;
}

static int
pas_midi_ioctl (int dev, unsigned cmd, unsigned arg)
{
  return -EINVAL;
}


static struct midi_operations pas_midi_operations =
{
  pas_midi_open,
  pas_midi_close,
  pas_midi_ioctl,
  pas_midi_out,
  pas_midi_start_read,
  pas_midi_end_read
};

long
pas_midi_init (long mem_start)
{
  midi_devs[num_midis++] = &pas_midi_operations;
  return mem_start;
}

void
pas_midi_interrupt (void)
{
  unsigned char stat;
  int i, incount;

  stat = pas_read (MIDI_STATUS);

  if (stat & M_S_INPUT_AVAIL)		/* Input byte available */
    {
      incount = pas_read (MIDI_FIFO_STATUS) & 0x0f;	/* Input FIFO count */
      if (!incount)
	incount = 16;

      for (i = 0; i < incount; i++)
	{
	  sequencer_midi_input (pas_read (MIDI_DATA));
	}
    }

  if (stat & M_S_OUTPUT_EMPTY)
    ofifo_bytes = 0;

  if (stat & M_S_FRAMING_ERROR)
    printk ("MIDI framing error\n");

  pas_write (stat, MIDI_STATUS);	/* Acknowledge interrupts */
}

#endif

#endif
