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.