This is SCSI.m in view mode; [Download] [Up]
//
// SCSI.m
//
// SCSI generic driver object library (phew)
// Version 3 (Revision 3.0)
//
// The SCSI class source, header, and documentation are all
// Copyright (C) 1990 by Jiro Nakamura and Canon, Inc.
// Copyright (C) 1991 by Jiro Nakamura and Canon, Inc.
// Copyright (C) 1992 by Jiro Nakamura
// All Rights Reserved.
//
//
// Original Author: Jiro Nakamura
// Created: June 12, 1990
// Last Modified: July 12, 1991
//
// RCS Information
// Revision Number-> $Revision: 3.1 $
// Last Revised-> $Date: 92/11/13 02:31:51 $
//
static char rcsid[]="$Id: SCSI.m,v 3.1 92/11/13 02:31:51 jiro Exp Locker: jiro $";
static char copyrightid[]="$Copyright: Copyright (C) 1992 by Jiro Nakamura$";
#define VERSION 3
#import "SCSI.h"
#import <libc.h>
#import <stdio.h>
#import <fcntl.h>
#import <stdio.h>
#import <objc/typedstream.h>
#import <appkit/nextstd.h> // For NXLogError
#import <appkit/Application.h>
#import <appkit/Panel.h>
// SCSI produces a whole truckload of debugging code. Usually we don't
// want it on.
#ifdef DEBUG
#define DEBUG2
#undef DEBUG
#endif
@implementation SCSI
+new
{
self = [super new];
target = -1;
lun = 0;
dev_name = "/dev/sg"; // last digit gets appended later
scsiOpen = FALSE;
return self;
return self;
}
+ (void )clearCommandBlock: (union cdb *) cd
{
int i;
char *p;
p = (char *)cd;
for(i=0; i<sizeof(union cdb); i++)
*p++ = 0;
return;
}
- (int) version
{
return VERSION;
}
- (int) openSCSI
{
char tmpBuf[20];
unsigned int tmp;
// *No* program should try to open a driver that's already open.
// Bad, bad, bad.
if( scsiOpen == TRUE)
{
sprintf(errorString, "SCSI Device already open -- "
"bug in program...");
return fd;
}
for( tmp = 0; ; tmp ++)
{
sprintf(tmpBuf, "%s%1X", dev_name, tmp) ;
#ifdef DEBUG
fprintf(stderr, "SCSI: Opening device: %s\n",
tmpBuf);
#endif
if( access(tmpBuf, F_OK) ) // no existo
break;
if( access(tmpBuf, R_OK | W_OK) ) // cannot access
continue;
if ((fd = open(tmpBuf, O_RDWR)) >= 0)
{
scsiOpen = TRUE;
return 0;
}
}
sprintf(errorString,"Could not open SCSI "
"generic driver <%sX>.\n"
"File descriptor = 0x%X\n", tmpBuf,
(unsigned int) fd);
perror("open");
scsiOpen = FALSE;
return -1;
}
- (int) openSCSIAt: (int) trg lun: (int) ln
{
if( [self openSCSI] )
return -1;
return( [self setTarget: trg lun: ln]);
}
- (int) setTarget: (int) trg lun: (int) ln
{
sa.sa_target = target = trg;
sa.sa_lun = lun = ln;
if (ioctl(fd,SGIOCSTL,&sa) < 0)
{
sprintf(errorString,"ioctl(SGIOCSTL): "
"Error setting target %d lun %d (errno = %d)\n",
target,lun, errno);
return -1;
}
return 0;
}
- (int) closeSCSI
{
if( scsiOpen == FALSE)
{
sprintf(errorString, "SCSI device already closed. "
"Error in program...");
return -1;
}
if ( close(fd) < 0)
{
sprintf(errorString, "Could not close %sX - fd = %XH\n"
"errno = %d\n", dev_name,
(unsigned int) fd ,errno);
perror("close");
return -1;
}
scsiOpen = FALSE;
return 0;
}
- (BOOL) scsiOpen
{
return( scsiOpen);
}
- (struct scsi_req *) statusReq
{
return &sr;
}
- (char *) errorString
{
return errorString;
}
- (int) findDevice
{
return [self findDevice: 0];
}
- (int) findDevice: (int) trg
{
int tmp;
if( scsiOpen)
return [self findDeviceSCSI: trg];
else
{
if([self openSCSI] )
{
sprintf(errorString,"Can't open SCSI driver");
return -1;
}
tmp = [self findDeviceSCSI: trg];
[self closeSCSI];
return( tmp);
}
}
- (int) findDeviceSCSI: (int) trg
{
int tmp, oldTarg, oldLun;
oldTarg = target;
oldLun = lun;
for( tmp = trg; tmp < 8; tmp ++)
{
if( [self setTarget: tmp lun: 0] )
continue; // no device
if( [self isDeviceSCSI])
{
[self setTarget: oldTarg lun: oldLun];
return( tmp);
}
}
[self setTarget: oldTarg lun: oldLun];
return( -1);
}
- (BOOL) isDevice
{
BOOL tmp;
if( scsiOpen)
return [self isDeviceSCSI];
if([self openSCSI] )
{
sprintf(errorString,"SCSI: Can't open SCSI driver");
NXLogError(errorString);
return NO;
}
tmp = [self isDeviceSCSI];
[self closeSCSI];
return( tmp);
}
- (BOOL) isDeviceSCSI // Subclasses should implement this
{
[self subclassResponsibility: _cmd];
return NO;
}
// =====================================================
// Archiving methods
// =====================================================
- write: (NXTypedStream *) stream
{
[super write: stream];
NXWriteTypes( stream, "ii**", &target, &lun, &dev_name, &errorString);
return self;
}
- read: (NXTypedStream *) stream
{
[super read: stream];
NXReadTypes( stream, "ii**", &target, &lun, &dev_name, &errorString);
return self;
}
// =====================================================
// Internal Routines
// =====================================================
- (int) performSCSIRequest
{
if (ioctl(fd,SGIOCREQ, &sr) < 0) {
#ifdef DEBUG
printf("..Error executing ioctl\n");
printf("errno = %d\n",errno);
#endif
return errno;
}
#ifdef DEBUG
fprintf(stderr,
"SCSI Command %x ==> %d byte(s) in %d.%06d sec\n",
sr.sr_cdb.cdb_c6.c6_opcode,
sr.sr_dma_xfr,
(int) sr.sr_exec_time.tv_sec,
(int) sr.sr_exec_time.tv_usec);
#endif
if(sr.sr_io_status || sr.sr_scsi_status) {
#ifdef DEBUG2
fprintf(stderr, "sr_io_status = 0x%02X\n", (unsigned int)
sr.sr_io_status);
if(sr.sr_io_status == SR_IOST_CHKSV) {
fprintf(stderr,
" sense key = 0x%02X sense code = %02XH\n",
(unsigned int) sr.sr_esense.er_sensekey,
(unsigned int) sr.sr_esense.er_addsensecode);
}
fprintf(stderr, "SCSI status = 0x%02X\n", (unsigned int)
sr.sr_scsi_status);
#endif
return sr.sr_io_status;
}
return 0;
}
// =====================================================
// SCSI ROUTINES AND MACROS
// =====================================================
- (int) inquiry: (struct inquiry_reply *) ibuffer
{
int tmp;
if( scsiOpen)
return [self inquirySCSI: ibuffer];
else
{
if([self openSCSI] )
{
sprintf(errorString,"Can't open SCSI driver");
return -1;
}
tmp = [self inquirySCSI: ibuffer];
[self closeSCSI];
return( tmp);
}
}
- (int) inquirySCSI: (struct inquiry_reply *) ibuffer
{
struct cdb_6 *cdbp = &sr.sr_cdb.cdb_c6;
[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;
return [self performSCSIRequest];
}
- (int) readCapacity: (struct capacity_reply *) rbuffer
{
int tmp;
if( scsiOpen)
{
return [self readCapacitySCSI: rbuffer];
}
else
{
if([self openSCSI] )
{
sprintf(errorString,"Can't open SCSI driver");
return -1;
}
tmp = [self readCapacitySCSI: rbuffer];
[self closeSCSI];
return( tmp);
}
}
- (int) readCapacitySCSI: (struct capacity_reply *) rbuffer
{
struct cdb_10 *cdbp = &sr.sr_cdb.cdb_c10;
[SCSI clearCommandBlock: (union cdb *) cdbp];
cdbp->c10_opcode = C10OP_READCAPACITY;
cdbp->c10_lun = lun;
cdbp->c10_len = 0;
sr.sr_dma_dir = SR_DMA_RD;
sr.sr_addr = (char *) rbuffer;
sr.sr_dma_max = 8;
sr.sr_ioto = 10;
return [self performSCSIRequest];
}
- (int) requestSense: (union esenseReply *) rbuffer
{
int tmp;
if( scsiOpen)
return [self requestSenseSCSI: rbuffer];
else
{
if([self openSCSI] )
{
sprintf(errorString,"Can't open SCSI driver");
return -1;
}
tmp = [self requestSenseSCSI: rbuffer];
[self closeSCSI];
return( tmp);
}
}
- (int) requestSenseSCSI: (union esenseReply *) buffer
{
struct cdb_6 *cdbp = &sr.sr_cdb.cdb_c6;
[SCSI clearCommandBlock: (union cdb *) cdbp];
cdbp->c6_opcode = C6OP_REQSENSE;
cdbp->c6_lun = lun;
cdbp->c6_len = sizeof(*buffer);
sr.sr_dma_dir = SR_DMA_RD;
sr.sr_addr = (char *) buffer;
sr.sr_dma_max = sizeof(*buffer);
sr.sr_ioto = 10;
return [self performSCSIRequest];
}
- (int) receiveDiagnosticAlloc: (int) alloc buffer: (char *) buffer
{
int tmp;
if( scsiOpen)
return [self receiveDiagnosticSCSIAlloc: alloc
buffer: buffer];
else
{
if([self openSCSI] )
{
sprintf(errorString,"Can't open SCSI driver");
return -1;
}
tmp = [self receiveDiagnosticSCSIAlloc: alloc
buffer: buffer];
[self closeSCSI];
return( tmp);
}
}
- (int) receiveDiagnosticSCSIAlloc: (int) alloc buffer: (char *) buffer
{
struct cdb_6 *cdbp = &sr.sr_cdb.cdb_c6;
[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;
return [self performSCSIRequest];
}
- (int) sendDiagnosticPf: (BOOL) pf selfTest: (BOOL) st
deviceOffLine: (BOOL) dol unitOffLine: (BOOL) uol
parameterListLength: (int) pll parameterBuffer: (char *) pbuf
{
int tmp;
if( scsiOpen)
return [self sendDiagnosticSCSIPf: pf selfTest: st
deviceOffLine: dol unitOffLine: uol
parameterListLength: pll parameterBuffer: pbuf];
else
{
if([self openSCSI] )
{
sprintf(errorString,"Can't open SCSI driver");
return -1;
}
tmp = [self sendDiagnosticSCSIPf: pf selfTest: st
deviceOffLine: dol unitOffLine: uol
parameterListLength: pll parameterBuffer: pbuf];
[self closeSCSI];
return( tmp);
}
}
- (int) sendDiagnosticSCSIPf: (BOOL) pf selfTest: (BOOL) st
deviceOffLine: (BOOL) dol unitOffLine: (BOOL) uol
parameterListLength: (int) pll parameterBuffer: (char *) pbuf
{
struct cdb_6 *cdbp = &sr.sr_cdb.cdb_c6;
[SCSI clearCommandBlock: (union cdb *) cdbp];
cdbp->c6_opcode = C6OP_SENDDIAG;
cdbp->c6_lun = lun;
cdbp->c6_lba =
(pf << 20) +
(st << 18) +
(dol << 17) +
(uol << 16) +
((((unsigned int) pll & 0xff00) >> 8)
& 0x00ff);
cdbp->c6_len = (unsigned int) pll & 0x00ff;
sr.sr_dma_dir = SR_DMA_WR;
sr.sr_addr = pbuf;
sr.sr_dma_max = pll;
sr.sr_ioto = 10;
#ifdef DEBUG
printf("Send diagnostic\n");
printf("LBA = %08X\n", cdbp->c6_lba);
printf("Len = %04X\n", cdbp->c6_len);
printf("St = %d\n", st);
#endif
return [self performSCSIRequest];
}
- (int) testUnitReady
{
int tmp;
if( scsiOpen)
return [self testUnitReadySCSI];
else
{
if([self openSCSI] )
{
sprintf(errorString,"Can't open SCSI driver");
return -1;
}
tmp = [self testUnitReadySCSI];
[self closeSCSI];
return( tmp);
}
}
- (int) testUnitReadySCSI
{
struct cdb_6 *cdbp = &sr.sr_cdb.cdb_c6;
[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;
return([self performSCSIRequest]);
}
- (int) modeSensePage: (int) page pc: (int) pc dbd: (BOOL) dbd
mpbuf: (struct mode_parameters *) mpbuf;
{
int tmp;
if( scsiOpen)
return [self modeSenseSCSIPage: page pc: pc dbd: dbd
mpbuf: mpbuf];
else
{
if([self openSCSI] )
{
sprintf(errorString,"Can't open SCSI driver");
return -1;
}
tmp = [self modeSenseSCSIPage: page pc: pc dbd: dbd
mpbuf: mpbuf];
[self closeSCSI];
return( tmp);
}
}
- (int) modeSenseSCSIPage: (int) page pc: (int) pc dbd: (BOOL) dbd
mpbuf: (struct mode_parameters *) mpbuf;
{
struct cdb_6 *cdbp = &sr.sr_cdb.cdb_c6;
char *bufferPointer;
int returnValue, i;
int pageDataLength, pdOffset;
char *pageBeginning;
if( (bufferPointer = malloc( MODESENSEBUFLENGTH )) == NULL )
{
NXRunAlertPanel("Out of Virtual Memory",
"Inexplicably, this program has run out "
"of virtual memory. We must quit (and you "
"should reboot).", "Quit", NULL, NULL );
[NXApp terminate: self];
}
bzero( bufferPointer, MODESENSEBUFLENGTH);
[SCSI clearCommandBlock: (union cdb *) cdbp];
cdbp->c6_opcode = C6OP_MODESENSE;
cdbp->c6_lun = lun;
cdbp->c6_lba =
(((unsigned int) dbd) << 19) +
(((unsigned int) pc) << 14) +
(((unsigned int) page) << 8);
cdbp->c6_len = 200;
sr.sr_dma_dir = SR_DMA_RD;
sr.sr_addr = (char *) bufferPointer;
sr.sr_dma_max = 256;
sr.sr_ioto = 10;
returnValue = [self performSCSIRequest];
#ifdef DEBUG
printf("Mode Sense: Return value = %d\n", returnValue);
printf("LBA = %08X\n", cdbp->c6_lba);
printf("Len = %04X\n", cdbp->c6_len);
#endif
*mpbuf = * ((struct mode_parameters *) bufferPointer);
mpbuf->blockDescriptorCount =
mpbuf->mph.dad.blockDescriptorLength / 8;
pageDataLength = mpbuf->mph.dad.modeDataLength - 3 -
mpbuf->mph.dad.blockDescriptorLength;
pageBeginning = bufferPointer + 4 +
mpbuf->mph.dad.blockDescriptorLength;
#ifdef DEBUG2
fprintf(stderr,"Page Data Length = %d, "
"Block Desc Length = %d\n",
pageDataLength, mpbuf->mph.dad.blockDescriptorLength);
#endif
for( i = 0; i < mpbuf->blockDescriptorCount; i ++ )
{
#ifdef DEBUG2
fprintf(stderr,"Printing block descriptor #%d\n", i);
#endif
mpbuf->mpbd[i] = * (struct ModeParameterBlockDescriptor *)
(bufferPointer + 4 + (8 * i));
}
if( pageDataLength <= 2)
{
mpbuf->pageCount = 0;
return -1;
}
pdOffset = 0;
for( mpbuf->pageCount =0;
pdOffset < pageDataLength && mpbuf->pageCount < MAXPAGES;
mpbuf->pageCount ++)
{
mpbuf->mpp[mpbuf->pageCount]= * ((union PageFormat *)
pageBeginning + pdOffset);
#ifdef DEBUG2
fprintf(stderr, "Page %d -- len %d (pdOffset %d)\n",
mpbuf->pageCount,
mpbuf->mpp[mpbuf->pageCount].genericPage.
pageHeader.pageLength,
pdOffset);
#endif
if( mpbuf->mpp[mpbuf->pageCount].
genericPage.pageHeader.pageLength == 0)
break;
pdOffset += mpbuf->mpp[mpbuf->pageCount].genericPage.
pageHeader.pageLength;
}
free(bufferPointer);
return returnValue;
}
- (const char *) identifyDeviceType: (struct inquiry_reply *) ibuffer
{
switch( ibuffer->ir_devicetype )
{
case DEVTYPE_DISK:
if( ibuffer->ir_removable == YES)
return( "Removable-Disk" );
else
return( "Hard-Drive" );
case DEVTYPE_TAPE:
return( "Tape-Drive" );
case DEVTYPE_PRINTER:
return( "Printer");
case DEVTYPE_PROCESSOR:
return( "Processor" );
case DEVTYPE_WORM:
if( ibuffer->ir_removable == YES)
return( "Removable-Worm" );
else
return( "Fixed-Worm" );
case DEVTYPE_CDROM:
if( ibuffer->ir_removable == YES)
return( "Removable-ReadOnly" );
else
return( "Fixed-ReadOnly" );
case DEVTYPE_SCANNER:
return( "Scanner" );
case DEVTYPE_OPTICAL:
return( "Optical" );
case DEVTYPE_STILLVIDEO:
return( "Still-Video" );
case DEVTYPE_NOTPRESENT:
return( "No-Device" );
default:
NXLogError("Unrecognized device type %02Xh (%d).", ibuffer->ir_devicetype , ibuffer->ir_devicetype );
return( "Not-Recognized" );
}
}
// =====================================================
// EXTERNAL LINKS FOR PROGRAMMERS
// =====================================================
- (int) executeRequest: (struct scsi_req *) sp
{
int error;
sr = *sp;
error = [self performSCSIRequest];
*sp = sr;
return error;
}
- (int) executeBusReset
{
if (ioctl(fd,SGIOCRST, NULL) < 0)
{
#ifdef DEBUG
printf("..Error executing ioctl\n");
printf("errno = %d\n",errno);
#endif
return errno;
}
return 0;
}
- (char *) returnError: sender
{
char *driverStatus, *scsiStatus, *esense;
return( [self returnDriverStatus: &driverStatus
scsiStatus: &scsiStatus andExtendedSense: &esense] );
}
- (char *) returnDriverStatus: (char **) driverStatus scsiStatus:
(char **) scsiStatus andExtendedSense: (char **) esense
{
// we have three error codes
// io_status -- Driver status
// status -- scsi status byte returned by target
// esense -- extended sense data
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;
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.