/*
 * sound/patmgr.c
 *
 * The patch maneger interface for the /dev/sequencer
 *
 * Copyright by Hannu Savolainen 1993
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: 1. Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer. 2.
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */

#define PATMGR_C
#include "sound_config.h"

#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SEQUENCER)

DEFINE_WAIT_QUEUES (server_procs[MAX_SYNTH_DEV],
		    server_wait_flag[MAX_SYNTH_DEV]);

static struct patmgr_info *mbox[MAX_SYNTH_DEV] =
{NULL};
static volatile int msg_direction[MAX_SYNTH_DEV] =
{0};

static int      pmgr_opened[MAX_SYNTH_DEV] =
{0};

#define A_TO_S	1
#define S_TO_A 	2

DEFINE_WAIT_QUEUE (appl_proc, appl_wait_flag);

int
pmgr_open (int dev)
{
  if (dev < 0 || dev >= num_synths)
    return RET_ERROR (ENXIO);

  if (pmgr_opened[dev])
    return RET_ERROR (EBUSY);
  pmgr_opened[dev] = 1;

  RESET_WAIT_QUEUE (server_procs[dev], server_wait_flag[dev]);

  return 0;
}

void
pmgr_release (int dev)
{

  if (mbox[dev])		/*
				 * Killed in action. Inform the client
				 */
    {

      mbox[dev]->key = PM_ERROR;
      mbox[dev]->parm1 = RET_ERROR (EIO);

      if (SOMEONE_WAITING (appl_proc, appl_wait_flag))
	WAKE_UP (appl_proc, appl_wait_flag);
    }

  pmgr_opened[dev] = 0;
}

int
pmgr_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
{
  unsigned long   flags;
  int             ok = 0;

  if (count != sizeof (struct patmgr_info))
    {
      printk ("PATMGR%d: Invalid read count\n", dev);
      return RET_ERROR (EIO);
    }

  while (!ok && !PROCESS_ABORTING (server_procs[dev], server_wait_flag[dev]))
    {
      DISABLE_INTR (flags);

      while (!(mbox[dev] && msg_direction[dev] == A_TO_S) &&
	     !PROCESS_ABORTING (server_procs[dev], server_wait_flag[dev]))
	{
	  DO_SLEEP (server_procs[dev], server_wait_flag[dev], 0);
	}

      if (mbox[dev] && msg_direction[dev] == A_TO_S)
	{
	  COPY_TO_USER (buf, 0, (char *) mbox[dev], count);
	  msg_direction[dev] = 0;
	  ok = 1;
	}

      RESTORE_INTR (flags);

    }

  if (!ok)
    return RET_ERROR (EINTR);
  return count;
}

int
pmgr_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
{
  unsigned long   flags;

  if (count < 4)
    {
      printk ("PATMGR%d: Write count < 4\n", dev);
      return RET_ERROR (EIO);
    }

  COPY_FROM_USER (mbox[dev], buf, 0, 4);

  if (*(unsigned char *) mbox[dev] == SEQ_FULLSIZE)
    {
      int             tmp_dev;

      tmp_dev = ((unsigned short *) mbox[dev])[2];
      if (tmp_dev != dev)
	return RET_ERROR (ENXIO);

      return synth_devs[dev]->load_patch (dev, *(unsigned short *) mbox[dev],
					  buf, 4, count, 1);
    }

  if (count != sizeof (struct patmgr_info))
    {
      printk ("PATMGR%d: Invalid write count\n", dev);
      return RET_ERROR (EIO);
    }

  /*
   * If everything went OK, there should be a preallocated buffer in the
   * mailbox and a client waiting.
   */

  DISABLE_INTR (flags);

  if (mbox[dev] && !msg_direction[dev])
    {
      COPY_FROM_USER (&((char *) mbox[dev])[4], buf, 4, count - 4);
      msg_direction[dev] = S_TO_A;

      if (SOMEONE_WAITING (appl_proc, appl_wait_flag))
	{
	  WAKE_UP (appl_proc, appl_wait_flag);
	}
    }

  RESTORE_INTR (flags);

  return count;
}

int
pmgr_access (int dev, struct patmgr_info *rec)
{
  unsigned long   flags;
  int             err = 0;

  DISABLE_INTR (flags);

  if (mbox[dev])
    printk ("  PATMGR: Server %d mbox full. Why?\n", dev);
  else
    {
      rec->key = PM_K_COMMAND;
      mbox[dev] = rec;
      msg_direction[dev] = A_TO_S;

      if (SOMEONE_WAITING (server_procs[dev], server_wait_flag[dev]))
	{
	  WAKE_UP (server_procs[dev], server_wait_flag[dev]);
	}

      DO_SLEEP (appl_proc, appl_wait_flag, 0);

      if (msg_direction[dev] != S_TO_A)
	{
	  rec->key = PM_ERROR;
	  rec->parm1 = RET_ERROR (EIO);
	}
      else if (rec->key == PM_ERROR)
	{
	  err = rec->parm1;
	  if (err > 0)
	    err = -err;
	}

      mbox[dev] = NULL;
      msg_direction[dev] = 0;
    }

  RESTORE_INTR (flags);

  return err;
}

int
pmgr_inform (int dev, int event, unsigned long p1, unsigned long p2,
	     unsigned long p3, unsigned long p4)
{
  unsigned long   flags;
  int             err = 0;

  if (!pmgr_opened[dev])
    return 0;

  DISABLE_INTR (flags);

  if (mbox[dev])
    printk ("  PATMGR: Server %d mbox full. Why?\n", dev);
  else
    {
      if ((mbox[dev] =
	   (struct patmgr_info *) KERNEL_MALLOC (sizeof (struct patmgr_info))) == NULL)
	{
	  printk ("pmgr: Couldn't allocate memory for a message\n");
	  return 0;
	}

      mbox[dev]->key = PM_K_EVENT;
      mbox[dev]->command = event;
      mbox[dev]->parm1 = p1;
      mbox[dev]->parm2 = p2;
      mbox[dev]->parm3 = p3;
      msg_direction[dev] = A_TO_S;

      if (SOMEONE_WAITING (server_procs[dev], server_wait_flag[dev]))
	{
	  WAKE_UP (server_procs[dev], server_wait_flag[dev]);
	}

      DO_SLEEP (appl_proc, appl_wait_flag, 0);
      if (mbox[dev])
	KERNEL_FREE (mbox[dev]);
      mbox[dev] = NULL;
      msg_direction[dev] = 0;
    }

  RESTORE_INTR (flags);

  return err;
}

#endif
