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