This is SCSI.m in view mode; [Download] [Up]
//
// SCSI2_Class - provides architecture independendent low-level
// SCSI device access.
//
// 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.
//
// Portions of this code are derived from code provided by
// Andreas Ploeger 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.154 94/12/07 04:04:34 chris Exp $"
// generic Unix headers
#import <libc.h>
#import <stdio.h>
#import <strings.h>
#import <fcntl.h>
#import <syslog.h>
// NeXTSTEP headers
#import <appkit/appkit.h>
// SCSI2 class specific headers
#import "SCSI.h"
#import "Version.h"
#import "scsi2reg.h"
#import "errio.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():
//
// ----------------------------------------------------------------------
u_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()
//
// ----------------------------------------------------------------------
u_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():
//
// ----------------------------------------------------------------------
u_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
// clearCommandBlock:
//
// PARAMETERS
// (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: (cdb *) cdb
{
bzero((char *)cdb,12);
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 initializing SCSI class and setting/getting
// miscellaneous parameters.
//
// **********************************************************************
// ----------------------------------------------------------------------
//
// NAME
// init
//
// RETURNS
// self
//
// EXPLANATION
// Initializes a new instance of the SCSI class.
//
// ----------------------------------------------------------------------
- init
{
target = -1;
lun = 0;
dev_name = "/dev/sg";
ioctlCode = SGIOCREQ;
isOpen = FALSE;
timeout = 30;
dprintf = printfToStderr;
return [super init];
}
// ----------------------------------------------------------------------
//
// NAME
// setTimeout:
//
// PARAMETERS
// (int)to Timeout delay in seconds.
//
// EXPLANATION
// Sets the timeout delay for SCSI pre-packaged command methods.
// Timeout defaults to 30 seconds which should be fine for most
// commands.
//
// SEE ALSO:
// timeout, setImmediate:
//
// ----------------------------------------------------------------------
- (void)setTimeout: (int)to
{
timeout = to;
}
// ----------------------------------------------------------------------
//
// NAME
// timeout
//
// RETURNS
// (int)timeout Current timeout delay in seconds.
//
// EXPLANATION
// Returns the current timeout delay for SCSI pre-packaged command
// methods.
//
// SEE ALSO
// setTimeout, setImmediate:
//
// ----------------------------------------------------------------------
- (int) timeout
{
return timeout;
}
// ----------------------------------------------------------------------
//
// NAME
// setImmediate:
//
// PARAMETERS
// (BOOL)immediate Specifies immediate mode on or off.
//
// EXPLANATION
// If setImmediate: TRUE is used lengthy commands (such as FORMAT
// UNIT) will be executed in immediate mode and return as soon
// as the device successfully acknowledges the command without
// necessarily waiting for the command to complete. If
// setImmediate: FALSE is used all commands will not return until
// they have been entirely completed by the target device. The
// default is immediate command execution. If immediate execution
// is disabled you may have to adjust the timeout value (using
// setTimeout:) since the default 30 second SCSI command timeout
// may not be sufficient to allow lengthy commands such as a
// FORMAT UNIT to complete.
//
// SEE ALSO
// setTimeout, immediate:
//
// ----------------------------------------------------------------------
- (void)setImmediate: (BOOL)immediate
{
immed = immediate;
}
// ----------------------------------------------------------------------
//
// NAME
// immediate
//
// RETURNS
// (int)immediate Current immediate execution mode selection.
//
// EXPLANATION
// If this method returns true then immediate command execution
// mode is currently enabled (the default). If this method returns
// false immediate mode execution is disabled and you may have to
// adjust the timeout to allow sufficient time for execution of
// lengthy commands such as FORMAT UNIT.
//
// SEE ALSO
// setTimeout, setImmediate:
//
// ----------------------------------------------------------------------
- (BOOL)immediate
{
return immed;
}
// **********************************************************************
//
// Instance methods for opening, closing, and setting the target/lun
// for the SCSI device.
//
// **********************************************************************
// ----------------------------------------------------------------------
//
// NAME
// openDevice: ioctlCode:
//
// PARAMETERS
// (const char *) deviceName SCSI device to open (i.e /dev/mt0)
// (int) ioctlCode ioctl request type to use
//
// RETURNS
// (int) 0 0 = success, -1 failure
//
// EXPLANATION
// Opens the specified SCSI device. The ioctlCode value specifies
// what value will be passed in the request field for ioctl calls
// used to issue command requests to this device. For example,
// for generic SCSI devices the request type is SGIOCTLREQ, for
// tape devices it's MTIOCSRQ, for disk type devices it's DKIOCREQ.
// 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. If
// using pre-packaged command methods with a device OTHER than the
// generic SCSI device the setDevice:ioctlCode: method must be used
// to specify the correct device and ioctl code to use.
//
// SEE ALSO:
// setDevice:ioctlCode:, openSCSI, closeSCSI
//
// ----------------------------------------------------------------------
- (int)openDevice:(const char *)deviceName ioctlCode: (int)iCode
{
dev_name = NXCopyStringBuffer(deviceName);
target = -2;
lun = 0;
ioctlCode = iCode;
return [self openSCSI];
}
// ----------------------------------------------------------------------
//
// 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:
// openSCSI, closeSCSI, setTarget: lun:
//
// ----------------------------------------------------------------------
- (int) openTarget: (int)trg lun: (int)ln
{
int result;
ioctlCode = SGIOCREQ;
dev_name = NXCopyStringBuffer("/dev/sg");
if (!(result = [self setTarget: trg lun: ln]))
{
result = [self openSCSI];
}
return result;
}
// ----------------------------------------------------------------------
//
// NAME
// openSCSI
//
// RETURNS
// (int) 0 = success, -1 = failure
//
// EXPLANATION
// Opens the SCSI driver. If an alternate driver name has been
// specified previously with setDriver: (i.e. /dev/mt0) that device
// will be opened, otherwise the first available generic (sg)
// device will be opened. If you are using the generic device you
// must remember to select a target and lun with setTarget:lun:
// before issuing any SCSI commands. 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 any SCSI
// device if there is already a SCSI device open.
//
// SEE ALSO
// openAt: lun:, openDevice: ioctlCode: closeSCSI, isOpen
//
// ----------------------------------------------------------------------
- (int) openSCSI
{
int result = -1;
char tmpBuf[20];
unsigned int tmp;
if (isOpen == TRUE)
{
sprintf(errorString, "SCSI device already open!");
dprintf(errorString);
}
else
{
if (target == -2)
{
if( (!access(dev_name, R_OK | W_OK))
&& ((fd = open(dev_name, O_RDWR)) >= 0) )
{
isOpen = TRUE;
result = 0;
}
else
{
sprintf(errorString, "Could not open SCSI device %s\n", dev_name);
dprintf(errorString);
perror("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)
&& (ioctl(fd, SGIOCENAS, NULL) >= 0) )
{
isOpen = TRUE;
result = 0;
break;
}
}
if (result)
{
sprintf(errorString,
"Could not open SCSI generic driver <%sX>.\n"
"File descriptor = 0x%X\n",
tmpBuf, (unsigned int) fd);
dprintf(errorString);
perror("open");
}
else
{
if (result = [self setTarget: target lun: lun])
{
[self closeSCSI];
}
}
}
}
return result;
}
// ----------------------------------------------------------------------
//
// NAME
// setDevice: ioctlCode:
//
// PARAMETERS
// (const char *) deviceName SCSI device to open (i.e /dev/mt0)
// (int) ioctlCode ioctl request type to use
//
// RETURNS
// (int) 0 = Success, -1 = Error
//
// EXPLANATION
// Selects the indicated device for subsequent SCSI commands. The
// ioctlCode value specifies what value will be passed in the
// request field for ioctl calls used to issue command requests to
// this device. For example, for generic SCSI devices the request
// type is SGIOCTLREQ, for tape type devices it is MTIOCSRQ,
// for disk type devices it's DKIOCREQ. The error value returned
// by this method is semi-meaningless since we don't know if
// the selected device can be successfully opened until we actually
// try to open it through the openSCSI: method. NOTE: The setDevice:
// method, unlike the setTarget:lun: method can only be called
// BEFORE the SCSI device is opened.
//
// SEE ALSO
// openDevice:ioctlCode:
//
// ----------------------------------------------------------------------
- (int) setDevice: (const char *)deviceName ioctlCode: (int)iCode
{
int result = -1;
if (isOpen == TRUE)
{
sprintf(errorString, "SCSI device already open!");
dprintf(errorString);
}
else
{
if ( (access(deviceName, F_OK))
&& (!access(deviceName, R_OK | W_OK)) )
{
ioctlCode = iCode;
target = -2;
lun = 0;
dev_name = NXCopyStringBuffer(deviceName);
result = 0;
}
}
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);
dprintf(errorString);
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);
dprintf(errorString);
result = -1;
}
}
}
return result;
}
// ----------------------------------------------------------------------
//
// NAME
// closeSCSI
//
// 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) closeSCSI
{
if (isOpen == FALSE)
{
sprintf(errorString, "SCSI device already closed!");
dprintf(errorString);
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);
dprintf(errorString);
perror("close");
return -1;
}
isOpen = FALSE;
return 0;
}
// **********************************************************************
//
// Methods for reporting status and error information.
//
// **********************************************************************
// ----------------------------------------------------------------------
//
// NAME
// setDprintf:
//
// PARAMETERS
// (PrintFunction)pf Pointer to current error print function.
//
// EXPLANATION
//
// This method allows the programmer to specify which PrintFunction
// will be used to display error and debugging messages from the
// SCSI class and its sub-classes. The PrintFunction typedef is
// defined in SCSI.h. A PrintFunction is is a function which
// accepts printf style parameters (a format string followed by a
// parameter list) and displays the formatted output to a particular
// place. The programmer can specify a custom PrintFunction or use
// one of the following pre-programmed PrintFunctions included in
// errio.c:
//
// printfToStderr - sends formatted output to stder
// printfToStdout - sends formatted output to stdou
// printfToSysLog - sends formatted output to system log
// printfToNull - a do nothing PrintFunction stub
//
// The default PrintFunction is printfToStdErr. A pointer to the
// currently selected PrintFunction can be retrieved using the dprintf
// method.
//
// Note to sub-class implementers: Internally, the pointer to the
// currently selected PrintFunction is stored in the dprintf instance
// variable of the SCSI class. In order for SCSI sub-class error
// messages to be properly redirected via the setDprintf method,
// sub-classes should always use the function pointer in the dprintf
// instance variable to display error and debugging messages.
// For example:
//
// dprintf("Your error message here, value: %d string: %s",1,"Hi!");
//
// SEE ALSO
// dprintf:
//
// ----------------------------------------------------------------------
+ setDprintf: (PrintFunction)pf
{
dprintf = pf;
return self;
}
// ----------------------------------------------------------------------
//
// NAME
// dprintf
//
// RETURNS
// (PrintFunction) Pointer to current error print function.
//
// EXPLANATION
// This returns the pointer to the current function which should
// be used for printing and formatting all error messages. See
// the explanation of the setDprintf method for more information.
//
// SEE ALSO
// setDprintf:
//
// ----------------------------------------------------------------------
+ (PrintFunction)dprintf
{
return dprintf;
}
// ----------------------------------------------------------------------
//
// 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, closeSCSI, openAt: lun:
//
// ----------------------------------------------------------------------
- (BOOL) isOpen
{
return isOpen;
}
// ----------------------------------------------------------------------
//
// NAME
// fd
//
// RETURNS
// (int) File descriptor or -1
//
// EXPLANATION
// This returns the file descriptor of the currently open device
// or -1 if there is no device open. Useful for performing
// ioctls on device.
//
// SEE ALSO
// open, closeSCSI, openAt: lun:
//
// ----------------------------------------------------------------------
- (int)fd
{
return isOpen ? fd :-1;
}
// ----------------------------------------------------------------------
//
// 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 stuct containing error info.
//
// EXPLANATION
// This returns the status of the last SCSI request as a
// pointer to a structure defined in "scsi2reg.".
//
// 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 "scsi2reg.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];
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.esr_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 (TRUE)
{
sprintf(stringBuffer,
"\tBytes Transferred:\t%d of max: %d\n"
"\tDriver Status:\t%s\n"
"\tTarget Status:\t%s\n"
"\tExtended Sense:\t%s\n",
sr.sr_dma_xfr,sr.sr_dma_max,
*driverStatus,
*scsiStatus,
*esense);
}
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 closeSCSI])
{
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 inquiry_reply ibuffer;
BOOL result;
PrintFunction oldPf = dprintf;
dprintf = printfToNull;
result = (![self inquiry: &ibuffer]);
dprintf = oldPf;
return result;
}
// **********************************************************************
//
// Instance methods for implementing commonly used SCSI commands.
//
// **********************************************************************
// ----------------------------------------------------------------------
//
// NAME
// inquiry:
//
// PARAMETERS
// (struct inquiry_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 inquiry_reply structure defined in "scsi2reg.h"
//
// ----------------------------------------------------------------------
- (int) inquiry: (struct inquiry_reply *) ibuffer
{
int result = -1;
BOOL wasOpen = isOpen;
struct cdb6 *cdbp = &sr.sr_cdb.cdb6;
bzero((char *)ibuffer,sizeof(*ibuffer));
if (wasOpen || ![self openSCSI])
{
[SCSI clearCommandBlock: (cdb *) cdbp];
cdbp->c6_opcode = C6OP_INQUIRY;
writeBigEndianValue(lun<<21, &cdbp->c6_lunlba_BE24, 24);
cdbp->c6_len = 36;
sr.sr_dma_dir = SR_DMA_RD;
sr.sr_addr = (char *)ibuffer;
sr.sr_dma_max = sizeof(struct inquiry_reply);
sr.sr_ioto = timeout;
result = [self performRequest];
if (!wasOpen && [self closeSCSI])
{
result = -1;
}
}
return(result);
}
// ----------------------------------------------------------------------
//
// NAME
// readCapacity
//
// PARAMETERS
// (struct capacity_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 capacity_reply structure
// defined in "SCSI2reg.h"
//
// ----------------------------------------------------------------------
- (int) readCapacity: (struct capacity_reply *) rbuffer
{
int result = -1;
BOOL wasOpen = isOpen;
struct cdb10 *cdbp = &sr.sr_cdb.cdb10;
bzero((char *)rbuffer, sizeof(*rbuffer));
if (wasOpen || ![self openSCSI])
{
[SCSI clearCommandBlock: (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 = sizeof(*rbuffer);
sr.sr_ioto = timeout;
result = [self performRequest];
if (!wasOpen && [self closeSCSI])
{
result = -1;
}
}
return(result);
}
// ----------------------------------------------------------------------
//
// NAME
// requestSense:
//
// PARAMETERS
// (struct esense_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 esense_reply structure defined in
// "scsi2reg".
//
// ----------------------------------------------------------------------
- (int) requestSense: (struct esense_reply *) ebuffer
{
int result = -1;
BOOL wasOpen = isOpen;
struct cdb6 *cdbp = &sr.sr_cdb.cdb6;
bzero((char *)ebuffer,sizeof(*ebuffer));
if (wasOpen || ![self openSCSI])
{
[SCSI clearCommandBlock: (cdb *) cdbp];
cdbp->c6_opcode = C6OP_REQSENSE;
cdbp->c6_len = sizeof(*ebuffer);
writeBigEndianValue((lun<<21), &cdbp->c6_lunlba_BE24, 24);
sr.sr_dma_dir = SR_DMA_RD;
sr.sr_addr = (char *)ebuffer;
sr.sr_dma_max = sizeof(*ebuffer);
sr.sr_ioto = timeout;
result = [self performRequest];
if (!wasOpen && [self closeSCSI])
{
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 cdb6 *cdbp = &sr.sr_cdb.cdb6;
if (wasOpen || ![self openSCSI])
{
[SCSI clearCommandBlock: (cdb *) cdbp];
cdbp->c6_opcode = C6OP_TESTRDY;
cdbp->c6_len = 0;
writeBigEndianValue((lun<<21), &cdbp->c6_lunlba_BE24, 24);
sr.sr_dma_dir = SR_DMA_RD;
sr.sr_addr = (char *)0;
sr.sr_dma_max = 0;
sr.sr_ioto = timeout;
result = [self performRequest];
if (!wasOpen && [self closeSCSI])
{
result = -1;
}
}
return result;
}
// ----------------------------------------------------------------------
//
// NAME
// receiveDiagnostic: size:
//
// PARAMETERS
// (char *) dbuf Pre-allocated buffer used to return data.
// (int) size Length of diagnostic data byffer.
//
// 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
// sendDiagnostic:size:st:dol:uol:
//
// ----------------------------------------------------------------------
- (int) receiveDiagnostic: (char *)dbuf size: (u_char)size
{
int result = -1;
BOOL wasOpen = isOpen;
struct cdb6 *cdbp = &sr.sr_cdb.cdb6;
bzero((char *)dbuf,size);
if (wasOpen || ![self openSCSI])
{
[SCSI clearCommandBlock: (cdb *) cdbp];
cdbp->c6_opcode = C6OP_RECEIVEDIAG;
cdbp->c6_len = size;
writeBigEndianValue((lun<<21), &cdbp->c6_lunlba_BE24, 24);
sr.sr_dma_dir = SR_DMA_RD;
sr.sr_addr = (char *)dbuf;
sr.sr_dma_max = size;
sr.sr_ioto = timeout;
result = [self performRequest];
if (!wasOpen && [self closeSCSI])
{
result = -1;
}
}
return result;
}
// ----------------------------------------------------------------------
//
// NAME
// sendDiagnostic:size:pf:st:dol:uol:
//
// PARAMETERS
// (BOOL) pf Page format
// (BOOL) st Self test
// (BOOL) dol Device off line
// (BOOL) uol Unit off line
// (u_char) size Parameter list length
// (char *) dbuf 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:size:
//
// ----------------------------------------------------------------------
- (int) sendDiagnostic: (char *)dbuf size: (u_char)size pf: (BOOL)pf st: (BOOL)st dol: (BOOL)dol uol: (BOOL)uol
{
int result = -1;
BOOL wasOpen = isOpen;
struct cdb6_send_diagnostic *cdbp = &sr.sr_cdb.cdb6_send_diagnostic;
if (wasOpen || ![self openSCSI])
{
[SCSI clearCommandBlock: (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 = dbuf;
sr.sr_dma_max = size;
sr.sr_ioto = timeout;
result = [self performRequest];
if (!wasOpen && [self closeSCSI])
{
result = -1;
}
}
return result;
}
// ----------------------------------------------------------------------
//
// NAME
// modeSenseRaw:size:page:pc:dbd:
//
// PARAMETERS
// (mode_parameter_list *) mpl pointer to data buffer
// (u_char) size size of buffer
// (u_char) page page type
// (u_char) pc page control code
// (BOOL) dbd disable block descriptor
//
// RETURNS
// (int) 0 = success, -1 = error
//
// EXPLANATION
// This implements the mandatory SCSI command ªMode Senseº (code
// 0x1A). The complete mode parameter list structure including
// headers and block descriptors (if dbd = FALSE) is returned in
// the designated pre-allocated buffer. It is up to the caller to
// interpret this returned data according to the structures defined
// in scsi2reg.h and the SCSI 2 spec. If you would prefer that
// JUST the requested page data be returned, without header or
// block descriptor information, use the modeSenseCooked:size:page:pc:
// method instead.
//
// SEE ALSO
// modeSenseCooked:size:page:pc:,modeSelectRaw:size:save:
//
// ----------------------------------------------------------------------
- (int) modeSenseRaw: (mode_parameter_list *)mpl size: (u_char)size page: (u_char)page pc: (u_char)pc dbd: (BOOL)dbd
{
struct cdb6_mode_sense *cdbp = &sr.sr_cdb.cdb6_mode_sense;
int result = -1;
BOOL wasOpen = isOpen;
bzero((char *)mpl,size);
if (wasOpen || ![self openSCSI])
{
[SCSI clearCommandBlock: (cdb *)cdbp];
cdbp->ms_opcode = C6OP_MODESENSE;
cdbp->ms_lun = lun;
cdbp->ms_allocationlength = size;
cdbp->ms_dbd = dbd;
cdbp->ms_pagecode = page;
cdbp->ms_pagecontrol = pc;
sr.sr_dma_dir = SR_DMA_RD;
sr.sr_addr = (char *)mpl;
sr.sr_dma_max = size;
sr.sr_ioto = timeout;
result = [self performRequest];
if (!wasOpen && [self closeSCSI])
{
result = -1;
}
}
return result;
}
// ----------------------------------------------------------------------
//
// NAME
// modeSelectRaw:size:save:
//
// PARAMETERS
// (mode_parameter_list *) mpl pointer to mode parameter list
// (u_char) size size of mode parameter list
// (BOOL) save TRUE = save parameters
//
// RETURNS
// (int) 0 = success, -1 = error
//
// EXPLANATION
// This implements the SCSI command ªMode Selectº. The data
// for the mode select operation is passed in the mode parameter
// list structure. If the save parameter is specified TRUE
// the save parameters option will be used for the Mode Select
// command and the data pages will be stored to non-volatile
// device memory, if the target device supports this. Remember
// to set all reserved fields to NULL and to supply only valid
// parameters in the mode parameter list pages or this call will
// probably fail. Be CAREFUL!
//
// SEE ALSO
// modeSenseCooked:size:page:pc:, modeSenseRaw:size:page:pc:dbd:
//
// ----------------------------------------------------------------------
- (int) modeSelectRaw: (mode_parameter_list *)mpl size: (u_char)size save: (BOOL)save
{
struct cdb6_mode_select *cdbp = &sr.sr_cdb.cdb6_mode_select;
int result = -1;
BOOL wasOpen = isOpen;
if (wasOpen || ![self openSCSI])
{
[SCSI clearCommandBlock: (cdb *)cdbp];
cdbp->msl_opcode = C6OP_MODESELECT;
cdbp->msl_lun = lun;
cdbp->msl_pf = 1;
cdbp->msl_pll = size;
cdbp->msl_sp = save;
sr.sr_dma_dir = SR_DMA_WR;
sr.sr_addr = (char *)mpl;
sr.sr_dma_max = size;
sr.sr_ioto = timeout;
result = [self performRequest];
if (!wasOpen && [self closeSCSI])
{
result = -1;
}
}
return result;
}
// ----------------------------------------------------------------------
//
// NAME
// modeSenseCooked:size:page:pc:
//
// PARAMETERS
// (mode_page *) mpdest pointer to pre-allocated page bugger
// (u_char) size page size
// (u_char) page page type
// (u_char) pc page control code
//
// RETURNS
// (int) 0 = success, -1 = error
//
// EXPLANATION
// This is a wrapper for the modeSenseRaw:size:page:pc:dbd: method
// which returns ONLY a single requested page in the provided
// buffer stripped of all the mode parameter list header and
// block descriptor information. Simpler to decode than the
// information returned by modeSenseRaw:size:page:pc:dbd: but not
// as flexible.
//
// SEE ALSO
// modeSenseRaw:size:page:pc:dbd, modeSelectRaw:size:save:
//
// ----------------------------------------------------------------------
- (int) modeSenseCooked: (mode_page *)mpdest size: (u_char)size page: (u_char)page pc: (u_char)pc
{
mode_parameter_list *mpl;
mode_page *mpsrc;
int result = -1;
bzero((char *)mpdest,size);
if (mpl = malloc(255))
{
if (![self modeSenseRaw: mpl size: 200 page: page pc: pc dbd: FALSE])
{
mpsrc = (mode_page *)(((char *)mpl)+4+mpl->mpl_bdlength);
if (mpsrc->ph.ph_pagelength < size)
{
size = mpsrc->ph.ph_pagelength;
}
bcopy((char *)mpsrc,(char *)mpdest,size);
result = 0;
}
free(mpl);
}
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 scsi_req structure in the predetermined places
// (see "scsi2reg.h" for the structure of scsi_req). 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
{
if (ioctl(fd, ioctlCode, &sr) < 0)
{
dprintf("IOctl failed!\nerrno = %d\n",errno);
return errno;
}
if(sr.sr_io_status || sr.sr_scsi_status)
{
dprintf("IOReq failed!\nsr_io_status = %d, sr_scsi_status = %d, dma_xfer = %d, dma_max = %d\n%s\n",
sr.sr_io_status,sr.sr_scsi_status,sr.sr_dma_xfr,sr.sr_dma_max,[self returnError: self]);
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.