This is scsi_commands.c in view mode; [Download] [Up]
/*
* scsi_commands.c: scsi command functions
*
* This is based on the file that NeXT included in
* /NextDeveloper/Examples/UNIX/SCSI_CD,
* done by James C. Lee at NeXT, Sep 1991, and updated by him
* in Feb 1993. He, in turn, got a lot of data structures &
* function calls from a perftest.c program done by Mike DeMoney.
* It has been changed "just a bit" by Garance Alistair Drosehn/March 1994,
* to add a few routines and make a few minor changes to some others.
*
*/
#import <sys/time.h>
#import <bsd/dev/disk.h>
#import <stdio.h>
#import <libc.h>
#import <stdlib.h>
#import "scsi_commands.h"
/* I use a copy of the NS-3.1 version of bsd/dev/scsireg.h, because
* at some point I think I want to split it apart into "field values"
* (such as #define DEVTYPE_DISK), and actual structure definitions.
* Garance Drosehn/Mar 94
*/
/* #import <bsd/dev/scsireg.h> */
#import "scsireg_31.h" /* garance... */
void fatal(const char *msg, ...)
{
va_list ap;
va_start(ap, msg);
/* fprintf(stderr, "%s: ", progname);*/
vfprintf(stderr, msg, ap);
fprintf(stderr, "\n");
va_end(ap);
exit(1);
}
inline int is_pow2(int i)
{
return (i & (i - 1)) == 0;
}
int do_inquiry(int fd, struct inquiry_reply *irp, struct esense_reply *erp)
{
struct scsi_req sr;
struct cdb_6 *c6p;
int err;
bzero((char *)&sr, sizeof(sr));
c6p = (struct cdb_6 *)&sr.sr_cdb;
c6p->c6_opcode = C6OP_INQUIRY;
c6p->c6_len = sizeof(*irp);
sr.sr_addr = (char *)irp;
sr.sr_dma_max = sizeof(*irp);
sr.sr_ioto = 5;
sr.sr_dma_dir = SR_DMA_RD;
err = ioctl(fd, SDIOCSRQ, &sr);
*erp = sr.sr_esense;
return err | sr.sr_io_status;
}
/**************************************************************************
* NOTE: It turns out that the following routine might not be all that
* safe to call. The man page for sg (the generic scsi driver)
* mentions that it's "extremely hazardous" to bind the generic
* scsi device (using SGIOCSTL) to a device that some other
* SCSI driver is already using. I don't know if doing just a
* inquiry command is hazardous, but this does sometimes cause
* the machine to freeze up for short period of time when it's
* doing the inquiry command on the bootup disk.
*
* Someone who knows more about SCSI programming than I do will
* need to look this over, I guess. Garance Drosehn/Mar 19/94
*/
int do_inquiryall(int replyCount, struct inquiry_all_reply *all,
struct esense_reply *erp)
{
struct scsi_adr scsiAddress;
struct inquiry_reply *thisIrp;
struct scsi_req sr;
struct cdb_6 *c6p;
int err, index, maxIndex, diskIndex, tryCount;
int tempFd = 0;
/* Open up one of the generic SCSI device drivers */
tempFd = open("/dev/sg0", O_RDONLY );
if (tempFd == -1) tempFd = open("/dev/sg1", O_RDONLY );
if (tempFd == -1)
return 0; /* failed */
/* for now, assume there's only one SCSI controller with
* 8 devices (id's 0 thru 7, where 7 is the computer).
* I make this assumption because I have no idea what
* to do if there are multiple SCSI controllers...
*/
maxIndex = replyCount;
if ( replyCount > INQ_ALL_MAX_SCSI_CNT )
maxIndex = INQ_ALL_MAX_SCSI_CNT;
all->maxScsiCount = maxIndex;
/* Go through all the devices in the /dev directory */
diskIndex = 0;
for(index = 0; index < maxIndex; index++) {
bzero((char *)&scsiAddress, sizeof(scsiAddress));
/* Set the scsi address */
scsiAddress.sa_target = index;
scsiAddress.sa_lun = 0;
/* Tell the driver what device we want to talk to */
err = ioctl(tempFd, SGIOCSTL, &scsiAddress);
/* Check for an error */
if (err == -1) {
close(tempFd);
fatal("do_inquiryall: unable to set SCSI address");
}
thisIrp = &(all->scsiArray[index].inqResult);
tryCount = 0;
try_again:
tryCount++;
bzero((char *)&sr, sizeof(sr));
c6p = (struct cdb_6 *)&sr.sr_cdb;
c6p->c6_opcode = C6OP_INQUIRY;
c6p->c6_len = sizeof(*thisIrp);
sr.sr_addr = (char *)thisIrp;
sr.sr_dma_max = sizeof(*thisIrp);
sr.sr_ioto = 90; /* timeout */
sr.sr_dma_dir = SR_DMA_RD;
/* this routine can't call do_inquiry here because it
needs to do an SGIOCREQ instead of SDIOCREQ */
err = ioctl(tempFd, SGIOCREQ, &sr);
*erp = sr.sr_esense;
all->scsiArray[index].devIoStat = sr.sr_io_status;
all->scsiArray[index].deviceNumber = -1;
switch (sr.sr_io_status) {
case SR_IOST_GOOD:
if(tryCount > 1) {
printf("scsi-cmd debug: scsi_id= %d OK on try#=%d\n",
index, tryCount);
}
all->scsiArray[index].deviceNumber = diskIndex++;
break;
case SR_IOST_SELTO:
/* it seems we get a "selection timeout" if there is
* no device with this scsi ID. Do Nothing */
break;
case SR_IOST_CHKSV:
case SR_IOST_CHKSNV:
/* if these come up, then there is a device here.
* Retrying it often seems to be the cure, though
* one should *-> NOTE <-* that I have no idea if
* that is the right thing to do at this point...
* Also NOTE that when this happens, the machine
* tends to freeze up for about 10-20 seconds.
*/
if (tryCount < 2) goto try_again;
all->scsiArray[index].deviceNumber = diskIndex++;
default:
/* probably need to do something with any other
* io_status values, but what? In some cases a
* retry might make sense, or maybe incrementing
* the disk index to at least skip over the device.
* Perhaps a SGIOCRST to reset the scsi bus?
*/
printf("scsi-cmd debug: scsi_id= %d, io_stat= %d try#=%d\n",
index, sr.sr_io_status, tryCount);
break;
}
}
if (tempFd > -1) close(tempFd);
return 1; /* seems to have succeeded */
}
int do_testunitready(int fd, struct timeval *tvp, struct esense_reply *erp)
{
struct scsi_req sr;
struct cdb_6 *c6p;
int err;
bzero((char *)&sr, sizeof(sr));
c6p = (struct cdb_6 *)&sr.sr_cdb;
c6p->c6_opcode = C6OP_TESTRDY;
sr.sr_addr = NULL;
sr.sr_dma_max = 0;
sr.sr_ioto = 5;
sr.sr_dma_dir = SR_DMA_RD;
err = ioctl(fd, SDIOCSRQ, &sr);
*tvp = sr.sr_exec_time;
*erp = sr.sr_esense;
return err | sr.sr_io_status;
}
int do_modesense(int fd, struct mode_sense_reply *msrp, int page,
struct esense_reply *erp)
{
struct scsi_req sr;
struct mode_sense_cmd *mscp;
int err;
bzero((char *)&sr, sizeof(sr));
mscp = (struct mode_sense_cmd *)&sr.sr_cdb;
mscp->msc_opcode = C6OP_MODESENSE;
mscp->msc_pcf = 0; /* report current values */
mscp->msc_page = page;
mscp->msc_len = sizeof(*msrp);
sr.sr_addr = (char *)msrp;
sr.sr_dma_max = sizeof(*msrp);
/*
* Extend timeout so Quantum drive works with test.
*/
sr.sr_ioto = 50;
sr.sr_dma_dir = SR_DMA_RD;
err = ioctl(fd, SDIOCSRQ, &sr);
printf("sr.sr_io_status: %d\n", sr.sr_io_status);
*erp = sr.sr_esense;
return err | sr.sr_io_status;
}
int do_readcapacity(int fd, struct capacity_reply *crp,
struct esense_reply *erp)
{
struct scsi_req sr;
struct cdb_10 *c10p;
int err;
bzero((char *)&sr, sizeof(sr.sr_cdb));
c10p = (struct cdb_10 *)&sr.sr_cdb;
c10p->c10_opcode = C10OP_READCAPACITY;
sr.sr_addr = (char *)crp;
sr.sr_dma_max = sizeof(*crp);
sr.sr_ioto = 5;
sr.sr_dma_dir = SR_DMA_RD;
err = ioctl(fd, SDIOCSRQ, &sr);
*erp = sr.sr_esense;
return err | sr.sr_io_status;
}
int do_seek(int fd, int lba, struct timeval *tvp, struct esense_reply *erp)
{
struct scsi_req sr;
struct cdb_6 *c6p;
int err;
bzero((char *)&sr, sizeof(sr));
c6p = (struct cdb_6 *)&sr.sr_cdb;
c6p->c6_opcode = C6OP_SEEK;
#ifdef i386
if (lba > 0) {
c6p->c6_lba0 = lba & 0xFF;
c6p->c6_lba1 = (lba >> 8) & 0xFF;
c6p->c6_lba2 = (lba >> 16) & 0xFF;
} else
c6p->c6_lba0 = c6p->c6_lba1 = c6p->c6_lba2 = 0;
#else
c6p->c6_lba = lba;
#endif
sr.sr_addr = 0;
sr.sr_dma_max = 0; /* don't really do I/O to memory */
sr.sr_ioto = 5;
sr.sr_dma_dir = SR_DMA_RD;
err = ioctl(fd, SDIOCSRQ, &sr);
*tvp = sr.sr_exec_time;
*erp = sr.sr_esense;
return err | sr.sr_io_status;
}
int do_read(int fd, int lba, int nblks, struct timeval *tvp,
struct esense_reply *erp)
{
struct scsi_req sr;
struct cdb_6 *c6p;
int err;
if (nblks > 256)
fatal("Too many blocks for read: %d", nblks);
if (nblks == 256)
nblks = 0;
bzero((char *)&sr, sizeof(sr));
c6p = (struct cdb_6 *)&sr.sr_cdb;
c6p->c6_opcode = C6OP_READ;
#ifdef i386
if (lba > 0) {
c6p->c6_lba0 = lba & 0xFF;
c6p->c6_lba1 = (lba >> 8) & 0xFF;
c6p->c6_lba2 = (lba >> 16) & 0xFF;
} else
c6p->c6_lba0 = c6p->c6_lba1 = c6p->c6_lba2 = 0;
#else
c6p->c6_lba = lba;
#endif
c6p->c6_len = nblks;
sr.sr_addr = NULL;
sr.sr_dma_max = 0; /* don't really do I/O to memory */
sr.sr_ioto = 5;
sr.sr_dma_dir = SR_DMA_RD;
err = ioctl(fd, SDIOCSRQ, &sr);
*tvp = sr.sr_exec_time;
if (sr.sr_dma_xfr != 0)
fatal("scsi driver did transfer: %d", sr.sr_dma_xfr);
*erp = sr.sr_esense;
return err | sr.sr_io_status;
}
int do_write(int fd, int lba, int nblks, struct timeval *tvp,
struct esense_reply *erp)
{
struct scsi_req sr;
struct cdb_6 *c6p;
int err;
if (nblks > 256)
fatal("Too many blocks for read: %d", nblks);
if (nblks == 256)
nblks = 0;
bzero((char *)&sr, sizeof(sr));
c6p = (struct cdb_6 *)&sr.sr_cdb;
c6p->c6_opcode = C6OP_WRITE;
#ifdef i386
if (lba > 0) {
c6p->c6_lba0 = lba & 0xFF;
c6p->c6_lba1 = (lba >> 8) & 0xFF;
c6p->c6_lba2 = (lba >> 16) & 0xFF;
} else
c6p->c6_lba0 = c6p->c6_lba1 = c6p->c6_lba2 = 0;
#else
c6p->c6_lba = lba;
#endif
c6p->c6_len = nblks;
sr.sr_addr = NULL;
sr.sr_dma_max = 0; /* don't really do I/O to memory */
sr.sr_ioto = 5;
sr.sr_dma_dir = SR_DMA_WR;
err = ioctl(fd, SDIOCSRQ, &sr);
*tvp = sr.sr_exec_time;
if (sr.sr_dma_xfr != 0)
fatal("scsi driver did transfer: %d", sr.sr_dma_xfr);
*erp = sr.sr_esense;
return err | sr.sr_io_status;
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.