//
//	DriveSCSI_Class - subclass of SCSI2_Class for disk drive devices
//
//	Copyright (C) 1994 by Christopher Wolf.
//
//	This library is free software; you can redistribute it and/or
//	modify it under the terms of the GNU Library General Public
//	License as published by the Free Software Foundation; either
//	version 2 of the License, or (at your option) any later version.
//
//	This library 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
//	Library General Public License for more details.
//
//	You should have received a copy of the GNU Library General Public
//	License along with this library; if not, write to the Free
//	Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
//	Bug-reports, comments and questions should be directed to:
//		Christopher Wolf <chris@alchemy.geo.cornell.edu>
//
//	Portions of this code and documentation are derived from an 
//	earlier work (SCSI Inquirer) Copyright (C) 1990, 1991, 1992 by 
//	Jiro Nakamura and used with permission.
//

//
// 	For best results view this file with a tab size of 4 and
//	a display width of 132 columns or wider.
//

//	file version information
#define RCSDSM "$Id: DriveSCSI.m,v 0.55 94/12/06 03:57:22 chris Exp $"

//	generic Unix headers
#import <stdio.h>
#import <libc.h>

//	NeXTSTEP specific headers
#import <appkit/appkit.h>

//	SCSI2 class specific headers 
#import "DriveSCSI.h"
#import "errio.h"

// string constants
const char RCSdsm[] = RCSDSM;
const char RCSdsh[] = RCSDSH;

@implementation DriveSCSI


//	----------------------------------------------------------------------
//
//	NAME
//		isDevice
//
//	RETURNS
//		(BOOL)			YES if sub-class can handle device, NO otherwise. 
//
//	EXPLANATION
//		It is the sub-classes responsibility to implement this method. 
//		The method should return YES if the subclass of SCSI can handle
//		the device at the current target, NO otherwise.  
//
//	SEE ALSO
//		findDevice (SCSI), findDevice: (SCSI)
//	
//	----------------------------------------------------------------------
	
- (BOOL) isDevice
	{

	struct inquiry_reply ibuffer;
	BOOL result;
	PrintFunction oldPf = dprintf;
	
	dprintf = printfToNull;
	result = ((![self inquiry: &ibuffer]) && (ibuffer.ir_devicetype == DEVTYPE_DISK));
	dprintf = oldPf;
		
	return result;
	
	}


//	----------------------------------------------------------------------
//
//	NAME
//		seekExtended:
//
//	RETURNS
//		(int) 0							Seek succeeded
//		(int) -1						Seek failed  
//
//	PARAMETERS
//		(u_int) logicalBlockAddress		logical block address to seek to 
//
//	EXPLANATION
//		This implements the SCSI command ªSeek Extendedº. It must be 
//		passed a long integer logicalBlockAddress which to seek to.  
//		This method returns when the device has finished seeking.
//		The method returns zero for success, non-zero otherwise.
//	
//	----------------------------------------------------------------------

- (int) seekExtended: (u_int) logicalBlockAddress
	{	

	struct cdb10 *cdbp = &sr.sr_cdb.cdb10;
	BOOL wasOpen = isOpen;
	int result = -1;
	
	if (wasOpen || ![self openSCSI])
		{

		[SCSI clearCommandBlock: (cdb *) cdbp];
		
		cdbp->c10_opcode 	= C10OP_SEEKEXTENDED;
		cdbp->c10_lun   	= lun;
		writeBigEndianLong(logicalBlockAddress, &cdbp->c10_lba_BE32);
	
		sr.sr_dma_dir	= SR_DMA_RD;
		sr.sr_addr		= NULL;
		sr.sr_dma_max   = 0;
		sr.sr_ioto		= timeout;

		result = [self performRequest];
		
		}
	
	if (!wasOpen && [self closeSCSI])
		{
		result = -1;
		}

	return(result);		

	}


//	----------------------------------------------------------------------
//
//	NAME
//		readExtended: lba: numBytes: numBlocks:
//
//	RETURNS
//		(int) 0					read succeeded
//		(int) -1				read failed  
//
//	PARAMETERS
//		(u_int) lba				logical block address to start read from
//		(char *) destBuffer 	pointer to buffer to place data in	
//		(u_int)	numBytes		size of read in bytes
//		(u_int) numBlock		size of read in blocks
//
//	EXPLANATION
//		This implements the SCSI command ªRead Extendedº.  It reads
//		data starting from the specified logical block address into the
//		specified buffer.   The size of the read must be specified in
//		BOTH number of bytes and number of blocks to be read.  The
//		method returns after the read has been completed.  The method
//		returns zero on success, non-zero otherwise.
//	
//	----------------------------------------------------------------------

- (int) readExtended: (char *)destBuffer numBytes: (u_int)numBytes numBlocks: (u_int) numBlocks lba: (u_int)lba 
	{
		
	struct cdb10 *cdbp = &sr.sr_cdb.cdb10;
	BOOL wasOpen = isOpen;
	int result = -1;
	
	if (wasOpen || ![self openSCSI])
		{

		[SCSI clearCommandBlock: (cdb *) cdbp];

		cdbp->c10_opcode = C10OP_READEXTENDED;
		cdbp->c10_lun    = lun;
		
		writeBigEndianLong(lba, &cdbp->c10_lba_BE32);
		writeBigEndianShort(numBlocks, &cdbp->c10_len_BE16);
		
		sr.sr_dma_dir	= SR_DMA_RD;
		sr.sr_addr		= destBuffer;
		sr.sr_dma_max   = numBytes;
		sr.sr_ioto		= timeout;

		result = [self performRequest];
	
		}
			
	if (!wasOpen && [self closeSCSI])
		{
		result = -1;
		}

	return(result);		

	}



//	----------------------------------------------------------------------
//
//	NAME
//		read:numBytes:numBlocks:lba:
//
//	RETURNS
//		(int) 					0 = success, -1 = failure
//
//	PARAMETERS
//		(u_int) lba				logical block address to start read from
//		(char *) destBuffer 	pointer to buffer to place data in	
//		(u_int)	numBytes		size of read in bytes
//		(u_int) numBlock		size of read in blocks
//
//	EXPLANATION
//		This implements the SCSI command ªRead (6)º.  It reads
//		data starting from the specified logical block address into the
//		specified buffer.   The size of the read must be specified in
//		BOTH number of bytes and number of blocks to be read.  The
//		method returns after the read has been completed.  The method
//		returns zero on success, non-zero otherwise.
//	
//	----------------------------------------------------------------------

- (int) read: (char *)destBuffer numBytes: (u_int)numBytes numBlocks: (u_int) numBlocks lba: (u_int)lba 
	{
		
	struct cdb6 *cdbp = &sr.sr_cdb.cdb6;
	BOOL wasOpen = isOpen;
	int result = -1;
	
	if (wasOpen || ![self openSCSI])
		{

		[SCSI clearCommandBlock: (cdb *) cdbp];

		cdbp->c6_opcode  = C6OP_READ;
		writeBigEndianValue((lun<<21) + (lba & 0x1FFFFF), &cdbp->c6_lunlba_BE24, 24);
		cdbp->c6_len	= numBlocks;
		
		sr.sr_dma_dir	= SR_DMA_RD;
		sr.sr_addr		= destBuffer;
		sr.sr_dma_max   = numBytes;
		sr.sr_ioto		= timeout;

		result = [self performRequest];
	
		}
			
	if (!wasOpen && [self closeSCSI])
		{
		result = -1;
		}

	return(result);		

	}



//	----------------------------------------------------------------------
//
//	NAME
//		formatUnitInterleave clearGList: cert: immed:
//
//	RETURNS
//		(int) 0					format succeeded
//		(int) -1				format failed  
//
//	EXPLANATION
//		This implements the SCSI command ªFormat Unitº.  It performs
//		a low level format of the device.  ALL DATA WILL BE ERASED!
//		Use this command with caution.  This command will operate in
//		immediate mode and return as soon as the device accepts the
//		format unit command unless setImmediate: FALSE (from SCSI.m)
//		is used in which case the command will not return until
//		the format is complete.  Note that if you setImmediate: FALSE
//		you will probably have to adjust the timeout delay using
//		the setTimeout: method from SCSI.m since the default timeout
//		is too short for non-immediate mode format operations.
//
//	SEE ALSO
//		setImmediate: (SCSI.m), setTimeout: (SCSI.m)
//	
//	----------------------------------------------------------------------

- (int) formatUnitInterleave: (u_int)interleave clearGList: (BOOL)clearGList cert: (BOOL)mediaCert 
	{
	
	struct cdb6_format_unit *cdbp = &sr.sr_cdb.cdb6_format_unit;
	struct defect_list_header *dlh = malloc(sizeof (struct defect_list_header));
	
	BOOL wasOpen = isOpen;
	int result = -1;
	
	if (wasOpen || ![self openSCSI])
		{
			
		[SCSI clearCommandBlock: (cdb *) cdbp];
		bzero(dlh, sizeof(struct defect_list_header));
			
		cdbp->fu_opcode  = C6OP_FORMAT;
		cdbp->fu_lun     = lun;
		cdbp->fu_fmtdata = 0;
		cdbp->fu_cmplist = clearGList;
		writeBigEndianShort(interleave,&cdbp->fu_interleave_BE16);
		
		dlh->dlh_fov   = 1;
		dlh->dlh_dcrt  = !mediaCert;
		dlh->dlh_dsp   = 1;
		dlh->dlh_immed = immed;
			
		sr.sr_dma_dir	= SR_DMA_WR;
		sr.sr_addr		= (char *)dlh;
		sr.sr_dma_max   = sizeof(struct defect_list_header);
		sr.sr_ioto		= timeout;	

		result = [self performRequest];

		}
		
	if (!wasOpen && [self closeSCSI])
		{
		result = -1;
		}

	free(dlh);
	return(result);		

	}


@end
