This is SCSI.m in view mode; [Download] [Up]
//
// SCSI2_Class - provides architecture independendent low-level
// SCSI device access.
//
// Copyrigzht (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 RCSSM "$Id: SCSI.m,v 0.76 94/09/21 14:16:46 chris Exp Locker: chris $"
// generic Unix headers
#import <libc.h>
#import <stdio.h>
#import <fcntl.h>
#import <stdio.h>
#import <objc/typedstream.h>
// NeXTSTEP headers
#import <appkit/nextstd.h>
#import <appkit/Application.h>
#import <appkit/Panel.h>
// SCSI2 class specific headers
#import "SCSI.h"
#import "VersionNumber.h"
#import "scsi2reg.h"
// string constants
const char RCSsm[] = RCSSM;
const char RCSsh[] = RCSSH;
const char RCSsrh[] = RCSSRH;
// **********************************************************************
//
// Functions for reading and writing big endian values of
// various bit sizes on both big (Motorola) and little (Intel) endian
// architecture machines.
//
// **********************************************************************
// ----------------------------------------------------------------------
//
// NAME
// readBigEndianLongFrom(void *address)
//
// PARAMETERS
// void *address pointer to MSB of 32 bit big-endian int
//
// RETURNS
// (int) 32 bit int value in native byte order
//
// EXPLANATION
// Reads a long-word (4 bytes) stored in big-endian (MSB first)
// order starting from the specified address and returns the value
// as a 32 bit int in native byte order.
//
// SEE ALSO
// readBigEndianShort(), readBigEndianValue(),
// writeBigEndianLong(), writeBigEndianShort(),
// writeBigEndianValue():
//
// ----------------------------------------------------------------------
int readBigEndianLong(void *address)
{
return readBigEndianValue(address,32);
}
// ----------------------------------------------------------------------
//
// NAME
// readBigEndianShortFrom(void *address)
//
// PARAMETERS
// void *address pointer to MSB of 16 bit big-endian int
//
// RETURNS
// (int) 32 bit int value in native byte order
//
// EXPLANATION
// Reads a short-word (2 bytes) stored in big-endian (MSB first)
// order starting from the specified address and returns the value
// as a 32 bit int in native byte order.
//
// SEE ALSO
// readBigEndianLong(), readBigEndianValue(),
// writeBigEndianLong(), writeBigEndianShort(),
// writeBigEndianValue()
//
// ----------------------------------------------------------------------
int readBigEndianShort(void *address)
{
return readBigEndianValue(address, 16);
}
// ----------------------------------------------------------------------
//
// NAME
// readBigEndianValue(void *address, int numBits)
//
// PARAMETERS
// void *address pointer to MSB of big-endian int
// int numBits number of bits in big-endian int (1-32)
//
// RETURNS
// (int) 32 bit int value in native byte order
//
// EXPLANATION
// Reads an integer consisting of the specified number of bits
// stored in big-endian (MSB first) format starting from the
// specified address. The number of bits can be from 1-32.
// The value is returned as a 32 bit int in native byte order.
//
// SEE ALSO
// readBigEndianShort(), readBigEndianLong(),
// writeBigEndianLong(), writeBigEndianShort(),
// writeBigEndianValue():
//
// ----------------------------------------------------------------------
int readBigEndianValue(void *address, int numBits)
{
#ifdef __BIG_ENDIAN__
return (*((int *)address))>>(32-numBits);
#else
return (htonl(*((int *)address)))>>(32-numBits);
#endif
}
// ----------------------------------------------------------------------
//
// NAME
// writeBigEndianLong(int value, void *address)
//
// PARAMETERS
// int value 32 bit int value in native byte order
// void *address pointer to MSB of 32-bit destination
//
// EXPLANATION
// Writes a value specified as a 32 bit long-word in native byte
// order as a 32 bit big-endian value at the specified destination
// address.
//
// SEE ALSO
// writeBigEndianShort(), writeBigEndianValue(),
// readBigEndianShort(), readBigEndianLong(),
// readBigEndianValue()
//
// ----------------------------------------------------------------------
void writeBigEndianLong(int value, void *address)
{
writeBigEndianValue(value, address, 32);
}
// ----------------------------------------------------------------------
//
// NAME
// writeBigEndianShort(int value, void *address)
//
// PARAMETERS
// int value 32 bit int value in native byte order
// void *address pointer to MSB of 16-bit destination
//
// EXPLANATION
// Writes a value specified as a 32 bit long-word in native byte
// order as a 16 bit big-endian value at the specified destination
// address.
//
// SEE ALSO
// writeBigEndianLong(), writeBigEndianValue(),
// readBigEndianShort(), readBigEndianLong(),
// readBigEndianValue():
//
// ----------------------------------------------------------------------
void writeBigEndianShort(int value, void *address)
{
writeBigEndianValue(value, address, 16);
}
// ----------------------------------------------------------------------
//
// NAME
// writeBigEndianValue(int value, void *address, int numbits)
//
// PARAMETERS
// int *value 32 bit int value in native byte order
// void *address pointer to MSB of destination
// int numBits number of bits for destination value
//
// EXPLANATION
// Writes a value specified as a 32 bit long-word in native byte
// order as a big-endian value with the specified number of bits
// at the specified destination address. Number of bits specified
// can be between 1 and 32.
//
// ----------------------------------------------------------------------
void writeBigEndianValue(int value, void *address, int numBits)
{
#ifdef __BIG_ENDIAN
*((int *)address) &= (~(0xFFFF<<(32-numBits)));
*((int *)address) |= (value<<(32-numBits));
#else
*((int *)address) &= htonl(~(0xFFFF<<(32-numBits)));
*((int *)address) |= htonl(value<<(32-numBits));
#endif
}
@implementation SCSI
// **********************************************************************
//
// Misc. Class (Factory) Methods
//
// **********************************************************************
// ----------------------------------------------------------------------
//
// NAME
// new
//
// RETURNS
// self pointer to new instance of SCSI class
//
// INSTANCE VARIABLES EFFECTED
// isOpen FALSE
//
// EXPLANATION
// Generates a new instance of the SCSI class.
//
// ----------------------------------------------------------------------
+new
{
self = [super new];
target = -1;
lun = 0;
dev_name = "/dev/sg";
isOpen = FALSE;
return self;
}
// ----------------------------------------------------------------------
//
// NAME
// clearCommandBlock:
//
// PARAMETERS
// (union cdb *) cdb pointer to command data block to zero
//
// EXPLANATION
// Clears the specifed command data block in preparation for issuing
// a SCSI command.
//
// ----------------------------------------------------------------------
+ (void) clearCommandBlock: (union cdb *) cdb
{
bzero((char *)cdb,sizeof(union cdb));
return;
}
// **********************************************************************
//
// Instance methods for archiving.
//
// **********************************************************************
// ----------------------------------------------------------------------
//
// NAME
// read
//
// RETURNS
// self
//
// EXPLANATION
// Retrieves instance variables associated with this class during
// an archive read: operation.
//
// SEE ALSO
// write:
//
// ----------------------------------------------------------------------
- read: (NXTypedStream *) stream
{
[super read: stream];
NXReadTypes( stream, "ii**", &target, &lun, &dev_name, &errorString);
return self;
}
// ----------------------------------------------------------------------
//
// NAME
// write
//
// RETURNS
// self
//
// EXPLANATION
// Preserves instance variables associated with this class during
// an archive write: operation.
//
// SEE ALSO
// read:
//
// ----------------------------------------------------------------------
- write: (NXTypedStream *) stream
{
[super write: stream];
NXWriteTypes( stream, "ii**", &target, &lun, &dev_name, &errorString);
return self;
}
// **********************************************************************
//
// Instance methods for opening, closing, and setting the target/lun
// for the SCSI device.
//
// **********************************************************************
// ----------------------------------------------------------------------
//
// NAME
// openTarget: lun:
//
// PARAMETERS
// (int) trg target to select after opening device
// (int) lun lun to select after opening device
//
// RETURNS
// (int) 0 0 = success, -1 failure
//
// EXPLANATION
// Opens the SCSI generic driver and selects the designated
// target device and LUN. The SCSI driver should be kept
// open for as short a time as possible. Pre-packaged command
// methods implemented in this class automatically open and close
// the SCSI driver, if necessary. (However, if you are going
// to rely on the auto open/close feature of pre-packaged commands
// you must use setTarget:lun: to specify the target before issuing
// the pre-packaged command.)
//
// SEE ALSO:
// open, close, setTarget: lun:
//
// ----------------------------------------------------------------------
- (int) openTarget: (int) trg lun: (int) ln
{
int result;
if (!(result = [self setTarget: trg lun: ln]))
{
result = [self open];
}
return result;
}
// ----------------------------------------------------------------------
//
// NAME
// open
//
// RETURNS
// (int) 0 = success, -1 = failure
//
// INSTANCE VARIABLES EFFECTED
// isOpen TRUE if open succeeded, FALSE if failed
// errorString ASCII status message
//
// EXPLANATION
// Opens the SCSI generic driver. The SCSI driver should be kept
// open for as short a time as possible. Pre-packaged command
// methods implemented in this class automatically open and close
// the SCSI driver. It is an error to attempt to open the SCSI
// device if it is already open. Remember that you must call
// setTarget:lun: either before or after the open method, but
// definitely BEFORE any SCSI commands are issued.
//
// SEE ALSO
// openAt: lun:, close, isOpen
//
// ----------------------------------------------------------------------
- (int) open
{
int result = -1;
char tmpBuf[20];
unsigned int tmp;
if (isOpen == TRUE)
{
sprintf(errorString, "SCSI device already open!");
}
else
{
for(tmp = 0; ; tmp ++)
{
sprintf(tmpBuf, "%s%1X", dev_name, tmp) ;
if (access(tmpBuf, F_OK))
{
break;
}
if( (!access(tmpBuf, R_OK | W_OK))
&& ((fd = open(tmpBuf, O_RDWR)) >= 0) )
{
isOpen = TRUE;
result = 0;
}
}
if (result)
{
sprintf(errorString,
"Could not open SCSI generic driver <%sX>.\n"
"File descriptor = 0x%X\n",
tmpBuf, (unsigned int) fd);
perror("open");
}
else
{
if (result = [self setTarget: target lun: lun])
{
[self close];
}
}
}
return result;
}
// ----------------------------------------------------------------------
//
// NAME
// setTarget: lun:
//
// PARAMETERS
// (int) trg target to select
// (int) lun lun to select
//
// RETURNS
// (int) 0 = Success, -1 = Error
//
// SETS
// isOpen TRUE if open succeeded, FALSE if failed
// errorString ASCII status message
// target selected SCSI target
// lun selected SCSI lun
//
// EXPLANATION
// Selects the indicated target and lun for subsequent SCSI commands.
//
// SEE ALSO
// openTarget: lun:
//
// ----------------------------------------------------------------------
- (int) setTarget: (int) trg lun: (int) ln
{
int result = 0;
if ( (trg < 0) || (trg > 7) || (lun < 0) || (lun > 7) )
{
sprintf(errorString,
"Missing or invalid target (%d) or lun (%d) specification!\n", target,lun);
result = -1;
}
else
{
sa.sa_target = target = trg;
sa.sa_lun = lun = ln;
if (isOpen)
{
if (ioctl(fd,SGIOCSTL,&sa) < 0)
{
sprintf(errorString,
"ioctl(SGIOCSTL): Error setting target %d lun %d (errno = %d)\n",
target, lun, errno);
result = -1;
}
}
}
return result;
}
// ----------------------------------------------------------------------
//
// NAME
// close
//
// RETURNS
// (int) 0 Close succeeded
// (int) -1 Error (ASCII error message in errorString)
//
// INSTANCE VARIABLES EFFECTED
// isOpen TRUE if open succeeded, FALSE if failed
// errorString ASCII status message
//
// EXPLANATION
// Closes the SCSI generic driver. The SCSI driver should be kept
// open for as short a time as possible. Pre-packaged commands
// automatically open and close the SCSI driver. It is an error
// to close the SCSI device if it is not open.
//
// SEE ALSO
// openAt: lun:, isOpen
//
// ----------------------------------------------------------------------
- (int) close
{
if (isOpen == FALSE)
{
sprintf(errorString, "SCSI device already closed!");
perror("close");
return -1;
}
if (close(fd) < 0)
{
sprintf(errorString,
"Could not close %sX - fd = %XH\nerrno = %d\n",
dev_name, (unsigned int)fd ,errno);
perror("close");
return -1;
}
isOpen = FALSE;
return 0;
}
// **********************************************************************
//
// Instance methods for reporting status and error information.
//
// **********************************************************************
// ----------------------------------------------------------------------
//
// NAME
// isOpen
//
// RETURNS
// (BOOL) YES = Device Open, NO = Device Closed
//
// EXPLANATION
// This returns YES if the SCSI device is open and accessible,
// NO otherwise.
//
// SEE ALSO
// open, close, openAt: lun:
//
// ----------------------------------------------------------------------
- (BOOL) isOpen
{
return isOpen;
}
// ----------------------------------------------------------------------
//
// NAME
// errorString
//
// RETURNS
// (char *) Pointer to ASCII error string.
//
// EXPLANATION
// This returns the class variable errorString which holds
// information (in ASCII format) about the last error. It is the
// responsibility of subclasses of SCSI to put the proper
// information in errorString when an error occurs. The SCSI
// class will not return errors in errorString for you. To access
// bus level errors use the sr return codes or use returnError
// to interpret the sr return codes for you.
//
// SEE ALSO
// statusReq
//
// ----------------------------------------------------------------------
- (char *) errorString
{
return &errorString[0];
}
// ----------------------------------------------------------------------
//
// NAME
// statusReq
//
// RETURNS
// (struct scsi_req *) Pointer to ASCII error string.
//
// EXPLANATION
// This returns the status of the last SCSI request as a
// pointer to a structure defined in <nextdev/scsireg.h>.
//
// SEE ALSO
// errorString
//
// ----------------------------------------------------------------------
- (struct scsi_req *) statusReq
{
return &sr;
}
// ----------------------------------------------------------------------
//
// NAME
// returnDriverStatus: scsiStatus: andExtendedSense:
//
// PARAMETERS
// (char **)driverStatus pointer to pointer to char which
// will be set to point to ASCII string
// containing driver status information.
// (char **)scsiStatus pointer to pointer to char which
// will be set to point to ASCII string
// containing SCSI status information.
// (char **)andExtendedSense pointer to pointer to char which
// will be set to point to ASCII string
// containing extended send information.
//
// RETURNS
// (char *) Pointer to composite string containing all the
// info in the three individual strings
// driverStatus, scsiStatus and andExtendedSense,
// concatenated into one long message.
//
// EXPLANATION
// This method places interpreted versions of the sr error codes,
// including the extended sense error codes if valid, into the
// character strings that were given to it. The format of the
// returned strings is not strict and should be only used for
// debugging purposes. The actual error codes are more or less
// taken from <nextdev/scsireg.h>. This method is provided as an
// easy way to provide debugging information.
//
// SEE ALSO
// returnError
//
// ----------------------------------------------------------------------
- (char *) returnDriverStatus: (char **) driverStatus scsiStatus: (char **) scsiStatus
andExtendedSense: (char **) esense
{
static char stringBuffer[512];
#ifdef DEBUG
fprintf(stderr,"Io_Status = %02Xh, Status = %02Xh.\n",sr.sr_io_status,sr.sr_scsi_status);
#endif
switch(sr.sr_io_status)
{
case SR_IOST_GOOD:
{
*driverStatus = "Command successful";
break;
}
case SR_IOST_SELTO:
{
*driverStatus = "Selection timeout";
break;
}
case SR_IOST_CHKSV:
{
*driverStatus = "Check target status and extended sense";
break;
}
case SR_IOST_CHKSNV:
{
*driverStatus = "Check target status";
break;
}
case SR_IOST_DMAOR:
{
*driverStatus = "Target attempted to move more than allocated amount of data bytes";
break;
}
case SR_IOST_IOTO:
{
*driverStatus = "Command timed out";
break;
}
case SR_IOST_BV:
{
*driverStatus = "SCSI Bus violation";
break;
}
case SR_IOST_CMDREJ:
{
*driverStatus = "Command rejected by driver";
break;
}
case SR_IOST_MEMALL:
{
*driverStatus = "Memory allocation failure";
break;
}
case SR_IOST_MEMF:
{
*driverStatus = "Memory fault";
break;
}
case SR_IOST_PERM:
{
*driverStatus = "Permission failure (not super user)";
break;
}
case SR_IOST_NOPEN:
{
*driverStatus = "Device not open";
break;
}
case SR_IOST_TABT:
{
*driverStatus = "Target aborted command";
break;
}
case ST_IOST_BADST:
{
*driverStatus = "Bad SCSI status byte (other than check status)";
break;
}
case ST_IOST_INT:
{
*driverStatus = "Internal driver error";
break;
}
case SR_IOST_BCOUNT:
{
*driverStatus = "Unexpected byte count seen on SCSI bus";
break;
}
case SR_IOST_VOLNA:
{
*driverStatus = "Desired volume not available";
break;
}
case SR_IOST_WP:
{
*driverStatus = "Media Write Protected";
break;
}
default:
{
*driverStatus = "Unrecognizable status";
break;
}
}
switch(sr.sr_scsi_status & STAT_VALIDMASK)
{
case STAT_GOOD:
{
if(sr.sr_io_status == SR_IOST_GOOD)
{
*scsiStatus = "GOOD - Command successful";
}
else
{
*scsiStatus = "Not Applicable (Command not executed)";
}
break;
}
case STAT_CHECK:
{
*scsiStatus = "CHECK CONDITION - Abnormal condition occured";
break;
}
case STAT_CONDMET:
{
*scsiStatus = "CONDITION MET / GOOD";
break;
}
case STAT_BUSY:
{
*scsiStatus = "BUSY - Target Busy";
break;
}
case STAT_INTMGOOD:
{
*scsiStatus = "INTERMEDIATE / GOOD";
break;
}
case STAT_INTMCONDMET:
{
*scsiStatus = "INTERMEDIATE / CONDITION MET / GOOD";
break;
}
case STAT_RESERVED:
{
*scsiStatus = "RESERVATION CONFLICT";
break;
}
case STAT_TERMINATED:
{
*scsiStatus = "COMMAND TERMINATED";
break;
}
case STAT_QUEUEFULL:
{
*scsiStatus = "QUEUE FULL";
break;
}
default:
{
*scsiStatus = "Unrecognizable status";
}
}
switch( sr.sr_esense.er_sensekey )
{
case SENSE_NOSENSE:
{
*esense = "No error to report";
break;
}
case SENSE_RECOVERED:
{
*esense = "Recovered from error";
break;
}
case SENSE_NOTREADY:
{
*esense = "Target not ready";
break;
}
case SENSE_MEDIA:
{
*esense = "Media flaw";
break;
}
case SENSE_HARDWARE:
{
*esense = "Hardware failure";
break;
}
case SENSE_ILLEGALREQUEST:
{
*esense = "Illegal request";
break;
}
case SENSE_UNITATTENTION:
{
*esense = "Drive attention";
break;
}
case SENSE_DATAPROTECT:
{
*esense = "Drive access protected";
break;
}
case SENSE_ABORTEDCOMMAND:
{
*esense = "Target aborted command";
break;
}
case SENSE_VOLUMEOVERFLOW:
{
*esense = "End of media, some data not transfered";
break;
}
case SENSE_MISCOMPARE:
{
*esense = "Source/media data mismatch";
break;
}
default:
{
*esense = "Unrecognizable sense key";
break;
}
}
if (SR_IOST_CHKSV == sr.sr_io_status)
{
sprintf(stringBuffer,
"\tDriver Status: \t%s\n"
"\tTarget Status: \t%s\n"
"\tExtended Sense: \t%s\n",
*driverStatus,
*scsiStatus,
*esense);
}
else
{
sprintf(stringBuffer,
"\tDriver Status: \t%s\n"
"\tTarget Status:\t%s\n",
*driverStatus,
*scsiStatus);
*esense = NULL;
}
#ifdef DEBUG
fprintf(stderr, "SCSI Completion string buf = %s\n", stringBuffer);
#endif
return stringBuffer;
}
// ----------------------------------------------------------------------
//
// NAME
// returnError
//
// RETURNS
// (char *) Pointer to a composite string containing
// concatenated driverStatus, scsiStatus and
// andExtendedSense info in one place.
//
// EXPLANATION
// This method is just a wrapper for the above defined method
// returnDriverStatus: scsiStatus: andExtendedSense method
// for cases where you are only interested in the concatenated
// error string and don't want it broken down into individual
// components.
//
// SEE ALSO
// returnDriverStatus: scsiStatus: andExtendedSense:
//
// ----------------------------------------------------------------------
- (char *) returnError: sender
{
char *driverStatus, *scsiStatus, *esense;
return [self returnDriverStatus: &driverStatus scsiStatus: &scsiStatus andExtendedSense: &esense];
}
// **********************************************************************
//
// Instance methods for determining which
// devices are supported by a sub-class of SCSI class.
//
// **********************************************************************
// ----------------------------------------------------------------------
//
// NAME
// findDevice:
//
// PARAMETERS
// (int) trg Target to start search from
//
// RETURNS
// (int) Target number of first SCSI device greater than
// or equal to the specified starting number that
// the sub-class can handle. Returns -1 if no
// appropriate device was able to be found.
//
// EXPLANATION
// This method starts at the specified target and sequentially steps
// through the rest of the targets calling the isDevice: method for
// each target. This method returns the number of the first target
// which responds TRUE to isDevice, or -1 if none responds TRUE.
// Remember that isDevice is over-ridden by sub-class implementations.
// This means that for instances of the SCSI super class findDevice:
// will return the number of the next valid SCSI target of any type;
// for instances of a specific sub-class findDevice: will return the
// number of the next device which that sub-class type recognizes.
//
// SEE ALSO
// findDevice, isDevice
//
// ----------------------------------------------------------------------
- (int) findDevice: (int) trg
{
int result = -1, oldTarg = target, oldLun = lun;
BOOL wasOpen = isOpen;
if (wasOpen || ![self openTarget: 0 lun: 0])
{
for(result = trg; result < 8; result++)
{
if( ![self setTarget: result lun: 0] && [self isDevice] )
{
break;
}
}
if (result == 8)
{
result = -1;
sprintf(errorString, "No target device found.");
}
if (oldTarg != -1)
{
[self setTarget: oldTarg lun: oldLun];
}
if (!wasOpen && [self close])
{
result = -1;
}
}
return(result);
}
// ----------------------------------------------------------------------
//
// NAME
// findDevice
//
// RETURNS
// (int) Target # of first SCSI device that the sub-class
// can handle.
//
// EXPLANATION
// This returns the target number of the first SCSI device that the
// subclass can handle. Implemented as a wrap for the method
// [self findDevice: 0]
//
// SEE ALSO
// findDevice, isDevice
//
// ----------------------------------------------------------------------
- (int) findDevice
{
return [self findDevice: 0];
}
// ----------------------------------------------------------------------
//
// NAME
// isDevice
//
// RETURNS
// (BOOL) YES if sub-class can handle device, NO otherwise.
//
// EXPLANATION
// It is the sub-classes responsibility to override this method.
// The method should return YES if the subclass of SCSI can handle
// the device at the current target, NO otherwise. The SCSI super-
// class returns YES as long as there is a device attached, no
// matter what type it is.
//
// SEE ALSO
// findDevice, findDevice:
//
// ----------------------------------------------------------------------
- (BOOL) isDevice
{
struct inquiry2_reply ibuffer;
if (![self inquiry: &ibuffer])
{
return YES;
}
return NO;
}
// **********************************************************************
//
// Instance methods for implementing commonly used SCSI commands.
//
// **********************************************************************
// ----------------------------------------------------------------------
//
// NAME
// inquiry:
//
// PARAMETERS
// (struct inquiry2_reply *) ibuffer Structure used to return
// inquiry data.
//
// RETURNS
// (int) 0 = success, -1 = error
//
// EXPLANATION
// This implements the mandatory SCSI command ªInquiryº (command
// group 0, code 0x12). It returns the inquiry data in
// the inquiry2_reply structure defined in "scsi2reg.h"
//
// ----------------------------------------------------------------------
- (int) inquiry: (struct inquiry2_reply *) ibuffer
{
int result = -1;
BOOL wasOpen = isOpen;
struct cdb_6 *cdbp = &sr.sr_cdb.cdb_c6;
if (wasOpen || ![self open])
{
[SCSI clearCommandBlock: (union cdb *) cdbp];
cdbp->c6_opcode = C6OP_INQUIRY;
cdbp->c6_lun = lun;
cdbp->c6_len = 36;
sr.sr_dma_dir = SR_DMA_RD;
sr.sr_addr = (char *) ibuffer;
sr.sr_dma_max = 36;
sr.sr_ioto = 10;
result = [self performRequest];
if (!wasOpen && [self close])
{
result = -1;
}
}
return(result);
}
// ----------------------------------------------------------------------
//
// NAME
// readCapacity
//
// PARAMETERS
// (struct capacity2_reply *) rbuffer Structure used to return
// capacity data.
//
// RETURNS
// (int) 0 = success, -1 = error
//
// EXPLANATION
// This implements the mandatory SCSI command ªRead Capacityº
// (command group 1, code 0x25) for direct-access devices. It
// returns read capacity data (logical unit capacity and block
// length) in rbuffer, which is a capacity2_reply structure
// defined in "SCSI2reg.h"
//
// ----------------------------------------------------------------------
- (int) readCapacity: (struct capacity2_reply *) rbuffer
{
int result = -1;
BOOL wasOpen = isOpen;
struct cdb2_10 *cdbp = (struct cdb2_10 *)&sr.sr_cdb.cdb_c10;
if (wasOpen || ![self open])
{
[SCSI clearCommandBlock: (union cdb *) cdbp];
cdbp->c10_opcode = C10OP_READCAPACITY;
cdbp->c10_lun = lun;
sr.sr_dma_dir = SR_DMA_RD;
sr.sr_addr = (char *) rbuffer;
sr.sr_dma_max = 8;
sr.sr_ioto = 10;
result = [self performRequest];
if (!wasOpen && [self close])
{
result = -1;
}
}
return(result);
}
// ----------------------------------------------------------------------
//
// NAME
// requestSense:
//
// PARAMETERS
// (struct esense2_reply *) buffer Structure used to return
// extended sense data.
//
// RETURNS
// (int) 0 = success, -1 = error
//
// EXPLANATION
// This implements the mandatory SCSI command ªRequest Senseº
// (command group 0, code 0x03). It returns the sense data in
// buffer, which is a esenseReply structure defined in
// <nextdev/scsireg.h>.
//
// ----------------------------------------------------------------------
- (int) requestSense: (struct esense2_reply *) ebuffer
{
int result = -1;
BOOL wasOpen = isOpen;
struct cdb_6 *cdbp = &sr.sr_cdb.cdb_c6;
if (wasOpen || ![self open])
{
[SCSI clearCommandBlock: (union cdb *) cdbp];
cdbp->c6_opcode = C6OP_REQSENSE;
cdbp->c6_lun = lun;
cdbp->c6_len = sizeof(*ebuffer);
sr.sr_dma_dir = SR_DMA_RD;
sr.sr_addr = (char *)ebuffer;
sr.sr_dma_max = sizeof(*ebuffer);
sr.sr_ioto = 10;
result = [self performRequest];
if (!wasOpen && [self close])
{
result = -1;
}
}
return(result);
}
// ----------------------------------------------------------------------
//
// NAME
// testUnitReady
//
// RETURNS
// (int) 0 = unit ready, -1 = error
//
// EXPLANATION
// This implements the mandatory SCSI command ªTest Unit Readyº
// (command group 0, code 0x00). Returns 0 if no error (unit ready),
// non-zero otherwise.
//
// ----------------------------------------------------------------------
- (int) testUnitReady
{
int result = -1;
BOOL wasOpen = isOpen;
struct cdb_6 *cdbp = &sr.sr_cdb.cdb_c6;
if (wasOpen || ![self open])
{
[SCSI clearCommandBlock: (union cdb *) cdbp];
cdbp->c6_opcode = C6OP_TESTRDY;
cdbp->c6_lun = lun;
cdbp->c6_len = 0;
sr.sr_dma_dir = SR_DMA_RD;
sr.sr_addr = (char *) 0;
sr.sr_dma_max = 0;
sr.sr_ioto = 10;
result = [self performRequest];
if (!wasOpen && [self close])
{
result = -1;
}
}
return result;
}
// ----------------------------------------------------------------------
//
// NAME
// receiveDiagnosticAlloc: buffer:
//
// PARAMETERS
// (int) alloc Length of diagnostic data.
// (union esenseReply *) buffer Structure used to return
// diagnostic data.
//
// RETURNS
// (int) 0 = success, -1 = error
//
// EXPLANATION
// This executes the SCSI-2 ªReceive Diagnosticº command (command
// group 0, code 0x1C). Allocation length of the buffer must be
// placed into alloc and a pointer to an allocated buffer space must
// be passed in buffer. It is the subclass/developer's
// responsibility to parse the diagnostic information.
//
// SEE ALSO
// sendDiagnosticPf: selfTest: deviceOffLine: unitOffLine:
// parameterListLength: parameterBuffer:
//
// ----------------------------------------------------------------------
- (int) receiveDiagnosticAlloc: (int) alloc buffer: (char *) buffer
{
int result = -1;
BOOL wasOpen = isOpen;
struct cdb_6 *cdbp = &sr.sr_cdb.cdb_c6;
if (wasOpen || ![self open])
{
[SCSI clearCommandBlock: (union cdb *) cdbp];
cdbp->c6_opcode = C6OP_RECEIVEDIAG;
cdbp->c6_lun = lun;
cdbp->c6_len = alloc;
sr.sr_dma_dir = SR_DMA_RD;
sr.sr_addr = (char *) buffer;
sr.sr_dma_max = alloc;
sr.sr_ioto = 10;
result = [self performRequest];
if (!wasOpen && [self close])
{
result = -1;
}
}
return result;
}
// ----------------------------------------------------------------------
//
// NAME
// sendDiagnosticPf: selfTest: deviceOffLine: unitOffLine:
// parameterListLength: parameterBuffer:
//
// PARAMETERS
// (BOOL) pf Page format
// (BOOL) st Self test
// (BOOL) dol Device off line
// (BOOL) uol Unit off line
// (int) pll Parameter list length
// (char *pbuf) Parameter buffer
//
// RETURNS
// (int) 0 = success, -1 = error
//
// EXPLANATION
// This executes the SCSI-2 ªSend Diagnosticº command (command group 0,
// code 0x1D). See SCSI-2 Spec for more complete description of
// diagnostic parameters.
//
// SEE ALSO
// receiveDiagnostic: buffer:
//
// ----------------------------------------------------------------------
- (int) sendDiagnosticPf: (BOOL) pf selfTest: (BOOL) st deviceOffLine: (BOOL) dol
unitOffLine: (BOOL) uol parameterListLength: (int) pll parameterBuffer: (char *) pbuf
{
int result = -1;
BOOL wasOpen = isOpen;
struct cdb_send_diagnostic *cdbp = (struct cdb_send_diagnostic *)&sr.sr_cdb.cdb_c6;
if (wasOpen || ![self open])
{
[SCSI clearCommandBlock: (union cdb *) cdbp];
cdbp->sd_opcode = C6OP_SENDDIAG;
cdbp->sd_lun = lun;
cdbp->sd_pf = pf;
cdbp->sd_selftest = st;
cdbp->sd_devofl = dol;
cdbp->sd_unitofl = uol;
sr.sr_dma_dir = SR_DMA_WR;
sr.sr_addr = pbuf;
sr.sr_dma_max = pll;
sr.sr_ioto = 60*60*60;
result = [self performRequest];
if (!wasOpen && [self close])
{
result = -1;
}
}
return result;
}
// ----------------------------------------------------------------------
//
// NAME
// modeSensePage: pc: dbd: mpbuf:
//
// PARAMETERS
// (int) page page code
// (int) pc page control
// (BOOL) dbd mpbuf
//
// RETURNS
// (int) 0 = success, -1 = error
//
// EXPLANATION
// This implements the mandatory SCSI command ªMode Senseº
// (code 0x1A). The page code must be passed in page, the page
// control code in pc, the disabled block descriptor in dbd.
// Information is returned in mpbuf.
//
// ----------------------------------------------------------------------
- (int) modeSensePage: (int) page pc: (int) pc dbd: (BOOL) dbd mpbuf: (struct mode_parameters *) mpbuf;
{
struct cdb_mode_sense *cdbp = (struct cdb_mode_sense *)&sr.sr_cdb.cdb_c6;
int result = -1, bdc, pageDataLength, count, pdOffset = 0;
char *pageBeginning;
BOOL wasOpen = isOpen;
if (wasOpen || ![self open])
{
[SCSI clearCommandBlock: (union cdb *) cdbp];
cdbp->ms_opcode = C6OP_MODESENSE;
cdbp->ms_lun = lun;
cdbp->ms_allocationLength = 200;
cdbp->ms_dbd = dbd;
cdbp->ms_pageCode = page;
cdbp->ms_pageControl = pc;
sr.sr_dma_dir = SR_DMA_RD;
sr.sr_addr = (char *)mpbuf;
sr.sr_dma_max = 256;
sr.sr_ioto = 10;
result = [self performRequest];
bdc = mpbuf->mph.dad.blockDescriptorLength/8;
writeBigEndianLong (bdc, &mpbuf->BE_blockDescriptorCount);
for(count = 0; count < bdc; count++ )
{
mpbuf->mpbd[count] = * (struct ModeParameterBlockDescriptor *)((char *)mpbuf + 4 + (8 * count));
}
pageDataLength = mpbuf->mph.dad.modeDataLength - 3 - mpbuf->mph.dad.blockDescriptorLength;
pageBeginning = (char *)mpbuf + 4 + mpbuf->mph.dad.blockDescriptorLength;
if(pageDataLength > 2)
{
for( count = 0; (pdOffset < pageDataLength) && (count < MAXPAGES); count++)
{
mpbuf->mpp[count]= * ((union PageFormat *) pageBeginning + pdOffset);
if( mpbuf->mpp[count].genericPage.pageHeader.pageLength == 0)
{
break;
}
pdOffset += mpbuf->mpp[count].genericPage.pageHeader.pageLength;
}
}
else
{
count = 0;
result = -1;
}
writeBigEndianLong(count, &mpbuf->BE_pageCount);
if (!wasOpen && [self close])
{
result = -1;
}
}
return result;
}
// **********************************************************************
//
// Instance methods for low-level SCSI access.
//
// **********************************************************************
// ----------------------------------------------------------------------
//
// NAME
// executeRequest
//
// RETURNS
// (int) 0 if request succeeded,
// errno if request failed.
//
// EXPLANATION
// This calls the generic SCSI driver with the parameters as set
// in the structure sp. This is a low level method which should only
// be called by other objects which wish to make SCSI driver
// command requests. Subclasses of SCSI should use the
// performSCSIRequest method instead. Error codes, if any, will be
// returned in the sp structure in the predetermined places
// (see <nextdev/scsireg.h> for the structure of sp). This method
// does not open or close the SCSI device for the caller.
//
// ----------------------------------------------------------------------
- (int) executeRequest: (struct scsi_req *) sp
{
int error;
sr = *sp;
error = [self performRequest];
*sp = sr;
return error;
}
// ----------------------------------------------------------------------
//
// NAME
// executeBusReset
//
// RETURNS
// (int) 0 if request succeeded,
// errno if ioctl call failed.
//
// EXPLANATION
// This performs a software RESET of the SCSI bus. It can only be
// performed by someone running as the superuser and should be
// used cautiously. See the sg (4) manual page for more information.
//
// ----------------------------------------------------------------------
- (int) executeBusReset
{
if (ioctl(fd,SGIOCRST,NULL) < 0)
{
return errno;
}
return 0;
}
// **********************************************************************
//
// Internal (private!) Instance Methods
// !!! ONLY SUBCLASSES SHOULD CALL THESE PRIVATE METHODS !!!
//
// **********************************************************************
// ----------------------------------------------------------------------
//
// NAME
// performRequest
//
// RETURNS
// (int) 0 if request succeeded,
// errno if ioctl call failed,
// sr.sr_io_status if request returned an error.
//
// EXPLANATION
// This calls the generic SCSI driver with the parameters as set
// in instance variable sr. This is a private method which should
// only be called by subclasses of SCSI which wish to make SCSI
// driver command requests. This method does NOT open or close
// the SCSI device for the caller.
//
// ----------------------------------------------------------------------
- (int) performRequest
{
char errorstr[512];
if (ioctl(fd, SGIOCREQ, &sr) < 0)
{
sprintf(errorstr,"IOctl failed! errno = %d",errno);
NXLogError(errorstr);
return errno;
}
if(sr.sr_io_status || sr.sr_scsi_status)
{
sprintf(errorstr,"IOReq failed! sr_io_status = %d, sr_scsi_status = %d msg = %s",
sr.sr_io_status,sr.sr_scsi_status,[self returnError: self]);
NXLogError(errorstr);
return sr.sr_io_status;
}
return 0;
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.