From: prb@aplexus.jhuapl.edu (Paul R. Bade)
Newsgroups: comp.os.vxworks
Subject: re: comp.os.vxworks newsdigest
Date: Mon, 14 Dec 92 08:05:25 EST
Organization: Lawrence Berkeley Laboratory, Berkeley CA
NNTP-Posting-Host: 128.3.112.16
Originator: daemon@vxw.ee.lbl.gov

----------
X-Sun-Data-Type: text
X-Sun-Data-Description: text
X-Sun-Data-Name: text
X-Sun-Content-Lines: 47

> Subject: SCSI Tape drive and MV167
> Date: Fri, 11 Dec 92 13:42:24 GMT
> From: jcreem@Rapnet.Sanders.Lockheed.Com (Jeffrey M. Creem x5700)
> Organization: Sanders Associates
> Keywords: SCSI, MV167, EXABYTE
> Message-ID: <1992Dec11.134224.27735@Rapnet.Sanders.Lockheed.Com>
> References: <1992Dec10.225412.29032@nosc.mil>
> 
> Hi,
>   I was wondering if anyone has had any experience using an
> exabyte 8500 drive on an MV167 with vxworks (or VADS). We
> have used the 8200 sucessfully in the past but are having problems
> with the 8500.  
> 
> Any help, tips or even knowing that this has been done in the past
> would be great.
>  Thanks,
>     Jeffrey Creem 
> 
> ---------------------------

Attached is a driver for the exabyte 8500

	Enjoy,


+====================================================================+
|       __     ____         __     __                                |
|      /\ \   /  \ \       /\_\__ /\ \      Johns Hopkins University |
|     /  \_\ | /\ \ \     / / /\_\\ \ \     Applied Physics Lab.     |
|    / /\ | |\ \/  \ \   / / / / / \ \ \                             |
|   /  \/ |_| \  /\ \_\ / / / / /   \ \ \   Paul R. Bade             |
|  / /\__/_/   \ \ \/_// / / / /    / / /   (301)-953-6000 x8681     |
| / / /         \ \_\  \ \/ / /    / / /    prb@aplexus.jhuapl.edu   |
| \/_/           \/_/   \__/_/     \/_/                              |
|               __     ____         __       __                      |
|              /\ \   /  \ \       /\ \     /\ \                     |
|             /  \_\ | /\ \ \     /  \_\   /  \ \                    |
|            / /\ | |\ \/  \ \   / /\ |_| / /\ \ \                   |
|           /  \/ |_| \  /\ \_\ / / / | | \/ /  \ \                  |
|          / /\  / /   \ \ \/_// / / / /    / /\ \_\                 |
|          \ \/ / /     \ \_\  \ \/ / /     \/ / / /                 |
|           \__/_/       \/_/   \__/_/        /_/_/                  |
|                                                                    |
+====================================================================+


----------
X-Sun-Data-Type: h-file
X-Sun-Data-Description: h-file
X-Sun-Data-Name: exabyte.h
X-Sun-Content-Lines: 250



/*******************************************************************************
* 
* 
*
*-------------------------------------------------------------------------------
* 
* Module:	
*
*-------------------------------------------------------------------------------
* 
* Description:	This file describes contains constant and type 
*		definitions for all of the routines to interface
*		to an exabyte tape drive. The routines are in Exabyte.c
*
*-------------------------------------------------------------------------------
* 
* $Log$
*
*******************************************************************************/

/************************************
*          Module Includes          *          
************************************/


/************************************
*           Local Defines           *
************************************/


/************************************
*          Local Functions          *
************************************/


/************************************
*          Local Variables          *
************************************/

/**********************************************************/
/*     Constant Definitions for the Exabyte Module        */
/**********************************************************/

/* Exabyte Status Codes */

#define EXB_NO_ERROR		0x00
#define EXB_BUSY		0x01	/* command rejected, device busy */
#define EXB_NOT_READY		0x02
#define EXB_MEDIUM_ERROR	0x03
#define EXB_HARDWARE_ERROR	0x04
#define EXB_ILLEGAL_REQUEST	0x05
#define EXB_UNIT_ATTENTION	0x06
#define EXB_DATA_PROTECT	0x07
#define EXB_BLANK_CHECK		0x08
#define EXB_EXABYTE_UNIQUE	0x09
#define EXB_ABORTED_COMMAND	0x0B
#define EXB_VOLUME_OVERFLOW	0x0D
#define EXB_NO_SENSE		0x0f

/* Exabyte Control Function Codes */

#define EXB_TEST_READY		0x1
#define EXB_REWIND		0x2
#define EXB_SPACE		0x3
#define EXB_LOAD		0x4
#define EXB_UNLOAD		0x5
#define EXB_WRITE_FILEMARK	0x6
 
/* Motion and Reconnect thresholds for exbModeSelect */
#define THRESHOLD_16K	0x10
#define THRESHOLD_32K	0x20
#define THRESHOLD_48K	0x30

typedef struct
{
	unsigned	valid			:1,	/* indicates bytes 3 thru 6 are valid */
			errorClass		:3, 	/* always 7 */
			errorCode		:4;	/* always 0 */
	unsigned char	segNum;				/* zero unless check is result of copy*/
	unsigned	filemark		:1, 	/* read a filemark */
			endOfMedia		:1,	/* Logical EOT or BOT */
			ili			:1,	/* incorrect logical blk length req. */
			reserved		:1,
			senseKey		:4;	/* sense key gives error indication */
	unsigned char	infoBytes[4];	
	unsigned char 	adtlSenseLength,		/* 0e except for copy command */
			reserved1,
			reserved2,
			reserved3,
			reserved4,
			adtlSenseCode,			/* 04 when not ready */
			reserved5,
			reserved6,
			errorCountHb,			/* high byte of error count */
			errorCountMB,			/* middle byte of error count */
			errorCountLb;			/* low byte of error count */
	unsigned	powerFail	 	:1,
			busParityError		:1,
			bufferParityError 	:1,
			mediaError		:1,
			errorCountOverflow	:1,
			tapeMotionError		:1,
			tapeNotPresent		:1,
			bot			:1,	/* beginning of tape */
			transferAbort		:1,
			tapeMarkDetect		:1,
			writeProtect		:1,
			filemarkError		:1,
			underrunError		:1,
			mediaWriteError		:1,
			servoError		:1,
			formatterError		:1,
			futureStuff		:5,
			peot			:1,
			spliceError1		:1,
			spliceError2		:1;
	unsigned char	reserved7,
			remainTapeHb,			/* high byte of remaining tape */
			remainTapeMb,			/* middle byte of remaining tape */
			remainTapeLb;			/* low byte of remaining tape */
}EXB_SENSE_DATA;

/**********************************************************/
/*      Type Definitions for the Exabyte Functions        */
/**********************************************************/


typedef struct
{
	unsigned	senseDataValid		:1, 	/* indicates if bits 1-7 are valid */
			tapeNotPresent		:1,
			writeProtect		:1,
			endOfMedia		:1,	/* Logical EOT or BOT */
			bot			:1,	/* beginning of tape */
			peot 			:1,
			filemark		:1,	/* read a filemark */
			ili			:1,	/* incorrect logical blk length req. */
			reserved		:20,
			errorCode		:4;	/* error code is always valid */
}EXB_STATUS;

/*******************************************************************************
*	exbInit()
*
*	Description:	Initializes and installs SCSI driver
*
*	Returns:	OK or ERROR
*
*******************************************************************************/
extern	STATUS		exbInit(void);


/*******************************************************************************
*	exbOpen()
*
*	Description:	Creates the tape drive device, waits for it to be 
*			ready and selects the mode
*
*	Returns:	device or ERROR
*
*******************************************************************************/
extern 	SCSI_PHYS_DEV*	exbOpen(int scsi_bus_id);


/*******************************************************************************
*	exbClose()
*
*	Description:	Deletes the tape drive device.
*
*	Returns:	device or ERROR
*
*******************************************************************************/
void exbClose(SCSI_PHYS_DEV * this_device);


/*******************************************************************************
*	exbModeSelect()
*
*	Description:	Selects the Mode for the tape drive
*
*	Returns:	EXB_STATUS
*
*******************************************************************************/
extern	EXB_STATUS 	exbModeSelect(SCSI_PHYS_DEV* scsi_phys_dev, int motionThreshold, int reconnectThreshold);


/*******************************************************************************
*	exbRequestSense()
*
*	Description:	Reads sense data for the tape drive
*
*	Returns:	EXB_STATUS
*
*******************************************************************************/
extern	EXB_STATUS	exbRequestSense(SCSI_PHYS_DEV*  scsi_phys_dev,
                            EXB_SENSE_DATA  *your_sense_buffer_ptr);


/*******************************************************************************
*	exbRead()
*
*	Description:	Reads data from the tape
*
*	Returns:	EXB_STATUS
*			*byte_count_ptr = actual number of bytes read
*
*******************************************************************************/
extern	EXB_STATUS	exbRead(SCSI_PHYS_DEV *scsi_phys_dev,
                    		unsigned char *buffer,
                    		int *byte_count_ptr);


/*******************************************************************************
*	exbWrite()
*
*	Description:	Writes data to the tape
*
*	Returns:	EXB_STATUS
*			*byte_count_ptr = actual number of bytes written
*
*******************************************************************************/
extern	EXB_STATUS	exbWrite(SCSI_PHYS_DEV *scsi_phys_dev,
                                unsigned char *buffer,
                                int *byte_count_ptr);

extern	EXB_STATUS 	exbTestReady(SCSI_PHYS_DEV* scsi_phys_dev);

extern	EXB_STATUS 	exbRewind(SCSI_PHYS_DEV* scsi_phys_dev);

extern	EXB_STATUS 	exbSpace(SCSI_PHYS_DEV*    scsi_phys_dev,
                        	int             blks,
                        	int             *count_ptr);

extern	EXB_STATUS	exbLoadTape(SCSI_PHYS_DEV* scsi_phys_dev);

extern	EXB_STATUS	exbUnloadTape(SCSI_PHYS_DEV* scsi_phys_dev);

extern	EXB_STATUS 	exbWriteFilemark(SCSI_PHYS_DEV* scsi_phys_dev);

extern	EXB_STATUS	exbControl(SCSI_PHYS_DEV* scsi_phys_dev,
				int cntl_code,
				int blks,
				int *count_ptr);

 
extern void     exbStatusShow (EXB_STATUS status);


----------
X-Sun-Data-Type: c-file
X-Sun-Data-Description: c-file
X-Sun-Data-Name: exabyte.c
X-Sun-Content-Lines: 1749

static char resid[] = "$Id$";
/* Developed by Johns Hopkins University Applied Physics Laboratory 1990
 *
 * Project:    
 *
 * Module:     exabyte.c
 *
 * Function: 	
 *
 * Resources:
 *          
 *       Hardware Resources:
 *                                                                      
 *       Communication Resources:
 *			comms                              
 *                             
 *       Tasks:
 *        Task Name             Priority                                   
 *        ---------             --------                                  
 *                      None
 *                                                                      
 *       Interrupt Vectors:                                       
 *        Name                  Vector                           
 *        ---------             ------                          
 *	  		None
 *                                                            
 *       Libraries Required:                                 
 *        Library Name          Description                 
 *        ------------          -----------                
 *			comms
 *			VxWorks
 *
 *       Entry Points:                                                        
 *       -------------                                                     
 *
 *	Host System:	VxWorks 5.0.2
 *	Language:	C
 *	Modifications:  $Log$
 *
*/


/************************************
*          Module Includes          *
************************************/

#include "vxWorks.h"
#include "scsiLib.h"
#include "exabyte.h"
#include "errno.h"

/************************************
*           Local Defines           *
************************************/

#define FIO_SCSI_COMMAND 	40		
#define SCSI_DBG		1

/************************************
*          Local Functions          *
************************************/


/************************************
*          Local Variables          *
************************************/

unsigned int      deltatick;

static int      second;


/*################*/
/* Module Externs -- this should be in the includes file*/
/*################*/

/* Scsi Controller Created in sysLib.c */

extern SCSI_CTRL	*pSysScsiCtrl;

/*################*/
/* Module Globals -- this should be in the includes file */
/*################*/

/*#################*/
/* Module Routines */
/*#################*/



/*******************************************************************************
* 
* Initialize Exabyte Tape Drive
*
*-------------------------------------------------------------------------------
*
* Function:	STATUS exbInit ()
*
*-------------------------------------------------------------------------------
*
* Description: 
*		WARNING: Only call this once at system start up!!
*
*-------------------------------------------------------------------------------
*
* Returns:	
*
*******************************************************************************/


STATUS exbInit (void)

{

	STATUS initStatus;

	second = sysClkRateGet();

	initStatus = sysScsiInit ();
 
	/* wait 8 seconds for scsi bus reset to settle */
 
	taskDelay (second*8);
	return (initStatus);
 
}


/*******************************************************************************
*
* Create Tape Device and Select its Mode
*
*-------------------------------------------------------------------------------
*
* Function:	SCSI_PHYS_DEV *exbOpen (scsi_bus_id)
*
*-------------------------------------------------------------------------------
*
* Description:
*		WARNING: Only call this once for each tape drive!!
*
*-------------------------------------------------------------------------------
*
* Returns:	
*
*******************************************************************************/


SCSI_PHYS_DEV *exbOpen (int scsi_bus_id)
 
{
	EXB_STATUS tape_status;
	SCSI_PHYS_DEV *this_device;
	int count;

	second = sysClkRateGet();

	/* Create Exabyte Device */

	logMsg ("exbOpen: going to call scsiPhysDevCreate\n");

	this_device = scsiPhysDevCreate (pSysScsiCtrl,
					scsi_bus_id,
					0,
					0,
					SCSI_DEV_SEQ_ACCESS,
					1,
					0x7fffffff,
					/* @prb 16384); temp for long tape*/
					0x40000);

	if (this_device == (SCSI_PHYS_DEV*) NULL)
	{
		return ((SCSI_PHYS_DEV*)-1);
	}

	logMsg ("exbOpen: made the scsiPhysDevCreate call and returned ok\n");

	/* don't send identify message so tape drive won't disconnect */

	this_device->useIdentify = FALSE;

#ifdef  INCLUDE_WAIT_FOR_READY

	/* Wait for Exabyte to be ready */

	count = 0;

	/*logMsg ("exbOpen:going to call test ready, going to delay 2 min. first\n");
	taskDelay (7200);*/

	logMsg ("exbOpen:going to call test ready\n");
 
	tape_status = exbTestReady (this_device);
	while	((tape_status.errorCode == EXB_UNIT_ATTENTION) 
		|| (tape_status.errorCode == EXB_NOT_READY) 	
		|| (tape_status.errorCode == EXB_BUSY))

	{
		logMsg ("waiting for exabyte drive to be ready so I can select the mode\n");
		/* logMsg ("exabyte drive status = %x\n", tape_status.errorCode ); */
		taskDelay (5 * second);	/* delay 5 seconds */
		count += 1;

		if (count >= 300)
		{
			logMsg ("Exabyte drive not ready after 5 minutes, giving up/n");
			scsiPhysDevDelete (this_device);
			return ((SCSI_PHYS_DEV*)-1);
		}

		tape_status = exbTestReady (this_device);
	} /* while */ 

	if (tape_status.errorCode != EXB_NO_ERROR)
	{
		logMsg ("Error testing for exabyte to be ready => giving up the ship\n");
		logMsg ("tape_status.errorCode = 0x%x\n", tape_status.errorCode);
		scsiPhysDevDelete (this_device);
		return ((SCSI_PHYS_DEV*)-1);
	}

#endif

	/* Select Exabyte Mode */
	count = 0;
        /* For 8500, 48K motion threshold and 32K reconnect threshold */
	tape_status = exbModeSelect (this_device,THRESHOLD_48K,THRESHOLD_32K);
	while	((tape_status.errorCode == EXB_UNIT_ATTENTION) 
		|| (tape_status.errorCode == EXB_NOT_READY) 
		|| (tape_status.errorCode == EXB_BUSY))
	{
		taskDelay (second);
		logMsg ("trying to select mode for exabyte drive\n");
		count += 1;

		if (count >= 300)
		{
			logMsg ("Couldn't send mode select to Exabyte drive after 5 min., giving up\n");
			scsiPhysDevDelete (this_device);
			return ((SCSI_PHYS_DEV*)-1);
		} /* if */

	        tape_status = exbModeSelect (this_device,THRESHOLD_48K,THRESHOLD_32K);

	} /* while */ 

	if (tape_status.errorCode != EXB_NO_ERROR)
	{
		logMsg ("Error selecting mode on exabyte => giving up the ship\n");
		scsiPhysDevDelete (this_device);
		return ((SCSI_PHYS_DEV*)-1);
	} /* if */

	return (this_device);
} /* exbOpen */


/*******************************************************************************
*
* Create Tape Device and Select its Mode
*
*-------------------------------------------------------------------------------
*
* Function:	void exbClose (SCSI_PHYS_DEV *)
*
*-------------------------------------------------------------------------------
*
* Description:
*		WARNING: Only call this once for each tape drive!!
*
*-------------------------------------------------------------------------------
*
* Returns:	
*
*******************************************************************************/


void exbClose (SCSI_PHYS_DEV * this_device)
 
{
		scsiPhysDevDelete (this_device);
}




/*******************************************************************************
*
* Select the mode of tape drive
*
*-------------------------------------------------------------------------------
*
* Function:	EXB_STATUS exbModeSelect (scsi_phys_dev, 
*                      int motionThreshold, int reconnectThreshold);)
*
*-------------------------------------------------------------------------------
*
* Description:
*
*-------------------------------------------------------------------------------
*
* Returns:	
*
*******************************************************************************/


EXB_STATUS 	exbModeSelect(SCSI_PHYS_DEV* scsi_phys_dev, int motionThreshold, int reconnectThreshold)
{
	SCSI_COMMAND 		scsi_cmnd_blk;		/* scsi command byte array */
	SCSI_TRANSACTION 	scsi_action;		/* information on scsi transaction */
	EXB_SENSE_DATA 		sense_buffer;		/* buffer for reading sense data */
	UTINY 			select_buffer[16];	/* mode select data */
	EXB_STATUS		return_status;
	unsigned int		debug_status;
	int			error_code;

	return_status.senseDataValid	= 0;
	return_status.tapeNotPresent	= 0;
	return_status.writeProtect	= 0;
	return_status.endOfMedia	= 0;
	return_status.bot		= 0;
	return_status.peot		= 0;
	return_status.filemark		= 0;
	return_status.ili		= 0;
	return_status.reserved		= 0;
	return_status.errorCode		= 0;

	/* Build scsi command block for this command */

	scsi_cmnd_blk[0]	= SCSI_OPCODE_MODE_SELECT;
	scsi_cmnd_blk[1]	= 0;
	scsi_cmnd_blk[2]	= 0;
	scsi_cmnd_blk[3]	= 0;
	scsi_cmnd_blk[4]	= 0x10;
	scsi_cmnd_blk[5]	= 0;

	/* Build select data */

	select_buffer[0]	= 0x00;
	select_buffer[1]	= 0x00;

	/* buffered mode of operation */

	select_buffer[2]	= 0x10;
	select_buffer[3]	= 0x08;
	select_buffer[4]	= 0x00;
	select_buffer[5]	= 0x00;
	select_buffer[6]	= 0x00;
	select_buffer[7]	= 0x00;
	select_buffer[8]	= 0x00;
	select_buffer[9]	= 0x00;
	select_buffer[10]	= 0x00;

	/* variable length data blocks */

	select_buffer[11]	= 0x00;
	select_buffer[12]	= 0x00;
	select_buffer[13]	= 0x00;

	select_buffer[14]	= (unsigned char)motionThreshold;
	select_buffer[15]	= (unsigned char)reconnectThreshold;


	/* Specify the transaction needed */

	scsi_action.cmdAddress		= scsi_cmnd_blk;
	scsi_action.cmdLength		= 6;
	scsi_action.dataAddress		= (UTINY *) select_buffer;
	scsi_action.dataDirection	= WRITE;
	scsi_action.dataLength		= 16;
	scsi_action.addLengthByte	= 0;

	/* Send the Scsi Command to the Tape Drive */


	debug_status = scsiIoctl (scsi_phys_dev,
				FIO_SCSI_COMMAND,
				(int) &scsi_action);

	if (	(debug_status == OK)
		&& (scsi_action.statusByte == SCSI_STATUS_GOOD)	)
	{

		/* return EXB_NO_ERROR if SCSI_STATUS_GOOD */

		return (return_status);
	}

	if (debug_status != OK) 
	{
		logMsg ("exbModeSelect: scsiIoctl returned => %x \n",
			 debug_status);
		error_code = errnoGet ();
		printErrno (error_code);

		switch (error_code)
		{
			case S_scsiLib_DEV_NOT_READY:
				return_status.errorCode = EXB_NOT_READY;
				break;
			case S_scsiLib_WRITE_PROTECTED:
				return_status.errorCode = EXB_DATA_PROTECT;
				break;
			case S_scsiLib_MEDIUM_ERROR:
				return_status.errorCode = EXB_MEDIUM_ERROR;
				break;
			case S_scsiLib_HARDWARE_ERROR:
				return_status.errorCode = EXB_HARDWARE_ERROR;
				break;
			case S_scsiLib_ILLEGAL_REQUEST:
				return_status.errorCode = EXB_ILLEGAL_REQUEST;
				break;
			case S_scsiLib_BLANK_CHECK:
				return_status.errorCode = EXB_BLANK_CHECK; 
				break;
			case S_scsiLib_VOLUME_OVERFLOW:
				return_status.errorCode = EXB_VOLUME_OVERFLOW;
				break;
			case S_scsiLib_SELECT_TIMEOUT:
				return_status.errorCode = EXB_BUSY;
				break;
			case S_scsiLib_UNIT_ATTENTION:
				return_status.errorCode = EXB_UNIT_ATTENTION;
				break;
			case S_scsiLib_ABORTED_COMMAND:
				return_status.errorCode = EXB_ABORTED_COMMAND;
				break;
			default:
				return_status.errorCode = EXB_NO_SENSE;
		}/* end switch */

		return (return_status);
	}

	switch (scsi_action.statusByte)
	{
		case SCSI_STATUS_BUSY:
			return (return_status);
			break;
		case SCSI_STATUS_CHECK_CONDITION:
			return (exbRequestSense (scsi_phys_dev, 
							(EXB_SENSE_DATA*) NULL));
			break;
		case SCSI_STATUS_MASK:
			logMsg ("SCSI_STATUS_MASK\n");
			break;
		case SCSI_STATUS_CONDITION_MET:
			logMsg ("SCSI_STATUS_CONDITION_MET\n");
			break;
		case SCSI_STATUS_INTMED_GOOD:
			logMsg ("SCSI_STATUS_INTMED_GOOD\n");
			break;
		case SCSI_STATUS_INTMED_COND_MET:
			logMsg ("SCSI_STATUS_INTMED_COND_MET\n");
			break;
		case SCSI_STATUS_RESERV_CONFLICT:
			logMsg ("SCSI_STATUS_RESERV_CONFLICT\n");
			break;
		case SCSI_STATUS_QUEUE_FULL:
			logMsg ("SCSI_STATUS_QUEUE_FULL\n");
			break;
		default:
			logMsg ("SCSI_STATUS UNKNOWN => %x \n",
				scsi_action.statusByte);
			break;
	}


	return_status.errorCode = EXB_NO_SENSE;
	return (return_status);

}



/*******************************************************************************
* 
* Read data from the tape drive.
*
*-------------------------------------------------------------------------------
*
* Function:	EXB_STATUS exbRead (	scsi_phys_dev,
				buffer,
				byte_count_ptr)
*
*-------------------------------------------------------------------------------
*
* Description:
*		WARNING: The Exabyte tape drive takes 40-50 seconds to read the
*		first thing on tape at the beginning of tape.  One successive
*		reads, there is 256KBytes of cache to help speed things up.
*		The read/write threshold is set to begin data transfer to tape
*		when the cache has emptied 48KBytes of data.
*
*
*-------------------------------------------------------------------------------
*
* Returns:	
*
*******************************************************************************/


EXB_STATUS exbRead (SCSI_PHYS_DEV *scsi_phys_dev,
		    unsigned char *buffer,
		    int *byte_count_ptr)

{
	SCSI_COMMAND 		scsi_cmnd_blk;	/* scsi command byte array */
	SCSI_TRANSACTION 	scsi_action;	/* information on scsi transaction */
	EXB_SENSE_DATA 		sense_buffer;	/* buffer for reading sense data */
	EXB_STATUS 		tape_status;	/* status returned from request sense */
	int 			bytes_not_transferred;
	unsigned char		*count_ptr;
	EXB_STATUS 		return_status;
	unsigned int		debug_status;
	int 			error_code;
	int 			time_out_count;

	return_status.senseDataValid	= 0;
	return_status.tapeNotPresent 	= 0;
	return_status.writeProtect 	= 0;
	return_status.endOfMedia 	= 0;
	return_status.bot 		= 0;
	return_status.peot 		= 0;
	return_status.filemark 		= 0;
	return_status.ili 		= 0;
	return_status.reserved 		= 0;
	return_status.errorCode 	= 0;

	count_ptr = (unsigned char*) byte_count_ptr;

	/* Build scsi command block for this command */

	scsi_cmnd_blk[0] 	= SCSI_OPCODE_READ;
	scsi_cmnd_blk[1] 	= 0;
	scsi_cmnd_blk[2] 	= (UTINY) count_ptr[1];
	scsi_cmnd_blk[3] 	= (UTINY) count_ptr[2];
	scsi_cmnd_blk[4] 	= (UTINY) count_ptr[3];
	scsi_cmnd_blk[5] 	= 0;

	/* Specify the transaction needed */
	scsi_action.cmdAddress		= scsi_cmnd_blk;
	scsi_action.cmdLength		= 6;
	scsi_action.dataAddress 	= (UTINY *) buffer;
	scsi_action.dataDirection 	= READ;
	scsi_action.dataLength		= *byte_count_ptr;
	scsi_action.addLengthByte	= -1;
	/*scsi_action.dataLength	= 1; */
	/*scsi_action.addLengthByte	= *byte_count_ptr -1; */

	/*logMsg ("trying to read 0x%x%x%x bytes\n", 	count_ptr[1], 
							count_ptr[2], 
							count_ptr[3]); */

	/* Send the Scsi Command to the Tape Drive */

	debug_status = scsiIoctl 	(scsi_phys_dev,
					FIO_SCSI_COMMAND,
					(int) &scsi_action);

	if (	(debug_status == OK)
		&& (scsi_action.statusByte == SCSI_STATUS_GOOD)	)
	{

		/* return EXB_NO_ERROR if SCSI_STATUS_GOOD */
	
		return (return_status);
	}

	if (debug_status != OK) 
	{
		logMsg ("exbRead: scsiIoctl returned => %x \n", debug_status);
		error_code = errnoGet ();
		switch (error_code)
 		{
			case S_scsiLib_DEV_NOT_READY:
				if (SCSI_DBG)
				{
 					logMsg ("exbRead: error is DEV_NOT_READY\n");
				}

				return_status.errorCode = EXB_NOT_READY;
				break;
 			case S_scsiLib_WRITE_PROTECTED:
				if (SCSI_DBG)
				{
					logMsg ("exbRead: error is WRITE_PROTECTED\n");
				}
				return_status.errorCode = EXB_DATA_PROTECT;
				break;
			case S_scsiLib_MEDIUM_ERROR:
				if (SCSI_DBG)
				{
					logMsg ("exbRead: error is EXB_MEDIUM_ERROR\n");
				}
				return_status.errorCode = EXB_MEDIUM_ERROR;
				break;
			case S_scsiLib_HARDWARE_ERROR:
				if (SCSI_DBG)
				{
					logMsg ("exbRead: error is EXB_HARDWARE_ERROR\n");
				}
				return_status.errorCode = EXB_HARDWARE_ERROR;
				break;
			case S_scsiLib_ILLEGAL_REQUEST:
				if (SCSI_DBG)
				{
					logMsg ("exbRead: error is EXB_ILLEGAL_REQUEST\n");
				}
				return_status.errorCode = EXB_ILLEGAL_REQUEST;
				break;
			case S_scsiLib_BLANK_CHECK:
				if (SCSI_DBG)
				{
					logMsg ("exbRead: error is EXB_BLANK_CHECK\n");
				}
				return_status.errorCode = EXB_BLANK_CHECK; 
				break;
			case S_scsiLib_VOLUME_OVERFLOW:
				if (SCSI_DBG)
				{
					logMsg ("exbRead: error is EXB_VOLUME_OVERFLOW\n"); 
				}
				return_status.errorCode = EXB_VOLUME_OVERFLOW;
				break;
			case S_scsiLib_SELECT_TIMEOUT:
				if (SCSI_DBG)
				{
					logMsg ("exbRead: error is SELECT_TIMEOUT\n");
				}
				return_status.errorCode = EXB_BUSY;
				break;
			case S_scsiLib_UNIT_ATTENTION:
				if (SCSI_DBG)
				{
					logMsg ("exbRead: error is EXB_UNIT_ATTENTION\n");
				}
				return_status.errorCode = EXB_UNIT_ATTENTION; 
				break;
			case S_scsiLib_ABORTED_COMMAND:
				if (SCSI_DBG)
				{
					logMsg ("exbRead: error is EXB_ABORTED_COMMAND\n");
				}
				return_status.errorCode = EXB_ABORTED_COMMAND;
				break;
			default:
				if (SCSI_DBG)
				{
					logMsg ("exbRead: error is EXB_NO_SENSE\n");
				}
		}/* end switch */

		*byte_count_ptr = 0;
		return (return_status);
	}

	switch (scsi_action.statusByte)
	{
		case SCSI_STATUS_BUSY:
			*byte_count_ptr = 0;
			return_status.errorCode = EXB_BUSY;
			return (return_status);
			break;
		case SCSI_STATUS_CHECK_CONDITION:
			return_status = exbRequestSense (scsi_phys_dev, &sense_buffer);
			time_out_count = 0;
			while (	(return_status.errorCode == EXB_NOT_READY)
			 	|| (return_status.errorCode == EXB_BUSY)	)

			{
				taskDelay (second);
				time_out_count+=1;
				if (time_out_count >= 120) 
				{
					*byte_count_ptr = 0;
					return (return_status);
				}
				return_status = exbRequestSense	(scsi_phys_dev,
									 &sense_buffer);
			}/* end while */

			if (return_status.errorCode != EXB_NO_ERROR)
			{
				*byte_count_ptr = 0;
				return (return_status);
			}

			if (	(return_status.ili) 
				|| (return_status.filemark)
				|| (return_status.endOfMedia)
				||(return_status.errorCode == EXB_BLANK_CHECK))
			{

				/* the driver already read the sense data so the valid bit */
				/*is no longer set; however the data still seems good*/
	
				/*logMsg ("sense_buffer.valid bit = %x", sense_buffer.valid);*/

				/* set the number of bytes not transferred */

				bytes_not_transferred =
					(	(sense_buffer.infoBytes[1] << 16)
						| (sense_buffer.infoBytes[2] << 8)
						| (sense_buffer.infoBytes[3])	);

				/* sign extend if negative number */

				if (0x00800000 & bytes_not_transferred)
				{
					bytes_not_transferred = (0xff000000 | bytes_not_transferred);
				}
				/* update the number of bytes actually transferred */

				*byte_count_ptr -= bytes_not_transferred;
			}
			return (return_status);
			break;
		case SCSI_STATUS_MASK:
			logMsg ("SCSI_STATUS_MASK\n");
			break;
		case SCSI_STATUS_CONDITION_MET:
			logMsg ("SCSI_STATUS_CONDITION_MET\n");
			break;
		case SCSI_STATUS_INTMED_GOOD:
			logMsg ("SCSI_STATUS_INTMED_GOOD\n");
			break;
		case SCSI_STATUS_INTMED_COND_MET:
			logMsg ("SCSI_STATUS_INTMED_COND_MET\n");
			break;
		case SCSI_STATUS_RESERV_CONFLICT:
			logMsg ("SCSI_STATUS_RESERV_CONFLICT\n");
			break;
		case SCSI_STATUS_QUEUE_FULL:
			logMsg ("SCSI_STATUS_QUEUE_FULL\n");
			break;
		default:
			logMsg ("SCSI_STATUS UNKNOWN => %x \n", scsi_action.statusByte);
			break;
	}

	*byte_count_ptr = 0;
	return_status.errorCode = EXB_NO_SENSE;
	return (return_status);

}



/*******************************************************************************
*
* Write data to the tape.
*
*-------------------------------------------------------------------------------
*
* Function:	EXB_STATUS exbWrite (scsi_phys_dev,
				buffer,
				byte_count_ptr)
*
*-------------------------------------------------------------------------------
*
* Description:
*		Recommendations: The exabyte writes in 8Kbyte strips on the
*		tape.  To maximize data throughput, you should transfer data in
*		blocks which are multiples of 8Kbytes.
*
*		* WARNING: The Exabyte tape drive takes 40-50 seconds to write
*		the first thing on tape at the beginning of tape.  However,
*		there is 256KBytes of cache to help soak up some data during
*		this time.  The read/write threshold is set to begin data
*		transfer to tape when the cache has emptied 48KBytes of data.
*
*-------------------------------------------------------------------------------
*
* Returns:	
*
*******************************************************************************/


EXB_STATUS exbWrite (SCSI_PHYS_DEV *scsi_phys_dev,
		     unsigned char   *buffer,
		     int             *byte_count_ptr)

{
	SCSI_COMMAND 		scsi_cmnd_blk;	/* scsi command byte array */
	SCSI_TRANSACTION 	scsi_action;	/* information on scsi transaction */
	EXB_SENSE_DATA 		sense_buffer;	/* buffer for reading sense data */
	unsigned char 		*count_ptr;
	EXB_STATUS 		return_status;
	unsigned int 		debug_status;
	int 			error_code;
	unsigned int 		timecount1, timecount2;

	return_status.senseDataValid 	= 0;
	return_status.tapeNotPresent 	= 0;
	return_status.writeProtect 	= 0;
	return_status.endOfMedia 	= 0;
	return_status.bot 		= 0;
	return_status.peot 		= 0;
	return_status.filemark 		= 0;
	return_status.ili 		= 0;
	return_status.reserved 		= 0;
	return_status.errorCode 	= 0;

	count_ptr = (unsigned char*) byte_count_ptr;

	/* Build scsi command block for this command */

	scsi_cmnd_blk[0] = SCSI_OPCODE_WRITE;
	scsi_cmnd_blk[1] = 0;
	scsi_cmnd_blk[2] = (UTINY) count_ptr[1];
	scsi_cmnd_blk[3] = (UTINY) count_ptr[2];
	scsi_cmnd_blk[4] = (UTINY) count_ptr[3];
	scsi_cmnd_blk[5] = 0;
	
	/* Specify the transaction needed */

	scsi_action.cmdAddress		= scsi_cmnd_blk;
	scsi_action.cmdLength 		= 6;
	scsi_action.dataAddress 	= (UTINY *) buffer;
	scsi_action.dataDirection 	= WRITE;
	scsi_action.dataLength		= *byte_count_ptr;
	scsi_action.addLengthByte	= 0;

	/* Send the Scsi Command to the Tape Drive */

	timecount1 	= tickGet ();
	debug_status 	= scsiIoctl (scsi_phys_dev,
					FIO_SCSI_COMMAND,
					(int) &scsi_action);

	if (	(debug_status == OK) 
		&& (scsi_action.statusByte == SCSI_STATUS_GOOD)	)
	{ 
		timecount2 	= tickGet ();
		deltatick 	= timecount2 - timecount1;

		/* return EXB_NO_ERROR is SCSI_STATUS_GOOD */

		return (return_status);
	}

	if (debug_status != OK) 
	{
		logMsg ("exbWrite: scsiIoctl returned => %x \n", 
			debug_status);
		error_code = errnoGet ();
		switch (error_code)
		{
			case S_scsiLib_DEV_NOT_READY:
				if (SCSI_DBG)
				{
					logMsg ("exbWrite: error is DEV_NOT_READY\n");
				}
				return_status.errorCode = EXB_NOT_READY;
				break;
			case S_scsiLib_WRITE_PROTECTED:
				if (SCSI_DBG)
				{
					logMsg ("exbWrite: error is WRITE_PROTECTED\n");
				}
				return_status.errorCode = EXB_DATA_PROTECT;
				break;
			case S_scsiLib_MEDIUM_ERROR:
				if (SCSI_DBG)
				{
					logMsg ("exbWrite: error is EXB_MEDIUM_ERROR\n");
				}
				return_status.errorCode = EXB_MEDIUM_ERROR;
				break;
			case S_scsiLib_HARDWARE_ERROR:
				if (SCSI_DBG)
				{
					logMsg ("exbWrite: error is EXB_HARDWARE_ERROR\n");
				}
				return_status.errorCode = EXB_HARDWARE_ERROR;
				break;
			case S_scsiLib_ILLEGAL_REQUEST:
				if (SCSI_DBG)
				{
					logMsg ("exbWrite: error is EXB_ILLEGAL_REQUEST\n");
				}
				return_status.errorCode = EXB_ILLEGAL_REQUEST;
				break;
			case S_scsiLib_BLANK_CHECK:
				if (SCSI_DBG)
				{
					logMsg ("exbWrite: error is EXB_BLANK_CHECK\n");
				}
				return_status.errorCode = EXB_BLANK_CHECK; 
				break;
			case S_scsiLib_VOLUME_OVERFLOW:
				if (SCSI_DBG)
				{
					logMsg ("exbWrite: error is EXB_VOLUME_OVERFLOW\n");
				}
				return_status.errorCode = EXB_VOLUME_OVERFLOW;
				break;
			case S_scsiLib_SELECT_TIMEOUT:
				if (SCSI_DBG)
				{
					logMsg ("exbWrite: error is SELECT_TIMEOUT\n");
				}
				return_status.errorCode = EXB_BUSY;
				break;
			case S_scsiLib_UNIT_ATTENTION:
				if (SCSI_DBG)
				{
					logMsg ("exbWrite: error is EXB_UNIT_ATTENTION\n");
				}
				return_status.errorCode = EXB_UNIT_ATTENTION;
				break;
			case S_scsiLib_ABORTED_COMMAND:
				if (SCSI_DBG)
				{
					logMsg ("exbWrite: error is EXB_ABORTED_COMMAND\n");
				}
				return_status.errorCode = EXB_ABORTED_COMMAND;
				break;
			default:
				if (SCSI_DBG)
				{
					logMsg ("exbWrite: error is EXB_NO_SENSE\n");
				}
		}/* end switch */
		*byte_count_ptr = 0;
		return (return_status);
	}

	logMsg ("exbWrite: error is scsi_action.statusByte is not ready\n");
	switch (scsi_action.statusByte)
	{
		case SCSI_STATUS_BUSY:
			*byte_count_ptr = 0;
			return_status.errorCode = EXB_BUSY;
			return (return_status);
			break;
		case SCSI_STATUS_CHECK_CONDITION:
			*byte_count_ptr = 0;
			return (exbRequestSense (scsi_phys_dev, 
							(EXB_SENSE_DATA*) NULL));
			break;
		case SCSI_STATUS_MASK:
			logMsg ("SCSI_STATUS_MASK\n");
			break;
		case SCSI_STATUS_CONDITION_MET:
			logMsg ("SCSI_STATUS_CONDITION_MET\n");
			break;
		case SCSI_STATUS_INTMED_GOOD:
			logMsg ("SCSI_STATUS_INTMED_GOOD\n");
			break;
		case SCSI_STATUS_INTMED_COND_MET:
			logMsg ("SCSI_STATUS_INTMED_COND_MET\n");
			break;
		case SCSI_STATUS_RESERV_CONFLICT:
			logMsg ("SCSI_STATUS_RESERV_CONFLICT\n");
			break;
		case SCSI_STATUS_QUEUE_FULL:
			logMsg ("SCSI_STATUS_QUEUE_FULL\n");
			break;
		default:
			logMsg ("SCSI_STATUS UNKNOWN => %x \n", scsi_action.statusByte);
			break;
	}

	*byte_count_ptr 		= 0;
	return_status.errorCode 	= EXB_NO_SENSE;
	return (return_status);

}

/*******************************************************************************
*
* Read the Sense Data from the Tape Drive
*
*-------------------------------------------------------------------------------
*
* Function:	EXB_STATUS exbRequestSense 	(scsi_phys_dev, 
							your_sense_buffer_ptr)
*
*-------------------------------------------------------------------------------
*
* Description:
*
*
*-------------------------------------------------------------------------------
*
* Returns:	
*
*******************************************************************************/


EXB_STATUS exbRequestSense (SCSI_PHYS_DEV*  scsi_phys_dev, 
			    EXB_SENSE_DATA  *your_sense_buffer_ptr)
{
	SCSI_COMMAND 		scsi_cmnd_blk;	/* scsi command byte array */
	SCSI_TRANSACTION 	scsi_action;	/* information on scsi transaction */
	EXB_SENSE_DATA 		my_sense_buffer;	/* buffer for reading sense data */
	EXB_SENSE_DATA 		*sense_buffer_ptr;/* buffer for reading sense data */
	EXB_STATUS 		return_status;
	unsigned int 		debug_status;
	int 			error_code;

	return_status.senseDataValid 	= 0;
	return_status.tapeNotPresent 	= 0;
	return_status.writeProtect 	= 0;
	return_status.endOfMedia 	= 0;
	return_status.bot 		= 0;
	return_status.peot 		= 0;
	return_status.filemark 		= 0;
	return_status.ili 		= 0;
	return_status.reserved 		= 0;
	return_status.errorCode 	= 0;

	/* Set up the pointer to the sense buffer we are going to use */
	/* if they passed a non-NULL ptr, then use their sense buffer */

	if (your_sense_buffer_ptr == NULL)
	{
		sense_buffer_ptr = &my_sense_buffer;
	}
	else
	{
		sense_buffer_ptr = your_sense_buffer_ptr;
	}

	/* Request Sense data if there is an error */
	/* Build scsi command block for this command */

	scsi_cmnd_blk[0] = SCSI_OPCODE_REQUEST_SENSE;
	scsi_cmnd_blk[1] = 0;
	scsi_cmnd_blk[2] = 0;
	scsi_cmnd_blk[3] = 0;
	scsi_cmnd_blk[4] = 26; /* size of max sense data */
	scsi_cmnd_blk[5] = 0;

	/* Specify the transaction needed */

	scsi_action.cmdAddress		= scsi_cmnd_blk;
	scsi_action.cmdLength 		= 6;
	scsi_action.dataAddress 	= (UTINY *) sense_buffer_ptr;
	scsi_action.dataDirection 	= READ;
	scsi_action.dataLength		= 26;
	scsi_action.addLengthByte	= 0;

	debug_status = scsiIoctl (scsi_phys_dev,
					FIO_SCSI_COMMAND,
					(int) & scsi_action);

	if (	(debug_status == OK)
		&& (scsi_action.statusByte == SCSI_STATUS_GOOD)	)
	{
		return_status.senseDataValid 	= 1;
		return_status.tapeNotPresent 	= sense_buffer_ptr->tapeNotPresent;
		return_status.writeProtect 	= sense_buffer_ptr->writeProtect;
		return_status.endOfMedia 	= sense_buffer_ptr->endOfMedia;
		return_status.bot 		= sense_buffer_ptr->bot;
		return_status.peot 		= sense_buffer_ptr->peot;
		return_status.filemark 		= sense_buffer_ptr->filemark;
		return_status.ili 		= sense_buffer_ptr->ili;
		return_status.reserved 		= 0;
		return_status.errorCode 	= sense_buffer_ptr->senseKey;
		return (return_status);
	}

	if (debug_status != OK) 
	{
		logMsg ("exbRequestSense: scsiIoctl returned => %x \n", debug_status);
		error_code = errnoGet ();
		switch (error_code)
		{
			case S_scsiLib_DEV_NOT_READY:
				return_status.errorCode = EXB_NOT_READY;
				break;
			case S_scsiLib_WRITE_PROTECTED:
				return_status.errorCode = EXB_DATA_PROTECT;
				break;
			case S_scsiLib_MEDIUM_ERROR:
				return_status.errorCode = EXB_MEDIUM_ERROR;
				break;
			case S_scsiLib_HARDWARE_ERROR:
				return_status.errorCode = EXB_HARDWARE_ERROR;
				break;
			case S_scsiLib_ILLEGAL_REQUEST:
				return_status.errorCode = EXB_ILLEGAL_REQUEST;
				break;
			case S_scsiLib_BLANK_CHECK:
				return_status.errorCode = EXB_BLANK_CHECK; 
				break;
			case S_scsiLib_VOLUME_OVERFLOW:
				return_status.errorCode = EXB_VOLUME_OVERFLOW;
				break;
			case S_scsiLib_SELECT_TIMEOUT:
				return_status.errorCode = EXB_BUSY;
				break;
			case S_scsiLib_UNIT_ATTENTION:
				return_status.errorCode = EXB_UNIT_ATTENTION;
				break;
			case S_scsiLib_ABORTED_COMMAND:
				return_status.errorCode = EXB_ABORTED_COMMAND;
				break;
			default:
				return_status.errorCode = EXB_NO_SENSE;
		}/* end switch */
	return (return_status);
	}

	switch (scsi_action.statusByte)
	{
		case SCSI_STATUS_BUSY:
			return_status.errorCode = EXB_BUSY;
			return (return_status);
			break;
		case SCSI_STATUS_CHECK_CONDITION:
			logMsg ("SCSI_STATUS_CHECK_CONDITION within a request sense??\n");
			break;
		case SCSI_STATUS_MASK:
			logMsg ("exbRequestSense:SCSI_STATUS_MASK\n");
			break;
		case SCSI_STATUS_CONDITION_MET:
			logMsg ("exbRequestSense:SCSI_STATUS_CONDITION_MET\n");
			break;
		case SCSI_STATUS_INTMED_GOOD:
			logMsg ("exbRequestSense:SCSI_STATUS_INTMED_GOOD\n");
			break;
		case SCSI_STATUS_INTMED_COND_MET:
			logMsg ("exbRequestSense:SCSI_STATUS_INTMED_COND_MET\n");
			break;
		case SCSI_STATUS_RESERV_CONFLICT:
			logMsg ("exbRequestSense:SCSI_STATUS_RESERV_CONFLICT\n");
			break;
		case SCSI_STATUS_QUEUE_FULL:
			logMsg ("exbRequestSense:SCSI_STATUS_QUEUE_FULL\n");
			break;
		default:
			logMsg ("exbRequestSense:SCSI_STATUS UNKNOWN => %x \n", scsi_action.statusByte);
			break;
	}

	return_status.errorCode = EXB_NO_SENSE;
	return (return_status);

}




/*******************************************************************************
*
* Interrogate the Tape Drive to see if its ready
*
*-------------------------------------------------------------------------------
*
* Function:	EXB_STATUS exbTestReady (scsi_phys_dev) 
*
*-------------------------------------------------------------------------------
*
* Description:
*
*-------------------------------------------------------------------------------
*
* Returns:	
*
*******************************************************************************/


EXB_STATUS exbTestReady (SCSI_PHYS_DEV* scsi_phys_dev)
{
	return (exbControl (scsi_phys_dev, EXB_TEST_READY, 0,0));
}


/*******************************************************************************
*
* Rewind the tape.
*
*-------------------------------------------------------------------------------
*
* Function:	EXB_STATUS exbRewind (scsi_phys_dev)
*
*-------------------------------------------------------------------------------
*
* Description:
*
*-------------------------------------------------------------------------------
*
* Returns:	
*
*******************************************************************************/


EXB_STATUS exbRewind (SCSI_PHYS_DEV* scsi_phys_dev)

{
	return (exbControl (scsi_phys_dev, EXB_REWIND, 0,0));
}


/*******************************************************************************
*
* 
*
*-------------------------------------------------------------------------------
*
* Function:	EXB_STATUS exbSpace (scsi_phys_dev, blks, count_ptr)
*
*-------------------------------------------------------------------------------
*
* Description:	The exabyte cache management parameters are specified in the
*		MODE SELECT command.  When backspacing, if the data is not in
*		the cache, the tape rewinds to the start of the file and then
*		reads forward again to the required data block.  This can be
*		extremely slow for a long file.  The cache is set up to hold
*		two previous blocks to accomodate expected playback requests.
*
*-------------------------------------------------------------------------------
*
* Returns:	
*
*******************************************************************************/


EXB_STATUS exbSpace ( SCSI_PHYS_DEV*	scsi_phys_dev,
			int 		blks,
			int 		*count_ptr)
{
	return (exbControl (scsi_phys_dev, EXB_SPACE, blks, count_ptr));
}


/*******************************************************************************
*
* Load the tape
*
*-------------------------------------------------------------------------------
*
* Function:	EXB_STATUS exbLoadTape (scsi_phys_dev)
*
*-------------------------------------------------------------------------------
*
* Description:
*
*
*-------------------------------------------------------------------------------
*
* Returns:	
*
*******************************************************************************/


EXB_STATUS exbLoadTape (SCSI_PHYS_DEV* scsi_phys_dev)
{
	return (exbControl (scsi_phys_dev, EXB_LOAD, 0,0));
}

/*******************************************************************************
*
* Unload the tape
*
*-------------------------------------------------------------------------------
*
* Function:	EXB_STATUS exbUnloadTape (scsi_phys_dev)
*
*-------------------------------------------------------------------------------
*
* Description:
*
*-------------------------------------------------------------------------------
*
* Returns:	
*
*******************************************************************************/


EXB_STATUS exbUnloadTape (SCSI_PHYS_DEV* scsi_phys_dev)
{
	return (exbControl (scsi_phys_dev, EXB_UNLOAD, 0,0));
}

/*******************************************************************************
*
* Write filemark to the tape.
*
*-------------------------------------------------------------------------------
*
* Function:	EXB_STATUS exbWriteFilemark (scsi_phys_dev)
*
*-------------------------------------------------------------------------------
*
* Description:
*		* WARNING: The Exabyte tape drive takes 40-50 seconds to write
*		the first thing on tape at the beginning of tape.  This includes
*		filemarks. Normally a filemark takes 10 seconds to be written.
*
*-------------------------------------------------------------------------------
*
* Returns:	
*
*******************************************************************************/


EXB_STATUS exbWriteFilemark (SCSI_PHYS_DEV* scsi_phys_dev)
{
	return (exbControl (scsi_phys_dev, EXB_WRITE_FILEMARK, 0,0));
}

/*******************************************************************************
*
* 
*
*-------------------------------------------------------------------------------
*
* Function:	EXB_STATUS exbControl (	scsi_phys_dev,
*							cntl_code,
*							blks,
*							count_ptr)
*
*-------------------------------------------------------------------------------
*
* Description:
*		* WARNING: NOT VISIBLE TO THE USER
*
*-------------------------------------------------------------------------------
*
* Returns:	
*
*******************************************************************************/


EXB_STATUS exbControl (	SCSI_PHYS_DEV*	scsi_phys_dev, 
			int		cntl_code, 
			int		blks, 
			int 		*count_ptr)
{
	SCSI_COMMAND 		scsi_cmnd_blk;	/* scsi command byte array */
	SCSI_TRANSACTION 	scsi_action;	/* information on scsi transaction */
	unsigned char 		*count_ptr1;
	EXB_STATUS 		return_status;
	unsigned int 		debug_status;
	int 			error_code;

	return_status.senseDataValid 	= 0;
	return_status.tapeNotPresent 	= 0;
	return_status.writeProtect 	= 0;
	return_status.endOfMedia 	= 0;
	return_status.bot 		= 0;
	return_status.peot 		= 0;
	return_status.filemark 		= 0;
	return_status.ili 		= 0;
	return_status.reserved 		= 0;
	return_status.errorCode 	= 0;
	
	count_ptr1 = (unsigned char*) count_ptr;

	switch (cntl_code)
	{
		case EXB_TEST_READY:

			/* Build scsi command block for this command */

			scsi_cmnd_blk[0] = SCSI_OPCODE_TEST_UNIT_READY;
			scsi_cmnd_blk[1] = 0;
			scsi_cmnd_blk[2] = 0;
			scsi_cmnd_blk[3] = 0;
			scsi_cmnd_blk[4] = 0;
			scsi_cmnd_blk[5] = 0;

			/* Specify the transaction needed */

			scsi_action.cmdAddress		= scsi_cmnd_blk;
			scsi_action.cmdLength 		= 6;
			scsi_action.dataAddress 	= (UTINY *)0;
			scsi_action.dataDirection 	= READ;
			scsi_action.dataLength		= 0;
			scsi_action.addLengthByte	= 0;
			break;
		case EXB_REWIND:

			/* Build scsi command block for this command */

			scsi_cmnd_blk[0] = SCSI_OPCODE_REWIND;
			scsi_cmnd_blk[1] = 0;
			scsi_cmnd_blk[2] = 0;
			scsi_cmnd_blk[3] = 0;
			scsi_cmnd_blk[4] = 0;
			scsi_cmnd_blk[5] = 0;

			/* Specify the transaction needed */

			scsi_action.cmdAddress		= scsi_cmnd_blk;
			scsi_action.cmdLength 		= 6;
			scsi_action.dataAddress 	= (UTINY *)0;
			scsi_action.dataDirection 	= READ;
			scsi_action.dataLength		= 0;
			scsi_action.addLengthByte	= 0;
			break;
		case EXB_SPACE:

			/* Build scsi command block for this command */

			scsi_cmnd_blk[0] = SCSI_OPCODE_SPACE;
			if (blks)
			{

				/* space by blocks */

				scsi_cmnd_blk[1] = 0;
			}
			else
			{
				/* space by filemarks */

				scsi_cmnd_blk[1] = 1;
			}

			scsi_cmnd_blk[2] = (UTINY) count_ptr1[1];
			scsi_cmnd_blk[3] = (UTINY) count_ptr1[2];
			scsi_cmnd_blk[4] = (UTINY) count_ptr1[3];
			scsi_cmnd_blk[5] = 0;

			/* Specify the transaction needed */

			scsi_action.cmdAddress		= scsi_cmnd_blk;
			scsi_action.cmdLength 		= 6;
			scsi_action.dataAddress 	= (UTINY *)0;
			scsi_action.dataDirection 	= READ;
			scsi_action.dataLength		= 0;
			scsi_action.addLengthByte	= 0;
			break;
		case EXB_LOAD:

			/* Build scsi command block for this command */

			scsi_cmnd_blk[0] = SCSI_OPCODE_LOAD_UNLOAD;
			scsi_cmnd_blk[1] = 0;
			scsi_cmnd_blk[2] = 0;
			scsi_cmnd_blk[3] = 0;
			scsi_cmnd_blk[4] = 1; /* load */
			scsi_cmnd_blk[5] = 0;

			/* Specify the transaction needed */

			scsi_action.cmdAddress= scsi_cmnd_blk;
			scsi_action.cmdLength = 6;
			scsi_action.dataAddress = (UTINY *)0;
			scsi_action.dataDirection = READ;
			scsi_action.dataLength= 0;
			scsi_action.addLengthByte= 0;
			break;
		case EXB_UNLOAD:

			/* Build scsi command block for this command */

			scsi_cmnd_blk[0] = SCSI_OPCODE_LOAD_UNLOAD;
			scsi_cmnd_blk[1] = 0;
			scsi_cmnd_blk[2] = 0;
			scsi_cmnd_blk[3] = 0;
			scsi_cmnd_blk[4] = 0; /* unload */
			scsi_cmnd_blk[5] = 0;

			/* Specify the transaction needed */

			scsi_action.cmdAddress		= scsi_cmnd_blk;
			scsi_action.cmdLength 		= 6;
			scsi_action.dataAddress 	= (UTINY *)0;
			scsi_action.dataDirection 	= READ;
			scsi_action.dataLength		= 0;
			scsi_action.addLengthByte	= 0;
			break;

		case EXB_WRITE_FILEMARK:

			/* Build scsi command block for this command */

			scsi_cmnd_blk[0] = SCSI_OPCODE_WRITE_FILEMARKS;
			scsi_cmnd_blk[1] = 0;
			scsi_cmnd_blk[2] = 0;
			scsi_cmnd_blk[3] = 0;
			scsi_cmnd_blk[4] = 1;
			scsi_cmnd_blk[5] = 0;

			/* Specify the transaction needed */

			scsi_action.cmdAddress		= scsi_cmnd_blk;
			scsi_action.cmdLength 		= 6;
			scsi_action.dataAddress 	= (UTINY *)0;
			scsi_action.dataDirection 	= READ;
			scsi_action.dataLength		= 0;
			scsi_action.addLengthByte	= 0;
			break;
		default:
			return_status.errorCode = EXB_ILLEGAL_REQUEST;
			return (return_status);
	}

	/* Send the Scsi Command to the Tape Drive */
	debug_status = scsiIoctl (scsi_phys_dev, 
					FIO_SCSI_COMMAND,
					(int) &scsi_action);

	if (	(debug_status == OK) 
		&& (scsi_action.statusByte == SCSI_STATUS_GOOD)	)
	{

		/* return EXB_NO_ERROR is SCSI_STATUS_GOOD */

		return (return_status);
	}

	/*logMsg ("Sent scsiIoctl command, status = %x \n", debug_status);*/

	if (debug_status != OK) 
	{
		logMsg ("exbControl: scsiIoctl returned => %x \n", debug_status);
		error_code = errnoGet ();
		switch (error_code)
		{
			case S_scsiLib_DEV_NOT_READY:
				if (SCSI_DBG)
				{
					logMsg ("exbControl: error is DEV_NOT_READY\n");
				}
				return_status.errorCode = EXB_NOT_READY;
				break;
			case S_scsiLib_WRITE_PROTECTED:
				if (SCSI_DBG)
				{
					logMsg ("exbControl: error is WRITE_PROTECTED\n");
				}
				return_status.errorCode = EXB_DATA_PROTECT;
				break;
			case S_scsiLib_MEDIUM_ERROR:
				if (SCSI_DBG)
				{
					logMsg ("exbControl: error is EXB_MEDIUM_ERROR\n");
				}
				return_status.errorCode = EXB_MEDIUM_ERROR;
				break;
			case S_scsiLib_HARDWARE_ERROR:
				if (SCSI_DBG)
				{
					logMsg ("exbControl: error is EXB_HARDWARE_ERROR\n");
				}
				return_status.errorCode = EXB_HARDWARE_ERROR;
				break;
			case S_scsiLib_ILLEGAL_REQUEST:
				if (SCSI_DBG)
				{
					logMsg ("exbControl: error is EXB_ILLEGAL_REQUEST\n");
				}
				return_status.errorCode = EXB_ILLEGAL_REQUEST;
				break;
			case S_scsiLib_BLANK_CHECK:
				if (SCSI_DBG)
				{
					logMsg ("exbControl: error is EXB_BLANK_CHECK\n");
				}
				return_status.errorCode = EXB_BLANK_CHECK; 
				break;
			case S_scsiLib_VOLUME_OVERFLOW:
				if (SCSI_DBG)
				{
				logMsg ("exbControl: error is EXB_VOLUME_OVERFLOW\n");
				}
				return_status.errorCode = EXB_VOLUME_OVERFLOW;
				break;
			case S_scsiLib_SELECT_TIMEOUT:
				if (SCSI_DBG)
				{
					logMsg ("exbControl: error is SELECT_TIMEOUT\n");
				}
				return_status.errorCode = EXB_BUSY;
				break;
			case S_scsiLib_UNIT_ATTENTION:
				if (SCSI_DBG)
				{
					logMsg ("exbControl: error is EXB_UNIT_ATTENTION\n");
				}
				return_status.errorCode = EXB_UNIT_ATTENTION;
				break;
			case S_scsiLib_ABORTED_COMMAND:
				if (SCSI_DBG)
				{
					logMsg ("exbControl: error is EXB_ABORTED_COMMAND\n");
				}
				return_status.errorCode = EXB_ABORTED_COMMAND;
				break;
			default:
				if (SCSI_DBG)
				{
					logMsg ("exbControl: error is EXB_NO_SENSE\n");
				}
				return_status.errorCode = EXB_NO_SENSE;
		}/* end switch */
		return (return_status);
	}

	switch (scsi_action.statusByte)
	{
		case SCSI_STATUS_BUSY:
			return_status.errorCode = EXB_BUSY;
			return (return_status);
			break;
		case SCSI_STATUS_CHECK_CONDITION:
			return (exbRequestSense (scsi_phys_dev,
							(EXB_SENSE_DATA*) NULL));
			break;
		case SCSI_STATUS_MASK:
			logMsg ("SCSI_STATUS_MASK\n");
			break;
		case SCSI_STATUS_CONDITION_MET:
			logMsg ("SCSI_STATUS_CONDITION_MET\n");
			break;
		case SCSI_STATUS_INTMED_GOOD:
			logMsg ("SCSI_STATUS_INTMED_GOOD\n");
			break;
		case SCSI_STATUS_INTMED_COND_MET:
			logMsg ("SCSI_STATUS_INTMED_COND_MET\n");
			break;
		case SCSI_STATUS_RESERV_CONFLICT:
			logMsg ("SCSI_STATUS_RESERV_CONFLICT\n");
			break;
		case SCSI_STATUS_QUEUE_FULL:
			logMsg ("SCSI_STATUS_QUEUE_FULL\n");
			break;
		default:
			logMsg ("SCSI_STATUS UNKNOWN => %x \n", scsi_action.statusByte);
			break;
	}

	return_status.errorCode = EXB_NO_SENSE;
	return (return_status);

}



/*******************************************************************************
*
* 
*
*-------------------------------------------------------------------------------
*
* Function:	static void	exbStatusShow (status)
*
*-------------------------------------------------------------------------------
*
* Description:
*
*-------------------------------------------------------------------------------
*
* Returns:	
*
*******************************************************************************/


void	exbStatusShow (EXB_STATUS status)
{
	if (status.senseDataValid)
	{
		if (status.tapeNotPresent = FALSE)
		{
			logMsg ("No tape in tape drive\n");
		}

		if (status.writeProtect)
		{
			logMsg ("Tape is write protected\n");
		}
		else
		{
			logMsg ("Tape is not write protected\n");
		}

		if (status.endOfMedia)
		{
			logMsg ("End of Media\n");
		}

		if (status.bot)
		{
			logMsg ("Beginning of Tape\n");
		}

		if (status.peot)
		{
			logMsg ("Physical end of Tape\n");
		}

		if (status.filemark)
		{
			logMsg ("Filemark\n");
		}

		if (status.ili)
		{
			logMsg ("illegal length on last command\n");
		}
	}/* end if */
	
	switch (status.errorCode)
	{ 
		case EXB_NO_ERROR:
			logMsg ("No error on last command\n");
			break;
		case EXB_BUSY:
			logMsg ("Scsi bus busy error on last command\n");
			break;
		case EXB_NOT_READY:
			logMsg ("Tape drive busy error on last command\n");
			break;
		case EXB_MEDIUM_ERROR:
			logMsg ("Medium error on last command\n");
			break;
		case EXB_HARDWARE_ERROR:
			logMsg ("Hardware error on last command\n");
			break;
		case EXB_ILLEGAL_REQUEST:
			logMsg ("Illegal Request error on last command\n");
			break;
		case EXB_UNIT_ATTENTION:
			logMsg ("Unit Attention error on last command\n");
			logMsg ("Media may have been changed by the operator\n");
			break;
		case EXB_DATA_PROTECT:
			logMsg ("Data protect error on last command\n");
			break;
		case EXB_BLANK_CHECK:
			logMsg ("Blank check error on last command\n");
			break;
		case EXB_EXABYTE_UNIQUE:
			logMsg ("Exabyte unique error on last command\n");
			break;
		case EXB_ABORTED_COMMAND:
			logMsg ("Aborted command error on last command\n");
			break;
		case EXB_VOLUME_OVERFLOW:
			logMsg ("Volume overflow error on last command\n");
			break;
		case EXB_NO_SENSE:
			logMsg ("No_Sense error on last command\n");
			break;
		default:
			logMsg ("Not legal value for status.errorCode\n");
	}/* end switch */
 }/* end exbStatusShow */