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.