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

A driver for Soundcards (Current version supports AdLib, SoundBlaster and SB
   Pro)

(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 "dev_table.h"

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

struct sbc_device
{
  int usecount;
};

static struct sbc_device sbc_devices[SND_NDEVS];

static int in_use = 0;		/* Total # of open device files (excluding
				   minor 0) */

static int soundcards_installed = 0;	/* Number of installed soundcards */
static int soundcard_configured = 0;

static int
sound_read (struct inode *inode, struct file *file, char *buf, int count)
{
  int dev;

  dev = inode->i_rdev;
  dev = MINOR (dev);

  DEB (printk ("sound_read(dev=%d, count=%d)\n", dev, count));

  switch (dev & 0x0f)
    {
    case SND_DEV_AUDIO:
      return audio_read (inode, file, buf, count);
      break;

    case SND_DEV_DSP:
      return dsp_read (inode, file, buf, count);
      break;

    case SND_DEV_SEQ:
      return sequencer_read (inode, file, buf, count);
      break;

    case SND_DEV_MIDIN:
      return MIDIbuf_read (inode, file, buf, count);

    default:
      ;
    }

  return -EPERM;
}

static int
sound_write (struct inode *inode, struct file *file, char *buf, int count)
{
  int dev;

  dev = inode->i_rdev;
  dev = MINOR (dev);

  DEB (printk ("sound_write(dev=%d, count=%d)\n", dev, count));

  switch (dev & 0x0f)
    {

    case SND_DEV_SEQ:
      return sequencer_write (inode, file, buf, count);
      break;

    case SND_DEV_AUDIO:
      return audio_write (inode, file, buf, count);
      break;

    case SND_DEV_DSP:
      return dsp_write (inode, file, buf, count);
      break;

    default:
      return -EPERM;
    }

  return count;
}

static int
sound_lseek (struct inode *inode, struct file *file, off_t offset, int orig)
{
  return -EPERM;
}

static int
sound_open (struct inode *inode, struct file *filp)
{
  int dev, retval;

  dev = inode->i_rdev;
  dev = MINOR (dev);

  DEB (printk ("sound_open(dev=%d) : usecount=%d\n", dev, sbc_devices[dev].usecount));

  if ((dev >= SND_NDEVS) || (dev < 0))
    {
      printk ("Invalid minor device %d\n", dev);
      return -ENODEV;
    }

  if (!soundcard_configured && dev)
    {
      printk ("SoundCard Error: The soundcard system has not been configured\n");
      return -ENODEV;
    }

  switch (dev & 0x0f)
    {
    case SND_DEV_CTL:
      if (!soundcards_installed)
	if (soundcard_configured)
	  {
	    printk ("Soundcard not installed\n");
	    return -ENODEV;
	  }
      break;

    case SND_DEV_SEQ:
      if ((retval = sequencer_open (inode, filp)) < 0)
	return retval;
      break;

    case SND_DEV_MIDIN:
      if ((retval = MIDIbuf_open (inode, filp)) < 0)
	return retval;
      break;

    case SND_DEV_AUDIO:
      if ((retval = audio_open (inode, filp)) < 0)
	return retval;
      break;

    case SND_DEV_DSP:
      if ((retval = dsp_open (inode, filp)) < 0)
	return retval;
      break;

    default:
      printk ("Invalid minor device %d\n", dev);
      return -ENODEV;
    }

  sbc_devices[dev].usecount++;
  in_use++;

  return 0;
}

static void
sound_release (struct inode *inode, struct file *filp)
{
  int dev;

  dev = inode->i_rdev;
  dev = MINOR (dev);

  DEB (printk ("sound_release(dev=%d)\n", dev));

  switch (dev)
    {
    case SND_DEV_SEQ:
      sequencer_release (inode, filp);
      break;

    case SND_DEV_MIDIN:
      MIDIbuf_release (inode, filp);
      break;

    case SND_DEV_AUDIO:
      audio_release (inode, filp);
      break;

    case SND_DEV_DSP:
      dsp_release (inode, filp);
      break;

    default:;
    }

  sbc_devices[dev].usecount--;
  in_use--;			/* If not control port */
}

static int
sound_ioctl (struct inode *inode, struct file *file,
	     unsigned int cmd, unsigned int arg)
{
  int dev;

  dev = inode->i_rdev;
  dev = MINOR (dev);

  DEB (printk ("sound_ioctl(dev=%d, cmd=0x%x, arg=0x%x)\n", dev, cmd, arg));

  switch (dev & 0x0f)
    {

    case SND_DEV_CTL:
      if (!soundcard_configured)
	{
	  struct soundcard_config conf;

	  if (cmd != SNDCTL_CONFIGURE)
	    {
	      printk ("SoundCard Error: The soundcard system has not been configured\n");
	      return -ENODEV;
	    }

	  memcpy_fromfs ((char *) &conf, (char *) arg, sizeof (conf));
	  return sndtable_config (&conf);
	}

      if (!num_mixers)
	return -ENODEV;

      if (dev >= num_mixers)
	return -ENODEV;

      return mixer_devs[dev]->ioctl (dev, cmd, arg);
      break;

    case SND_DEV_SEQ:
      return sequencer_ioctl (inode, file, cmd, arg);
      break;

    case SND_DEV_AUDIO:
      return audio_ioctl (inode, file, cmd, arg);
      break;

    case SND_DEV_DSP:
      return dsp_ioctl (inode, file, cmd, arg);
      break;

    case SND_DEV_MIDIN:
      return MIDIbuf_ioctl (inode, file, cmd, arg);
      break;

    default:
      return -EPERM;
      break;
    }

  return -EPERM;
}

static int sound_select(struct inode * inode, struct file * filp, int sel_type, select_table * wait)
{
  int dev;

  dev = inode->i_rdev;
  dev = MINOR (dev);

  DEB (printk ("sound_ioctl(dev=%d, cmd=0x%x, arg=0x%x)\n", dev, cmd, arg));

  switch (dev & 0x0f)
    {
    case SND_DEV_SEQ:
    	return sequencer_select(inode, filp, sel_type, wait);
    	break;

    default: 
    	return 0;
    }
    
    return 0;
}

static struct file_operations sound_fops =
{
  sound_lseek,
  sound_read,
  sound_write,
  NULL,				/* sound_readdir */
  sound_select,	
  sound_ioctl,
  NULL,
  sound_open,
  sound_release
};

void sound_setup(char *str, int *ints)
{
}

long
soundcard_init (long mem_start)
{
  int i;

  register_chrdev(SND_MAJOR,"sound", &sound_fops);

  soundcard_configured = 1;

  mem_start = sndtable_init (mem_start);	/* Initialize call tables and
						   detect cards */

  if (!(soundcards_installed = sndtable_get_cardcount ()))
    return mem_start;		/* No cards detected */

  if (num_dspdevs)		/* Audio devices present */
    {
      mem_start = DMAbuf_init (mem_start);
      mem_start = audio_init (mem_start);
      mem_start = dsp_init (mem_start);
    }

  if (num_midis)
    mem_start = MIDIbuf_init (mem_start);

  if (num_midis + num_synths)
    mem_start = sequencer_init (mem_start);

  for (i = 0; i < SND_NDEVS; i++)
    {
      sbc_devices[i].usecount = 0;
    }

  return mem_start;
}

void
tenmicrosec (void)
{
  int i;
  for (i = 0; i < 16; i++)
    inb (0x80);
}

void
soundcard_load (int major)
{
  register_chrdev(major,"sound", &sound_fops);
  soundcard_configured = 0;
}

int
soundcard_unload (int major)
{
  if (!soundcard_configured)
    return 0;
  if (in_use)
    {
      printk ("SoundCard busy (%d)\n", in_use);
      return -EBUSY;
    }

  soundcard_configured = 0;
  /*chrdev_fops[major] = NULL;*/
  return 0;
}

void
soundcard_start (void)
{
  soundcard_configured = 1;
}

#else

long
soundcard_init (long mem_start)	/* Dummy version */
{
  return mem_start;
}

#endif
