/* @(#)iodev.c	19.1 (ES0-DMD) 02/25/03 13:56:10 */
/*===========================================================================
  Copyright (C) 1995 European Southern Observatory (ESO)
 
  This program is free software; you can redistribute it and/or 
  modify it under the terms of the GNU General Public License as 
  published by the Free Software Foundation; either version 2 of 
  the License, or (at your option) any later version.
 
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
 
  You should have received a copy of the GNU General Public 
  License along with this program; if not, write to the Free 
  Software Foundation, Inc., 675 Massachusetss Ave, Cambridge, 
  MA 02139, USA.
 
  Corresponding concerning ESO-MIDAS should be addressed as follows:
	Internet e-mail: midas@eso.org
	Postal address: European Southern Observatory
			Data Management Division 
			Karl-Schwarzschild-Strasse 2
			D 85748 Garching bei Muenchen 
			GERMANY
===========================================================================*/

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.COPYRIGHT  (c)   1988   European Southern Observatory
.IDENT      iodev.c
.LANGUAGE   C
.AUTHOR     P.Grosbol      ESO/IPG
.KEYWORDS   VMS QIO, tape I/O
.ENVIRON    VAX/VMS
.COMMENT    Ref. to VAX/VMS I/O User's Guide Vol.1, Chap.7 for
            status codes etc.
.VERSION    1.0    1988-Nov-28 : Creation,  PJG
.VERSION    1.1    1988-Dec-08 : Set EOF-status to 0, CGS
.VERSION    1.2    1989-Mar-11 : Do not dismount tape, PJG
.VERSION    1.3    1989-Oct-11 : Hide everything, FO
			Also errors correctly retrieved via osmsg() 
.VERSION    1.4    1989-Dec-20 : 4 parameters for ioinfo
.VERSION    1.5    21-May-1990 : 2 parameters for ioclose. 
			Added IO$M_NOASSIST option in MOUNT, such that
			a clear error message is displayed if the
			tape unit is not ready ...
.VERSION    1.6    17-Aug-1990 : Check (open AND close) that the colon is 
			present...
.VERSION    1.7    14-Nov-1990 : Modified oserror values (cf errno.h)
.VERSION    1.8    23-Apr-1991 : Check ALL statuses...
.VERSION    1.9	   20-Aug-1992 : variant_struct not known for GNU. CG.
--------------------------------------------------------------------*/
static char class_name[] = "mt";	/* MAgnetic Tape Class */

#include    <osdefos.h>
#include    <osudef.h>	
#include    <descrip.h>               /* VMS descriptor macro         */
#include    <iodef.h>                 /* VMS QIO function codes       */

#ifndef variant_struct
#define variant_struct struct
#endif
#include    <mntdef.h>                /* VMS MOUNT definitions        */

#include    <ssdef.h>                 /* VMS status code definitions  */
#include    <errno.h>                 /* Standard Unix error codes    */

MID_EXTERN      int         oserror;      /* MIDAS os-error status        */
MID_EXTERN      int        vmserror;      /* translated by osmsg()        */

#define     DMT$M_NOUNLOAD     1      /* take it from <dmtdef>        */
#define	    MT$M_DENSITY    7936      /* Density mask (0x1f00)	      */
#define     MT$S_DENSITY       5      /* Length in bits of den. field */
#define     MT$V_DENSITY       8      /* Offset in bits of den. field */
#define     MT$K_NRZI_800      3      /* Value for 800 bpi            */
#define     MT$K_PE_1600       4      /* Value for 1600 bpi           */
#define     MT$K_GCR_6250      5      /* Value for 6250 bpi           */

#define     MAXDN             64      /* max. length of device name   */
static      char   dname[MAXDN];      /*      device name             */
static      $DESCRIPTOR(src,dname);

typedef struct {
		unsigned short int iostatus;
		unsigned short int length;
		unsigned long  int info;
               } IOSB;

/*======================================================================*/
static void devname(name)
/*+++++++++++++ 
.PURPOSE Set decriptor with device name
.RETURNS Nothing
.REMARK  Add colon if missing
--------------*/
	char	*name;	/* IN: Device name	*/
{
	int	len;

  len = strlen(name);
					/* Add the : if necessary */
  if ((name[len-1] != ':') && (len < sizeof(dname)))
  {	oscopy (dname, name, len);
	dname[len++] = ':';
	src.dsc$a_pointer = dname;
  }
  else 	src.dsc$a_pointer = name;

  src.dsc$w_length  = len;
  return;
}

static int ioinfo(fd, s, fileno, blkno)
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Retrieve Info concerning an opened device
.RETURNS 0 (success) / -1 (error)
.REMARKS File position and Block position could be set in SCSI tapes ? 
	 Here, these numbers are just set to -1 (unknown)
--------------------------------------------------------------------*/
int              fd;                /* IN:  File Descriptor         */
struct osustat   *s;                /* OUT: The filled components   */
int              *fileno;           /* OUT: File position (-1)      */
long             *blkno;            /* OUT: Block position          */
{
  short         qio_chan;
  IOSB 		iosb;

  if (fileno)  *fileno = -1;
  if (blkno)   *blkno  = -1;
  qio_chan     = fd;
  s->usize     = 0;
  s->blocksize = 0;
  vmserror     = sys$qiow(0,qio_chan,IO$_SENSEMODE,&iosb,0,0,0,0,0,0,0,0);
  if (vmserror!= SS$_NORMAL) {
     oserror = EIO;
     return -1;
  }

  s->density   = (iosb.info & MT$M_DENSITY)>> MT$V_DENSITY;
  if      (s->density == MT$K_GCR_6250)   s->density = 6250; 
  else if (s->density == MT$K_NRZI_800)   s->density =  800;
  else                                    s->density = 1600;

  s->isda = 0;                      /* Note: should use getdvi...   */
  s->istm = 1;                      /* Note: should use getdvi...   */
  
  return 0;
}

static int  ioopen(device,mode,den)
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE    Assign I/O channel to device
.RETURN     I/O channel of OK else -1 if assign error
--------------------------------------------------------------------*/
char       *device;                 /* IN: pointer to device name   */
int        mode;                    /* IN: open mode - NOT used!    */
int        den;                     /* IN: tape density             */
{
  int       sys$assign();
  int        sys$mount();
  int      tden,flag,siz;
  short         qio_chan;
  struct MNTL {
                short   length;
                short     func;
                char      *buf;
                int       term;
               } mntl[6];

  devname(device);

  siz = 32000; tden = den;               /* setup MOUNT parameters  */
  flag = MNT$M_FOREIGN | MNT$M_NOLABEL | MNT$M_NOASSIST;

  mntl[0].length = src.dsc$w_length; 	mntl[0].func = MNT$_DEVNAM;
  mntl[0].buf = src.dsc$a_pointer;   	mntl[0].term = 0;
  mntl[1].length = 4;                	mntl[1].func = MNT$_FLAGS;
  mntl[1].buf = (char *) &flag;      	mntl[1].term = 0;
  mntl[2].length = 4; 			mntl[2].func = MNT$_DENSITY;
  mntl[2].buf = (char *) &tden; 	mntl[2].term = 0;
  mntl[3].length = 4; 			mntl[3].func = MNT$_BLOCKSIZE;
  mntl[3].buf = (char *) &siz; 		mntl[3].term = 0;
  mntl[4].length = 4; 			mntl[4].func = MNT$_RECORDSIZ;
  mntl[4].buf = (char *) &siz; 		mntl[4].term = 0;
  mntl[5].length = 0; 			mntl[5].func = 0;
  mntl[5].buf = (char *) 0; 		mntl[5].term = 0;

  vmserror = sys$mount(mntl); 
  vmserror &= 0xFFFF;                      /* blank out status level */
  if (vmserror != SS$_NORMAL && vmserror != SS$_DEVMOUNT) {
     oserror = ENXIO;
     return -1;
  }
  /* if (vmserror == SS$_DEVMOUNT)
     printf("Warning: Device already mounted\n"); */

  vmserror = sys$assign(&src,&qio_chan,0,0);
  if (vmserror != SS$_NORMAL) {
     oserror = ENXIO;
     return -1;
  }
  return  qio_chan;
}

static int  ioclose(fd, option)
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE    Deassign or close I/O channel to device
.RETURN     0: OK ,else  -1: error
--------------------------------------------------------------------*/
int          fd;                    /* IN: file desc. - I/O-channel */
int	 option;		    /* IN: Option OPU_DISMOUNT	    */
{
  int       sys$dassgn();
  int       sys$dismou();
  short         qio_chan;
  IOSB		    iosb;
  char           *device;

  qio_chan = fd;
  vmserror = sys$qiow(0,qio_chan,IO$_REWIND|IO$M_NOWAIT,&iosb,0,0,0,0,0,0,0,0);
  vmserror = sys$dassgn(qio_chan);
  if (vmserror != SS$_NORMAL) {
     oserror = ENXIO;
     return -1;
  }
                                    /* Need to get back device name */
  device = osuname(-1);
  devname(device);

  vmserror = sys$dismou(&src, (option & OPU_DISMOUNT ? 0 :DMT$M_NOUNLOAD));
  return 0;
}

static int  ioread(fd,buf,no)
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE    Read data from a VMS I/O-channel
.RETURN     no. of bytes read if OK 
            else 0: EOF, -2: QIO error
--------------------------------------------------------------------*/
int                   fd;             /* IN: I/O-channel no.        */
char                *buf;             /* IN: pointer to data buffer */
int                   no;             /* IN: no. of bytes to read   */
{
  int         sys$qiow();
  short         qio_chan;
  IOSB		    iosb;

  qio_chan = fd;
  vmserror = sys$qiow(0,qio_chan,IO$_READLBLK,&iosb,0,0,buf,no,0,0,0,0);
  if (vmserror != SS$_NORMAL) {
     oserror = EIO;
     return -1;
  }
  vmserror = iosb.iostatus;
  if (vmserror & 1)	return (iosb.length);
  if (vmserror == SS$_ENDOFFILE)	return (0);
  oserror = EIO;
  return(-1);
}

static int  iowrite(fd,buf,no)
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE    Write data to a VMS I/O-channel
.RETURN     no. of bytes written if OK 
            else -1: EOT, -2: QIO error, -3: Timeout
--------------------------------------------------------------------*/
int                    fd;            /* IN: I/O-channel no.        */
char                 *buf;            /* IN: pointer to data buffer */
int                    no;            /* IN: no. of bytes to read   */
{
  int          sys$qiow();
  short          qio_chan;
  IOSB		    iosb;

  qio_chan = fd;
  vmserror = sys$qiow(0,qio_chan,IO$_WRITELBLK,&iosb,0,0,buf,no,0,0,0,0);
  if (vmserror != SS$_NORMAL) {
     oserror = EIO;
     return -1;
  }
  vmserror = iosb.iostatus;
  if (vmserror & 1)	return (iosb.length);
  if (vmserror == SS$_ENDOFFILE)	return (0);
  oserror = EIO;
  return(-1);
}

static int  ioweof(fd)
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE    Write EOF's on tape assigned to a VMS I/O-channel
.RETURN     0: OK ,else -1: QIO error, -2: EOT
--------------------------------------------------------------------*/
int              fd;                   /* IN: I/O-channel no.       */
{
  int         sys$qiow();
  int                  i;
  short         qio_chan;
  IOSB		    iosb;

  qio_chan = fd;
  vmserror = sys$qiow(0,qio_chan,IO$_WRITEOF,&iosb,0,0,0,0,0,0,0,0);
  if (vmserror != SS$_NORMAL) {
      oserror = EIO;
      return -1;
  }
  vmserror = iosb.iostatus;
  if (vmserror & 1)	return (iosb.length);
  if (vmserror == SS$_ENDOFFILE)	return (0);
  oserror = EIO;
  return(-1);
}

static int  iorew(fd)
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE    Rewind tape assigned to a VMS I/O-channel.
.RETURN     0: OK , -1: QIO error
--------------------------------------------------------------------*/
int            fd;                        /* IN: I/O-channel no.    */
{
  int         sys$qiow();
  short         qio_chan;
  IOSB		    iosb;

  qio_chan = fd;
  vmserror = sys$qiow(0,qio_chan,IO$_REWIND,&iosb,0,0,0,0,0,0,0,0);
  if (vmserror != SS$_NORMAL) {
     oserror = EIO;
     return -1;
  }
  vmserror = iosb.iostatus;
  if (vmserror & 1)	return (iosb.length);
  if (vmserror == SS$_ENDOFFILE)	return (0);
  oserror = EIO;
  return(-1);
}

static int  iosf(fd,no)
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE    Skip files on tape assigned to a VMS I/O-channel
.RETURN     0: OK , -1: QIO error
--------------------------------------------------------------------*/
int               fd;                 /* IN: I/O-channel no.        */
int               no;                 /* IN: no. of files to skip   */
{
  int         sys$qiow();
  short         qio_chan;
  IOSB		    iosb;

  qio_chan = fd;
  vmserror = sys$qiow(0,qio_chan,IO$_SKIPFILE,&iosb,0,0,no,0,0,0,0,0);
  if (vmserror != SS$_NORMAL) {
     oserror = EIO;
     return -1;
  }
  vmserror = iosb.iostatus;
  if (vmserror & 1)	return (iosb.length);
  if (vmserror == SS$_ENDOFFILE)	return (0);
  oserror = EIO;
  return(-1);
}

static int  iosr(fd,no)
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE    Skip files on tape assigned to a VMS I/O-channel
.RETURN     0: OK , -1: QIO error
--------------------------------------------------------------------*/
int               fd;                 /* IN: I/O-channel no.        */
int               no;                 /* IN: no. of files to skip   */
{
  int         sys$qiow();
  short         qio_chan;
  IOSB		    iosb;

  qio_chan = fd;
  vmserror = sys$qiow(0,qio_chan,IO$_SKIPRECORD,&iosb,0,0,no,0,0,0,0,0);
  if (vmserror != SS$_NORMAL) {
     oserror = EIO;
     return -1;
  }
  vmserror = iosb.iostatus;
  if (vmserror & 1)	return (iosb.length);
  if (vmserror == SS$_ENDOFFILE)	return (0);
  oserror = EIO;
  return(-1);
}

static int iofsf(fd,no)
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE    Forward Skip Files on tape assigned to a VMS I/O-channel
.RETURN     0: OK , -1: QIO error
--------------------------------------------------------------------*/
int               fd;                 /* IN: I/O-channel no.        */
int               no;                 /* IN: no. of files to skip   */
{
  return iosf(fd,no);
}

static int iobsf(fd,no)
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE    Backward Skip Files on tape assigned to a VMS I/O-channel
.RETURN     0: OK , -1: QIO error
--------------------------------------------------------------------*/
int               fd;                 /* IN: I/O-channel no.        */
int               no;                 /* IN: no. of files to skip   */
{
  return iosf(fd,-no);
}

static int iofsr(fd,no)
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE    Forward Skip Files on tape assigned to a VMS I/O-channel
.RETURN     0: OK , -1: QIO error
--------------------------------------------------------------------*/
int               fd;                 /* IN: I/O-channel no.        */
int               no;                 /* IN: no. of records to skip */
{
  return iosr(fd,no);
}

static int iobsr(fd,no)
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE    Bachward Skip Record on tape assigned to a VMS I/O-channel
.RETURN     0: OK , -1: QIO error
--------------------------------------------------------------------*/
int               fd;                 /* IN: I/O-channel no.        */
int               no;                 /* IN: no. of records to skip */
{
  return iosr(fd,-no);
}

/*=====================================================================
 * 		Definition of the structure returned to osu
 *=====================================================================*/

struct iolist *iodev0();

static OPITEM list_of_functions[] = {
	{ U_INFO,	ioinfo},
	{ U_OPEN,	ioopen},
	{ U_CLOSE,  	ioclose},
	{ U_READ,	ioread},
	{ U_WRITE,	iowrite},
	{ U_REWIND, 	iorew},
	{ U_WEOF,	ioweof},
	{ U_FMF,	iofsf},	/* File Move Forward  */
	{ U_FMB,	iobsf},	/* File Move Backward */
	{ U_BMF,	iofsr},	/* Block Move Forward  */
	{ U_BMB,	iobsr}	/* Block Move Backward */
 };

static struct iolist this_dev = {
	iodev0,		/* Next iodev in List */
	class_name,	/* How it's written in DEVCAPFILE */
	sizeof(list_of_functions)/sizeof(list_of_functions[0]),
	list_of_functions
 };
 
struct iolist *iodev()
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Get All definitions concerning this class of Devices
.RETURNS The iolist
.REMARKS Simply returns the local iolist address...
------------------------------------------------------------*/
{
  return(&this_dev);
}
