/* @(#)iodev1.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
===========================================================================*/

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.TYPE        	Module
.NAME        	iodev1
.LANGUAGE    	C
.AUTHOR      	IPG-ESO Garching
.CATEGORY    	Basic disk i/o
.COMMENTS    	Class is made for Disks / OD
.VERSION 0.0	12-Oct-1989: Design, F. Ochsenbein, C. Guirao
.VERSION 1.0	11-Jan-1990: Implementation.
.VERSION 1.1	18-May-1990: 2 parameterse for ioclose
.VERSION 1.2	17-Aug-1990: Correct order: 1) Mount 2) Assign
.VERSION 1.3	17-Oct-1990: Removed GNU-nonunderstandable code...
.VERSION 1.4	20-Aug-1992: variant_struct not known for GNU. CG.
.ENVIRONMENT 	VMS
------------------------------------------------------------*/
static char class_name[] = "disk";	

#include <osdefos.h>
#include <osudef.h>
#include <filedef.h>	/* Contains definitions of READ / WRITE, etc... */

struct iolist *iodev2();		/* Next io routines in list */


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

#define     DMT$M_NOUNLOAD     1      /* take it from <dmtdef>        */
#include    <iodef.h>                 /* VMS QIO function codes       */
#include    <ssdef.h>                 /* VMS status code definitions  */
#include    <descrip.h>               /* VMS descriptor macro         */
#include    <dvidef.h>		      /* VMS for GetDVI		      */
#include    <dcdef.h>		      /* VMS classes of devices	      */
#include    <errno.h> 

#define SECTOR_SIZE	512		/* The VMS standard */

MID_EXTERN int oserror;
MID_EXTERN int vmserror;      		/* translated by osmsg()        */

typedef struct dsc$descriptor_s SDV;

			 /* Set address & length of a string    */

static short chan;
static struct {
	unsigned short iostatus;
	unsigned short length;
	unsigned long info;
      } iosb = {0, 0, 0};		/* The i/o status block		*/

typedef	struct item {			/* Specification for System Services */
		unsigned short len;
		unsigned short item_code;
		char *addr;
		long *return_len;
		} ITEM;

#define if_VMSerror	if (!(vmserror&1)) 

/*======================================================================*/
static SDV *devname(name)
/*+++++++++++++ 
.PURPOSE Set sdv with device name
.RETURNS Nothing
.REMARK  Add colon if missing
--------------*/
	char	*name;	/* IN: Device name	*/
{
	static 	SDV sdv = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
	static 	char aName[20];
	int	len;

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

  sdv.dsc$w_length  = len;
  return (&sdv);
}

/*======================================================================*/
static int io1 (op, buffer, length, block)
/*+++++++++++++ 
.PURPOSE	Interface to SYS$QIOW, with test of main returned status
.RETURNS	Byte count / -1
.REMARK		No tracing in this routine, only called from other osu routines
--------------*/
	int	op;		/* IN: The operation to perform	*/
	char	*buffer;	/* OUT: Buffer address		*/
	int	length;		/* IN: Buffer length		*/
	int	block;		/* IN: Sector number		*/
{
	register int l;

  if (oserror)	return(-1);

  iosb.iostatus = 0, iosb.length = 0;
  vmserror = SYS$QIOW (0,chan,op,&iosb,0,0,buffer,length,block,0,0,0);
  l = iosb.length;

  if (!(vmserror & 1))	oserror = EIO, l = -1;

  return(l);
}

/*======================================================================*/
static int io2 (op, buffer, length, block)
/*+++++++++++++ 
.PURPOSE	Interface to SYS$QIOW, with test of 2 returned codes
.RETURNS	Length / -1 if error / 0 for EOF or Virgin Sectors
.REMARK	
--------------*/
	int	op;		/* IN: The operation to perform	*/
	char	*buffer;	/* OUT: Buffer address		*/
	int	length;		/* IN: Buffer length		*/
	int	block;		/* IN: Sector number		*/
{
	register int l;
	
  l = io1(op, buffer, length, block);
  
  if (oserror == 0) 
  {	switch(iosb.iostatus)
	{ case SS$_NORMAL: 	break;
#if 0		/* Following cases ignored here */
	  case SS$_DATAOVERUN: if (l == length) l++;
	  			break;
	  case SS$_ENDOFTAPE:	oserrmsg = "Physical End reached";
	  			oserror = -1;
				break; 
#endif
	  case SS$_FORCEDERROR:
	  case SS$_FORMAT:	/* This happens with virgin sectors on ODs. */
	  			break;
	  case SS$_ENDOFVOLUME:
	  case SS$_ENDOFFILE:	l = 0; break;	/* No error */


	  default:		oserror = EIO;
	  			vmserror = iosb.iostatus;
				break; 
	}
  }
  return (oserror ? -1 : l);
}

/*======================================================================*/
static int iorew(fd)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Go to sector #0
.RETURNS 0 (success) / -1 (error)
------------------------------------------------------------*/
        int    fd;	     	/* IN: File Descriptor */
{
	return(0);
}

static int ioinfo(fd, s, fileno, blkno)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Retrieve Info concerning an opened device
.RETURNS 0 (success) / -1 (error)
------------------------------------------------------------*/
        int    fd;	     	/* IN: File Descriptor */
        struct osustat  *s;     /* OUT: The filled components */
	int	*fileno;	/* OUT: Where we are	*/
	long	*blkno;		/* OUT: Where we are	*/
{
	SDV	*sdv;		/* Need the device name to get info */
	static int dev_class;		/* The VMS device class	*/
	static struct {	struct item item_1;
			long end;} getdvi = {
		{ sizeof(dev_class),DVI$_DEVCLASS,(char *)&dev_class,(long *)0},
		0};

  oserror = 0;
  if (fileno)  *fileno = *blkno  = -1;	/* Unknown ...	*/
  s->usize     = 0;
  s->blocksize = 0;
  s->density   = 0;


  sdv = devname(osuname(-1));

		/* Check if Tape or Disk via GETDVI */
  getdvi.item_1.item_code = DVI$_DEVCLASS;
  vmserror = SYS$GETDVIW(0,0,sdv, &getdvi, &iosb,0,0,0);
  if_VMSerror	{ oserror = EIO; return(-1);}
  s->isda = (dev_class == DC$_DISK);

	/* For Disks, get relevant device info */
  if (s->isda){
	getdvi.item_1.item_code = DVI$_MAXBLOCK;
	vmserror = SYS$GETDVIW(0,0, sdv,&getdvi,&iosb,0,0,0);
  	if_VMSerror	{ oserror = EIO; return(-1);}
	s->blocksize = SECTOR_SIZE;	/* Unit used for usize	*/
	s->usize     = dev_class;	/* Number of sectors in 512-bytes */
  }
  return(0);
}

static int ioopen(name, mode, den)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Open Raw Disk Tape
.RETURNS File descriptor / -1 (error)
.REMARKS  
------------------------------------------------------------*/
char *name;             /* IN:  Physical name of tape device */
int mode;               /* IN:  Open mode */
int den;                /* IN:  Density. Not used */
{
	SDV	*sdv;		/* Device name to get info */
        int 	l;
	static unsigned long mount_option = 
		/* MNT$M_NOASSIST|MNT$M_FOREIGN|MNT$M_MESSAGE;  */
		MNT$M_NOASSIST|MNT$M_FOREIGN;
	static struct {			/* Mount service	*/
		ITEM	item_1;
		ITEM 	item_2;
		long end;} mount = {
		{ 0, MNT$_DEVNAM, 0, 0},	/* Device Name */
		{ sizeof(mount_option),MNT$_FLAGS, (char *)&mount_option,(long *)0},
		0};

  oserror = 0;

	/* Mount the Disk */
  
  sdv = devname(name);
  mount.item_1.len  = sdv->dsc$w_length;
  mount.item_1.addr = sdv->dsc$a_pointer;

  vmserror = SYS$MOUNT(&mount);
  if_VMSerror		  	/* Some error, but take only 16 bits */
  	switch(vmserror&0xffff)	{
	case SS$_DEVMOUNT:	break;	/* Already mounted, not an error */
	default: 	oserror = EINTR;
			return(-1);
  }

						/* Assign the device	*/
  vmserror = SYS$ASSIGN(sdv, &chan, 0,0);
  if_VMSerror	{ oserror = EIO; return(-1);}

  return(chan);
}

static int ioclose(fd, option)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Close the Device
.RETURNS 0 (success) / -1 (failure)
------------------------------------------------------------*/
int fd;                 /* IN:  Tape file descriptor */
int option;		/* IN: Option OPU_DISMOUNT	*/
{
	SDV	*sdv;
	
  oserror=0;
  vmserror = SYS$DASSGN(fd);
  if_VMSerror	{ oserror = EIO; return(-1);}
    
  sdv = devname(osuname(-1));
  vmserror = SYS$DISMOU(sdv, (option & OPU_DISMOUNT ? 0 :DMT$M_NOUNLOAD));
  if_VMSerror	{ oserror = EIO; return(-1);}

  return(0);
}

static int iosread(fd, sno, ssize, buffer, size)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Read a buffer from a DA device
.RETURNS Bytes read / -1 if error
.REMARKS 0 Bytes read, means ???
------------------------------------------------------------*/
int 	fd;   		/* IN: Tape device file descriptor */
long 	sno;		/* IN: Starting sector to read	*/
int	ssize;		/* IN: Size on one sector	*/
char 	*buffer;       	/* OUT:  Buffer for reading 	*/
int 	size;   	/* IN: Length of bytes to be read */
{
	long	block;

    oserror = 0, chan = fd;
    block = sno*(ssize/SECTOR_SIZE);
    return (io2(IO$_READLBLK, buffer, size, block));
}

static int ioswrite(fd, sno, ssize, buffer, size)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Write a block to a DA device
.RETURNS Bytes written / -1 if error
.REMARKS 0 Bytes read, means ???
------------------------------------------------------------*/
int 	fd;       	/* IN: Tape device file descriptor */
long 	sno;		/* IN: Starting sector to write	*/
int	ssize;		/* IN: Size on one sector	*/
char 	*buffer; 	/* IN: Buffer to write		*/
int 	size;    	/* IN: Numberof bytes to write	*/
{
	long	block;

    oserror = 0, chan = fd;
    block = sno*(ssize/SECTOR_SIZE);

    return (io2(IO$_WRITELBLK, buffer, size, block));
}

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

static OPITEM list_of_functions[] = {
	{ U_OPEN,	ioopen},		/* Open (name, mode)	*/
	{ U_CLOSE,  	ioclose},
	{ U_INFO,	ioinfo},
	{ U_SREAD,      iosread},      /* Sector Read (fd,sno,ssize,buf,bsize)*/
	{ U_SWRITE,     ioswrite},     /* Sector Write(fd,sno,ssize,buf,bsize)*/
	{ U_REWIND, 	iorew}
 };


static struct iolist this_dev = {
        iodev2,                 /* 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 *iodev1()
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Get All definitions concerning this class of Devices
.RETURNS The iolist
.REMARKS 
------------------------------------------------------------*/
{
  return(&this_dev);
}
