ftp.nice.ch/pub/next/tools/cdrom/mCD.971026.s.tar.gz#/mCD/scsi_cd.subproj/scsi_commands.c

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.