/*
 * Copyright (c) 1994/95 by Jaroslav Kysela (Perex soft)
 */

#include "gus_dev.h"

io_handle_t osf_io_handle;
struct controller *osf_controller;
struct controller *osf_controllers[ 4 ];
dma_handle_t osf_dma1_handle;
dma_handle_t osf_dma2_handle;

int gus_is_dynamic = 0;
dev_t gus_devno = NODEV; 	/* No major number assigned yet. */
int gus_config = FALSE;  	/* State flags indicating driver configured */
int gus_probe_flag = 0;

/* device section */

int gus_read( dev_t dev, struct uio *uio, int flag )
{
  int pos, res = 0;
  struct iovec *iov = uio -> uio_iov;

  if ( uio -> uio_segflg != UIO_USERSPACE )
    {
      printf( "gus_read: buffers isn't in user space :(\n" );
      return -EIO;
    } 
  for ( pos = 0; pos < uio -> uio_iovcnt; pos++, iov++ )
    {
      switch ( minor( dev ) ) {
        case GUS_MINOR_GSYNTH:
          res = gus_read_gf1( iov -> iov_base, iov -> iov_len );
        case GUS_MINOR_MIXER:
          res = -EIO;
        case GUS_MINOR_PCM_8: 
        case GUS_MINOR_PCM_16:
        case GUS_MINOR_AUDIO:	
          res = gus_read_pcm( minor( dev ), iov -> iov_base, iov -> iov_len );
        default:
          res = -ENODEV;
      }
      if ( res < 0 ) break;		/* any error */
      uio -> uio_resid -= res;
      if ( res != iov -> iov_len )	/* last buffer transferred */
        break;
    }
  return res;
}

int gus_write( dev_t dev, struct uio *uio, int flag )
{
  int pos, res = 0;
  struct iovec *iov = uio -> uio_iov;

  if ( uio -> uio_segflg != UIO_USERSPACE )
    {
      printf( "gus_write: buffers isn't in user space :(\n" );
      return -EIO;
    } 
  for ( pos = 0; pos < uio -> uio_iovcnt; pos++, iov++ )
    {
      switch ( minor( dev ) ) {
        case GUS_MINOR_GSYNTH:	
          res = gus_write_gf1( iov -> iov_base, iov -> iov_len );
        case GUS_MINOR_MIXER:
          res = -EIO;
        case GUS_MINOR_PCM_8: 
        case GUS_MINOR_PCM_16:
        case GUS_MINOR_AUDIO:	
          res = gus_write_pcm( minor( dev ), iov -> iov_base, iov -> iov_len );
        default:
          res = -ENODEV;
      }
      if ( res < 0 ) break;
      uio -> uio_resid -= iov -> iov_len;
    }
  return res;
}

int gus_open( dev_t dev, int flag, int format )
{
  switch ( minor( dev ) ) {
    case GUS_MINOR_GSYNTH:	return gus_open_gf1();
    case GUS_MINOR_MIXER:	return gus_open_mixer();
    case GUS_MINOR_PCM_8: 
    case GUS_MINOR_PCM_16:
    case GUS_MINOR_AUDIO:	
      return gus_open_pcm( minor( dev ) );
  }
  return -ENODEV;
}

int gus_close( dev_t dev, int flag, int format )
{
  switch ( minor( dev ) ) {
    case GUS_MINOR_GSYNTH:	gus_release_gf1();	break;
    case GUS_MINOR_MIXER:	gus_release_mixer(); 	break;
    case GUS_MINOR_PCM_8: 
    case GUS_MINOR_PCM_16:
    case GUS_MINOR_AUDIO:	gus_release_pcm( minor( dev ) );
  }
  return 0;	/* success */
}

static int gus_ioctl( dev_t dev, unsigned int cmd, int *data, int flag )
{
  switch ( minor( dev ) ) {
    case GUS_MINOR_GSYNTH: return gus_ioctl_gf1( cmd, (int)data );
    case GUS_MINOR_MIXER: return gus_ioctl_mixer( cmd, (int)data );
    case GUS_MINOR_PCM_8:
    case GUS_MINOR_PCM_16: 
    case GUS_MINOR_AUDIO: 
      return gus_ioctl_pcm( minor( dev ), cmd, (int)data );
  }
  return -ENODEV;
}

extern int nodev();

static struct cdevsw gus_cdevsw_entry = {
  gus_open,
  gus_close,
  gus_read,
  gus_write,
  gus_ioctl,
  nodev,
  nodev,
  0,
  nodev,
  nodev,
  DEV_FUNNEL_NULL,
  NULL,
  NULL
};

/* init/shutdown section */

extern struct config_entry *ldbl_module_list;
extern int ldbl_debug;

static int cmajnum = 0;
static int bmajnum = 0;
static int begunit = 0;
static int numunit = 0;
static int gusversion = 0;

static unsigned char msysdesc[CFG_ATTR_NAME_SZ] = "";
static unsigned char mcfgname[CFG_ATTR_NAME_SZ] = "";
static unsigned char mcfg[CFG_ATTR_NAME_SZ] = "";
static unsigned char modtype[CFG_ATTR_NAME_SZ] = "";
static unsigned char modpath[CFG_ATTR_NAME_SZ] = "";
static unsigned char devdir[CFG_ATTR_NAME_SZ] = "";
static unsigned char devcharf[CFG_ATTR_NAME_SZ] = "";
static unsigned char devcmajor[CFG_ATTR_NAME_SZ] = "";
static unsigned char devcminor[CFG_ATTR_NAME_SZ] = "";
static unsigned char devuser[CFG_ATTR_NAME_SZ] = "";
static unsigned char devgroup[CFG_ATTR_NAME_SZ] = "";
static unsigned char devmode[CFG_ATTR_NAME_SZ] = "";

cfg_subsys_attr_t gus_attributes[] = { 
{"Subsystem_Description",  CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY,
			(caddr_t)msysdesc,2,CFG_ATTR_NAME_SZ,0},
{"Module_Config_Name",     CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY,
                        (caddr_t)mcfgname,2,CFG_ATTR_NAME_SZ,0},
{"Module_Config1", 	   CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY,
                        (caddr_t)mcfg,2,CFG_ATTR_NAME_SZ,0},
{"Module_Type",            CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY,
                        (caddr_t)modtype,2,CFG_ATTR_NAME_SZ,0},
{"Module_Path",            CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY,
                        (caddr_t)modpath,2,CFG_ATTR_NAME_SZ,0},
{"Device_Dir",             CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY,
                        (caddr_t)devdir,2,CFG_ATTR_NAME_SZ,0},
{"Device_Char_Files",      CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY,
                        (caddr_t)devcharf,2,CFG_ATTR_NAME_SZ,0},
{"Device_Char_Major",      CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY,
                        (caddr_t)devcmajor,0,CFG_ATTR_NAME_SZ,0},
{"Device_Char_Minor",	   CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY,
		        (caddr_t)devcminor,0,CFG_ATTR_NAME_SZ,0},
{"Device_User",	   	   CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY,
		        (caddr_t)devuser,0,CFG_ATTR_NAME_SZ,0},
{"Device_Group",	   CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY,
		        (caddr_t)devgroup,0,CFG_ATTR_NAME_SZ,0},
{"Device_Mode",	   	   CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY,
		        (caddr_t)devmode,0,CFG_ATTR_NAME_SZ,0},
{"cmajnum",                CFG_ATTR_INTTYPE, CFG_OP_QUERY,
                        (caddr_t)&cmajnum,0,99,0},
{"bmajnum",                CFG_ATTR_INTTYPE, CFG_OP_QUERY,
                        (caddr_t)&bmajnum,0,99,0},
{"begunit",                CFG_ATTR_INTTYPE, CFG_OP_QUERY,
                        (caddr_t)&begunit,0,8,0},
{"numunit",                CFG_ATTR_INTTYPE, CFG_OP_QUERY,
                        (caddr_t)&numunit,0,8,0},
{"version",                CFG_ATTR_INTTYPE, CFG_OP_QUERY,
                        (caddr_t)&gusversion,0,9999999,0},
{"",0,0,0,0,0,0}
};

extern int nodev();
ihandler_id_t *gus_id_t[ 1 ]; 

#define GUS_BUSNAME    "eisa"


struct eisa_option gus_option_snippet [] =
/***************************************************/
{
/*  module      function  driver  intr_b4 itr_aft        adpt    */
/*  name        name      name    probe   attach  type   config  */
/*  ------      -------   ------  ------- ------- ----   ------  */
{   "ISA8499",  "",  	  "gus",    0,      1,    'C',   0  },
{   "",         ""      } /* Null terminator in the */
                          /* table */
}; 


int gus_probe( io_handle_t addr1, struct controller *ctlr )
{
  printf( "gus_probe !!!go!!!!\n" );
  gus_probe_flag = 1;
  return 0;

  printf( "gus probe\n" );
  if ( ctlr -> ctlr_num > 0 ) return 0;
  gus_port = addr1 & 0x3ff;		/* 0x220 - 0x260 */
  osf_io_handle = addr1 & ~0xffff;	/* start from 0 */
  osf_controllers[ 0 ] = osf_controller = ctlr;
  osf_dma1_handle = 0L;
  osf_dma2_handle = 0L;
  if ( gus_init() )			/* test the HW */
    {
      printf( "Error: Gravis UltraSound doesn't detected at port 0x%x\n", gus_port );
      return 0; 
    }
#if 0
  res = get_config( ctrl, 
#endif
  gus_probe_flag = 1;
  return 0;
}

int gus_cattach( struct controller *ctlr )
{
  printf( "gus attach\n" );
  return -EIO;
}

int gus_ctlr_unattach( struct bus *bus, struct controller *ctlr )
{
  printf( "gus unattach\n" );
  return 0;
}

struct driver gusdriver = {
        gus_probe,             /* probe */
        0,                     /* slave */
        gus_cattach,           /* cattach */
        0,                     /* dattach */
        0,                     /* go */
        0,                     /* addr_list */
        0,                     /* dev_name */
        0,                     /* dev_list */
        "gus",                 /* ctlr_name */
        osf_controllers,       /* ctlr_list */
        0,                     /* xclu */
        0,                     /* addr1_size */
        0,                     /* addr1_atype */
        0,                     /* addr2_size */
        0,                     /* addr2_atype */
        gus_ctlr_unattach,     /* ctlr_unattach */
        0                      /* dev_unattach */
}; 

int gus_configure( cfg_op_t op, cfg_attr_t indata, size_t indatalen,
                   cfg_attr_t outdata, size_t outdatalen )
{
  dev_t cdevno; 
  int retval; 
  int i;      
  struct controller *ctlr;
  struct bus *bus;
  struct eisa_slot *slot;

  switch ( op ) {
    case CFG_OP_CONFIGURE: 
      gus_is_dynamic = strcmp( modtype, "Dynamic" ) == 0;
      ldbl_debug = 255;
      if ( gus_is_dynamic ) 
        { 
          if ( strcmp(mcfgname,"") == 0 ) 
            {
              printf( "gus_configure, null config name.\n");
              return EINVAL;
            }
          bus = system_bus -> bus_list;
          if ( bus != NULL )
            {
              printf( "####\n" );
              print_bus_entry( bus );
              slot = (struct eisa_slot *)bus -> bus_slot;
              printf( "Slot: 0x%x\n", slot );
              printf( "####\n" );
            }
          if ( ldbl_stanza_resolver( mcfgname, GUS_BUSNAME,
                          	     &gusdriver,
                           	     (caddr_t *)gus_option_snippet ) != ESUCCESS ) 
            return EINVAL;
          printf( "resolver ok.\n" );
#if 0
          print_hardware_topology();
#endif
          printf( "---\n" );
          if ( ldbl_ctlr_configure( GUS_BUSNAME, LDBL_WILDNUM, 
                                    mcfgname, &gusdriver, 0 ) != ESUCCESS )
            return(EINVAL);
          printf( "configure ok.\n" );
#if 0
          print_hardware_topology();
#endif
          if ( !gus_probe_flag ) return EINVAL;
          printf( "---\n" );
#if 0
          printf( ">>> test 0x726 = 0x%x\n", READ_BUSIO_D8( 0x726 ) );
#endif
        }

      if ( strcmp( devcmajor, "" ) != 0 ) 
        { 
          if ( ( strcmp( devcmajor, "-1" ) == 0 ) ||
               ( strcmp( devcmajor, "?" ) == 0 ) ||
               ( strcmp( devcmajor, "any" ) == 0 ) ||
               ( strcmp( devcmajor, "ANY" ) == 0 ) ) 
            {
              cdevno = NODEV;
            }
           else 
            {
              cdevno = atoi( devcmajor );
              printf( "devcmajor = %s, cdevno = 0x%x\n", devcmajor, cdevno );
              cdevno = makedev( cdevno, 0 );
              printf( "devcmajor = %s, cdevno = 0x%x\n", devcmajor, cdevno );
            }
        }
       else
        return EINVAL;
         
      cdevno = cdevsw_add( cdevno, &gus_cdevsw_entry ); 
      printf( "nodev test\n" );
      if ( cdevno == NODEV ) 
        {
          printf( "nodev err.\n" );
          return ENODEV;
        } 
      printf( "nodev ok\n" );
      gus_devno = cdevno; 
      cmajnum = major( gus_devno ); 
      begunit = 0; 
      bmajnum = NODEV; 
      gus_config = TRUE; 
      printf( "gus_config_ok\n" );
      break;

    case CFG_OP_UNCONFIGURE: 
      if ( gus_config != TRUE ) return EINVAL;
      retval = cdevsw_del( gus_devno );
      if ( retval ) return ESRCH;
      if ( gus_is_dynamic ) 
        { 
          if (ldbl_ctlr_unconfigure( GUS_BUSNAME,
                                     LDBL_WILDNUM, &gusdriver,
                                     LDBL_WILDNAME, LDBL_WILDNUM ) != ESUCCESS ) 
            return ESRCH;
        }
      gus_config = FALSE; 
      break;

    case CFG_OP_QUERY: 
      break;

    default: /* Unknown operation type */
      return EINVAL; 
  }
  return 0; 
}

/* another functions */

int gus_ioctl_out( int *addr, int value )
{
  *addr = value;
  return 0;
}

char get_fs_byte( char *ptr )
{
  unsigned char res;

  MEMCPY_FROMFS( &res, ptr, sizeof( char ) );
  return res;
}

int get_fs_int( int *ptr )
{
  int res;

  MEMCPY_FROMFS( &res, ptr, sizeof( int ) );
  return res;
}

void put_fs_word( short data, short *ptr )
{
  MEMCPY_TOFS( &data, ptr, sizeof( short ) );
}

void put_fs_int( int data, int *ptr )
{
  MEMCPY_TOFS( &data, ptr, sizeof( int ) );
}
