Return to BSD News archive
Newsgroups: comp.unix.bsd Path: sserve!manuel!munnari.oz.au!uunet!spool.mu.edu!agate!tfs.com!tfs.com!julian From: julian@tfs.com (Julian Elischer) Subject: NEW SCSI SYSTEM (beta) part 3 of 4 (repost) Message-ID: <1992Sep18.050833.3217@tfs.com> Organization: TRW Financial Systems Date: Fri, 18 Sep 1992 05:08:33 GMT Lines: 1580 # This is a shell archive. Save it in a file, remove anything before # this line, and then unpack it by entering "sh file". Note, it may # create directories; files and directories will be owned by you and # have default permissions. # # This archive contains: # # scsi/sd.c # echo x - scsi/sd.c sed 's/^X//' >scsi/sd.c << 'END-of-scsi/sd.c' X/* X * Written by Julian Elischer (julian@tfs.com) X * for TRW Financial Systems for use under the MACH(2.5) operating system. X * X * TRW Financial Systems, in accordance with their agreement with Carnegie X * Mellon University, makes this software available to CMU to distribute X * or use in any manner that they see fit as long as this message is kept with X * the software. For this reason TFS also grants any other persons or X * organisations permission to use or modify this software. X * X * TFS supplies this software to be publicly redistributed X * on the understanding that TFS is not responsible for the correct X * functioning of this software in any circumstances. X * X */ X X/* X * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 X */ X X/* RCS header and log section X * $Log: $ X */ X#define SPLSD splbio X#define ESUCCESS 0 X#include <sd.h> X#include <sys/types.h> X#include "param.h" X#include "dkbad.h" X#include "systm.h" X#include "conf.h" X#include "file.h" X#include "stat.h" X#include "ioctl.h" X#include "buf.h" X#include "uio.h" X#include "malloc.h" X X#include <sys/errno.h> X/*#include <sys/user.h>*/ X#include <sys/disklabel.h> X#include <scsi/scsi.h> X#include <scsi/scsiconf.h> X Xlong int sdstrats,sdqueues; X X X#include <ddb.h> X#if NDDB > 0 Xint Debugger(); X#else NDDB > 0 X#define Debugger() X#endif NDDB > 0 X X X#define PAGESIZ 4096 X#define SECSIZE 512 X#define PDLOCATION 29 X#define BOOTRECORDSIGNATURE (0x55aa & 0x00ff) X#define SDOUTSTANDING 2 X#define SDQSIZE 4 X#define SD_RETRIES 4 X X#define wdunit(dev) ((minor(dev) & 0x38) >> 3) X#define wdpart(dev) (minor(dev) & 0x7) X#define MAKESDDEV(maj, unit, part) (makedev(maj,((unit<<3)+part))) X#define UNITSHIFT 3 X#define PARTITION(z) (minor(z) & 0x07) X#define RAW_PART 3 X#define UNIT(z) ( (minor(z) >> UNITSHIFT) ) X Xtypedef long paddr_t; X#define paddr(X) (paddr_t)(X->b_un.b_addr) X X#define B_MD1 0x10000000 X#define WHOLE_DISK(unit) ( (unit << UNITSHIFT) + RAW_PART ) X X/******************************************************************************/ X X#define ACTIVE 0x80 /* indicator of active partition */ X#define BOOT_MAGIC 0xAA55 /* signature of the boot record */ X X/******************************************************************************/ X X Xstruct buf sd_buf_queue[NSD]; Xint sd_done(); Xint sdstrategy(); X Xint sd_debug = 0; X Xstruct scsi_xfer sd_scsi_xfer[NSD][SDOUTSTANDING]; /* XXX */ Xstruct scsi_xfer *sd_free_xfer[NSD]; Xint sd_xfer_block_wait[NSD]; X Xstruct sd_data X{ X int flags; X#define SDVALID 0x02 /* PARAMS LOADED */ X#define SDINIT 0x04 /* device has been init'd */ X#define SDWAIT 0x08 /* device has someone waiting */ X#define SDHAVELABEL 0x10 /* have read the label */ X#define SDDOSPART 0x20 /* Have read the DOS partition table */ X#define SDWRITEPROT 0x40 /* Device in readonly mode (S/W)*/ X struct scsi_switch *sc_sw; /* address of scsi low level switch */ X int ctlr; /* so they know which one we want */ X int targ; /* our scsi target ID */ X int lu; /* out scsi lu */ X int cmdscount; /* cmds allowed outstanding by board*/ X int wlabel; /* label is writable */ X struct disk_parms X { X u_char heads; /* Number of heads */ X u_short cyls; /* Number of cylinders */ X u_char sectors;/*dubious*/ /* Number of sectors/track */ X u_short secsiz; /* Number of bytes/sector */ X u_long disksize; /* total number sectors */ X }params; X struct disklabel disklabel; X struct dos_partition dosparts[NDOSPART]; /* DOS view of disk */ X int partflags[MAXPARTITIONS]; /* per partition flags */ X#define SDOPEN 0x01 X int openparts; /* one bit for each open partition */ X unsigned int sd_start_of_unix; /* unix vs dos partitions */ X}sd_data[NSD]; X X#ifdef DEBUG Xsdx(char * str) X{ Xprintf("at %s\n",str); Xspinwait(2000); X} X#else X#define sdx(x) X#endif X X Xstatic int next_sd_unit = 0; X/***********************************************************************\ X* The routine called by the low level scsi routine when it discovers * X* A device suitable for this driver * X\***********************************************************************/ X Xint sdattach(ctlr,targ,lu,scsi_switch) Xstruct scsi_switch *scsi_switch; X{ X int unit,i; X unsigned char *tbl; X struct sd_data *sd; X struct disk_parms *dp; X X unit = next_sd_unit++; X sd = sd_data + unit; X dp = &(sd->params); X if(scsi_debug & PRINTROUTINES) printf("sdattach: "); X /*******************************************************\ X * Check we have the resources for another drive * X \*******************************************************/ X if( unit >= NSD) X { X printf("Too many scsi disks..(%d > %d) reconfigure kernel",(unit + 1),NSD); X return(0); X } X /*******************************************************\ X * Store information needed to contact our base driver * X \*******************************************************/ X sd->sc_sw = scsi_switch; X sd->ctlr = ctlr; X sd->targ = targ; X sd->lu = lu; X sd->cmdscount = SDOUTSTANDING; /* XXX (ask the board) */ X X X i = sd->cmdscount; X while(i-- ) X { X sd_scsi_xfer[unit][i].next = sd_free_xfer[unit]; X sd_free_xfer[unit] = &sd_scsi_xfer[unit][i]; X } X /*******************************************************\ X * Use the subdriver to request information regarding * X * the drive. We cannot use interrupts yet, so the * X * request must specify this. * X \*******************************************************/ X sd_get_parms(unit, SCSI_NOSLEEP | SCSI_NOMASK); X printf(" sd%d: %dMB, cyls %d, heads %d, secs %d, bytes/sec %d\n", X unit, X ( dp->cyls X * dp->heads X * dp->sectors X * dp->secsiz X ) X / (1024 * 1024), X dp->cyls, X dp->heads, X dp->sectors, X dp->secsiz); X /*******************************************************\ X * Set up the bufs for this device * X \*******************************************************/ X sd->flags |= SDINIT; X sd->flags |= SDVALID; X return; X X} X X X X/*******************************************************\ X* open the device. Make sure the partition info * X* is a up-to-date as can be. * X\*******************************************************/ Xsdopen(dev) X{ X int errcode = 0; X int unit, part; X struct disk_parms disk_parms; X struct sd_data *sd ; X X unit = UNIT(dev); X part = PARTITION(dev); X sd = sd_data + unit; X if(scsi_debug & (PRINTROUTINES | TRACEOPENS)) X printf("sdopen: dev=0x%x (unit %d (of %d),partition %d)\n" X , dev, unit, NSD, part); X /*******************************************************\ X * Check the unit is legal * X \*******************************************************/ X if ( unit >= NSD ) { X errcode = ENXIO; X goto open1; X } X /*******************************************************\ X * Make sure the disk has been initialised * X * At some point in the future, get the scsi driver * X * to look for a new device if we are not initted * X \*******************************************************/ X if (! (sd->flags & SDINIT)) X return(ENXIO); X X /*******************************************************\ X * If it's been invalidated, and not everybody has * X * closed it then forbid re-entry. * X \*******************************************************/ X if ((! (sd->flags & SDVALID)) X && ( sd->openparts)) X return(ENXIO); X /*******************************************************\ X * Check that it is still responding and ok. * X \*******************************************************/ X X if(scsi_debug & TRACEOPENS) X printf("device is "); X if (sd_req_sense(unit, 0) != 0) { X errcode = ENXIO; X if(scsi_debug & TRACEOPENS) X printf("not reponding\n"); X goto open1; X } X if(scsi_debug & TRACEOPENS) X printf("ok\n"); X /*******************************************************\ X * In case it is a funny one, tell it to start * X * not needed for hard drives * X \*******************************************************/ X /*sd_start_unit(unit); X if(scsi_debug & TRACEOPENS) X printf("started ");*/ X /*******************************************************\ X * Load the physical device parameters * X \*******************************************************/ X sd_get_parms(unit, 0); X if (sd->params.secsiz != SECSIZE) { X printf("sd%d: Can't deal with %d bytes logical blocks\n" X ,unit, sd->params.secsiz); X Debugger(); X errcode = ENXIO; X goto open1; X } X if(scsi_debug & TRACEOPENS) X printf("Params loaded "); X /*******************************************************\ X * Load the partition info if not already loaded * X \*******************************************************/ X sdgetdisklabel(unit); X if(scsi_debug & TRACEOPENS) X printf("Disklabel loaded "); X /*******************************************************\ X * Check the partition is legal * X \*******************************************************/ X if ( part >= MAXPARTITIONS ) { X errcode = ENXIO; X goto open1; X } X if(scsi_debug & TRACEOPENS) X printf("ok"); X /*******************************************************\ X * Check that the partition exists * X \*******************************************************/ X if (( sd->disklabel.d_partitions[part].p_fstype != FS_UNUSED ) X || (part == RAW_PART)) X { X sd->partflags[part] |= SDOPEN; X sd->openparts |= (1 << part); X if(scsi_debug & TRACEOPENS) X printf("open %d %d\n",sdstrats,sdqueues); X sd->flags |= SDVALID; X } X else X { X errcode = ENXIO; X } X Xopen1: X return(errcode); X} X X/*******************************************************\ X* Get ownership of a scsi_xfer * X* If need be, sleep on it, until it comes free * X\*******************************************************/ Xstruct scsi_xfer *sd_get_xs(unit,flags) Xint flags; Xint unit; X{ X struct scsi_xfer *xs; X int s; X X if(flags & (SCSI_NOSLEEP | SCSI_NOMASK)) X { X if (xs = sd_free_xfer[unit]) X { X sd_free_xfer[unit] = xs->next; X xs->flags = 0; X } X } X else X { X s = SPLSD(); X while (!(xs = sd_free_xfer[unit])) X { X sd_xfer_block_wait[unit]++; /* someone waiting! */ X sleep((caddr_t)&sd_free_xfer[unit], PRIBIO+1); X sd_xfer_block_wait[unit]--; X } X sd_free_xfer[unit] = xs->next; X splx(s); X xs->flags = 0; X } X return(xs); X} X X/*******************************************************\ X* Free a scsi_xfer, wake processes waiting for it * X\*******************************************************/ Xsd_free_xs(unit,xs,flags) Xstruct scsi_xfer *xs; Xint unit; Xint flags; X{ X int s; X X if(flags & SCSI_NOMASK) X { X if (sd_xfer_block_wait[unit]) X { X printf("doing a wakeup from NOMASK mode\n"); X wakeup((caddr_t)&sd_free_xfer[unit]); X } X xs->next = sd_free_xfer[unit]; X sd_free_xfer[unit] = xs; X } X else X { X s = SPLSD(); X if (sd_xfer_block_wait[unit]) X wakeup((caddr_t)&sd_free_xfer[unit]); X xs->next = sd_free_xfer[unit]; X sd_free_xfer[unit] = xs; X splx(s); X } X} X X/*******************************************************\ X* trim the size of the transfer if needed, * X* called by physio * X* basically the smaller of our max and the scsi driver's* X* minphys (note we have no max) * X\*******************************************************/ X/* Trim buffer length if buffer-size is bigger than page size */ Xvoid sdminphys(bp) Xstruct buf *bp; X{ X (*(sd_data[UNIT(bp->b_dev)].sc_sw->scsi_minphys))(bp); X} X X/*******************************************************\ X* Actually translate the requested transfer into * X* one the physical driver can understand * X* The transfer is described by a buf and will include * X* only one physical transfer. * X\*******************************************************/ X Xint sdstrategy(bp) Xstruct buf *bp; X{ X struct buf *dp; X unsigned int opri; X struct sd_data *sd ; X int unit; X Xsdx("Strat"); X sdstrats++; X unit = UNIT((bp->b_dev)); X sd = sd_data + unit; X if(scsi_debug & PRINTROUTINES) printf("\nsdstrategy "); X if(scsi_debug & SHOWREQUESTS) printf("sd%d: %d bytes @ blk%d\n", X unit,bp->b_bcount,bp->b_blkno); X sdminphys(bp); X /*******************************************************\ X * If the device has been made invalid, error out * X \*******************************************************/ X if(!(sd->flags & SDVALID)) X { X bp->b_error = EIO; X goto bad; X } X /*******************************************************\ X * "soft" write protect check * X \*******************************************************/ X if ((sd->flags & SDWRITEPROT) && (bp->b_flags & B_READ) == 0) { X bp->b_error = EROFS; X bp->b_flags |= B_ERROR; X goto done; X } X /*******************************************************\ X * If it's a null transfer, return immediatly * X \*******************************************************/ X if (bp->b_bcount == 0) { X goto done; X } X X /*******************************************************\ X * Decide which unit and partition we are talking about * X \*******************************************************/ Xsdx("A"); X if ((sd->flags & SDHAVELABEL) != 0 X && PARTITION(bp->b_dev) != RAW_PART) { X X /* X * do bounds checking, adjust transfer. if error, process. X * if end of partition, just return X */ X if (bounds_check_with_label(bp,&sd->disklabel,sd->wlabel) <= 0) X goto done; X /* otherwise, process transfer request */ X } Xsdx("B"); X X opri = SPLSD(); X dp = &sd_buf_queue[unit]; X X /*******************************************************\ X * Place it in the queue of disk activities for this disk* X \*******************************************************/ X disksort(dp, bp); X X /*******************************************************\ X * Tell the device to get going on the transfer if it's * X * not doing anything, otherwise just wait for completion* X \*******************************************************/ X sdstart(unit); X X splx(opri); X return; Xbad: X bp->b_flags |= B_ERROR; Xdone: Xsdx("C"); X X /*******************************************************\ X * Correctly set the buf to indicate a completed xfer * X \*******************************************************/ X bp->b_resid = bp->b_bcount; X biodone(bp); X return; X} X X/***************************************************************\ X* sdstart looks to see if there is a buf waiting for the device * X* and that the device is not already busy. If both are true, * X* It deques the buf and creates a scsi command to perform the * X* transfer in the buf. The transfer request will call sd_done * X* on completion, which will in turn call this routine again * X* so that the next queued transfer is performed. * X* The bufs are queued by the strategy routine (sdstrategy) * X* * X* This routine is also called after other non-queued requests * X* have been made of the scsi driver, to ensure that the queue * X* continues to be drained. * X* * X* must be called at the correct (highish) spl level * X\***************************************************************/ X/* sdstart() is called at SPLSD from sdstrategy and sd_done*/ Xsdstart(unit) Xint unit; X{ X int drivecount; X register struct buf *bp = 0; X register struct buf *dp; X struct scsi_xfer *xs; X union scsi_cmd cmd; X int blkno, nblk; X struct sd_data *sd = sd_data + unit; X struct partition *p ; X X if(scsi_debug & PRINTROUTINES) printf("sdstart%d ",unit); X /*******************************************************\ X * See if there is a buf to do and we are not already * X * doing one * X \*******************************************************/ X if(!sd_free_xfer[unit]) X { X return; /* none for us, unit already underway */ X } X X if(sd_xfer_block_wait[unit]) /* there is one, but a special waits */ X { X return; /* give the special that's waiting a chance to run */ X } X X X dp = &sd_buf_queue[unit]; X if ((bp = dp->b_actf) != NULL) /* yes, an assign */ X { X dp->b_actf = bp->av_forw; X } X else X { X return; X } X X xs=sd_get_xs(unit,0); /* ok we can grab it */ X xs->flags = INUSE; /* Now ours */ X X /*******************************************************\ X * We have a buf, now we should move the data into * X * a scsi_xfer definition and try start it * X \*******************************************************/ X /*******************************************************\ X * First, translate the block to absolute * X \*******************************************************/ X p = sd->disklabel.d_partitions + PARTITION(bp->b_dev); X blkno = bp->b_blkno + p->p_offset; X nblk = (bp->b_bcount + 511) >> 9; X X /*******************************************************\ X * Fill out the scsi command * X \*******************************************************/ X bzero(&cmd, sizeof(cmd)); X cmd.generic.opcode = (bp->b_flags & B_READ) X ? READ_BIG : WRITE_BIG; X cmd.rw_big.addr_3 = (blkno & 0xff000000) >> 24; X cmd.rw_big.addr_2 = (blkno & 0xff0000) >> 16; X cmd.rw_big.addr_1 = (blkno & 0xff00) >> 8; X cmd.rw_big.addr_0 = blkno & 0xff; X cmd.rw_big.length2 = (nblk & 0xff00) >> 8; X cmd.rw_big.length1 = (nblk & 0xff); X /*******************************************************\ X * Fill out the scsi_xfer structure * X * Note: we cannot sleep as we may be an interrupt * X \*******************************************************/ X xs->flags |= SCSI_NOSLEEP; X xs->adapter = sd->ctlr; X xs->targ = sd->targ; X xs->lu = sd->lu; X xs->retries = SD_RETRIES; X xs->timeout = 10000;/* 10000 millisecs for a disk !*/ X xs->cmd = &cmd; X xs->cmdlen = sizeof(struct scsi_rw_big); X xs->resid = bp->b_bcount; X xs->when_done = sd_done; X xs->done_arg = unit; X xs->done_arg2 = (int)xs; X xs->error = XS_NOERROR; X xs->bp = bp; X /*******************************************************\ X * If B_MD1 is set we are handling a UIO * X \*******************************************************/ X xs->data = (u_char *)bp->b_un.b_addr; X xs->datalen = bp->b_bcount; X if(bp->b_flags & B_MD1) X { X xs->flags |= SCSI_DATA_UIO; X } X /*******************************************************\ X * Pass all this info to the scsi driver. * X \*******************************************************/ X X X X if ( (*(sd->sc_sw->scsi_cmd))(xs) != SUCCESSFULLY_QUEUED) X { X printf("sd%d: oops not queued",unit); X xs->error = XS_DRIVER_STUFFUP; X sd_done(unit,xs); /* clean up (calls sdstart) */ X } X sdqueues++; X} X X/*******************************************************\ X* This routine is called by the scsi interrupt when * X* the transfer is complete. X\*******************************************************/ Xint sd_done(unit,xs) Xint unit; Xstruct scsi_xfer *xs; X{ X struct buf *bp; X int retval; X int retries = 0; X X if(scsi_debug & PRINTROUTINES) printf("sd_done%d ",unit); X if (! (xs->flags & INUSE)) X panic("scsi_xfer not in use!"); X if(bp = xs->bp) X { X switch(xs->error) X { X case XS_NOERROR: X bp->b_error = 0; X bp->b_resid = 0; X break; X X case XS_SENSE: X retval = (sd_interpret_sense(unit,xs)); X if(retval) X { X bp->b_flags |= B_ERROR; X bp->b_error = retval; X } X break; X X case XS_TIMEOUT: X printf("sd%d timeout\n",unit); X X case XS_BUSY: /* should retry */ /* how? */ X /************************************************/ X /* SHOULD put buf back at head of queue */ X /* and decrement retry count in (*xs) */ X /* HOWEVER, this should work as a kludge */ X /* -may not have a buf!- */ X /************************************************/ X if(xs->retries--) X { X xs->error = XS_NOERROR; X xs->flags &= ~ITSDONE; X if ( (*(sd_data[unit].sc_sw->scsi_cmd))(xs) X == SUCCESSFULLY_QUEUED) X { /* don't wake the job, ok? */ X return; X } X xs->flags |= ITSDONE; X } X X case XS_DRIVER_STUFFUP: X bp->b_flags |= B_ERROR; X bp->b_error = EIO; X break; X default: X printf("sd%d: unknown error category from scsi driver\n" X ,unit); X } X biodone(bp); X sd_free_xs(unit,xs,0); X sdstart(unit); /* If there's anything waiting.. do it */ X } X else /* special has finished */ X { X wakeup(xs); X } X} X/*******************************************************\ X* Perform special action on behalf of the user * X* Knows about the internals of this device * X\*******************************************************/ Xsdioctl(dev_t dev, int cmd, caddr_t addr, int flag) X{ X /* struct sd_cmd_buf *args;*/ X union scsi_cmd *scsi_cmd; X int error = 0; X unsigned int opri; X unsigned char unit, part; X register struct sd_data *sd; X X Xsdx("IOCTL"); X /*******************************************************\ X * Find the device that the user is talking about * X \*******************************************************/ X unit = UNIT(dev); X part = PARTITION(dev); X sd = &sd_data[unit]; X if(scsi_debug & PRINTROUTINES) printf("sdioctl%d ",unit); X X /*******************************************************\ X * If the device is not valid.. abandon ship * X \*******************************************************/ X if (!(sd_data[unit].flags & SDVALID)) X return(EIO); X switch(cmd) X { X#ifdef NOT_NOW X /*******************************************************\ X * This is a direct command for the scsi driver, pass * X * it on.. * X \*******************************************************/ X case A_CMD: X addr = (struct sd_cmd_buf *) addr; X sd_cmd(addr->byte[0], addr->byte[1], addr->byte, addr->byte[3], addr->byte[4], addr->byte[5], addr->byte[6], addr->byte[7]); X break; X /*******************************************************\ X * This is a low level scsi command ... just do it * X \*******************************************************/ X case A_SCSI: X /* will not work yet... fix it later.... X error = sd_scsi_cmd(UNIT(dev), X addr, X /* len */ 10, X ccb_data, X sizeof(ccb_data), X 10000, /* hope 10 sec is enough */ X 0); X break; X#endif NOT_NOW X X X case DIOCSBAD: Xsdx("DIOCSBAD"); X error = EINVAL; X break; X X case DIOCGDINFO: Xsdx("DIOCGDINFO"); X *(struct disklabel *)addr = sd->disklabel; X break; X X case DIOCGPART: Xsdx("DIOCGPART"); X ((struct partinfo *)addr)->disklab = &sd->disklabel; X ((struct partinfo *)addr)->part = X &sd->disklabel.d_partitions[PARTITION(dev)]; X break; X X case DIOCSDINFO: Xsdx("DIOCSDINFO"); X if ((flag & FWRITE) == 0) X error = EBADF; X else X error = setdisklabel(&sd->disklabel, X (struct disklabel *)addr, X /*(sd->flags & DKFL_BSDLABEL) ? sd->openparts : */0, X sd->dosparts); X if (error == 0) { X sd->flags |= SDHAVELABEL; X } X break; X X case DIOCWLABEL: Xsdx("DIOCWLABEL"); X sd->flags &= ~SDWRITEPROT; X if ((flag & FWRITE) == 0) X error = EBADF; X else X sd->wlabel = *(int *)addr; X break; X X case DIOCWDINFO: Xsdx("DIOCWDINFO"); X sd->flags &= ~SDWRITEPROT; X if ((flag & FWRITE) == 0) X error = EBADF; X else X { X if ((error = setdisklabel(&sd->disklabel X , (struct disklabel *)addr X , /*(sd->flags & SDHAVELABEL) ? sd->openparts :*/ 0 X , sd->dosparts)) == 0) X { X int wlab; X X sd->flags |= SDHAVELABEL; X X /* simulate opening partition 0 so write succeeds */ X sd->openparts |= (1 << 0); /* XXX */ X wlab = sd->wlabel; X sd->wlabel = 1; X error = writedisklabel(dev, sdstrategy, X &sd->disklabel, sd->dosparts); X sd->wlabel = wlab; X } X } X break; X X X default: Xsdx("default"); X error = ENOTTY; X break; X } Xsdx("done"); X return (error); X} X X X/*******************************************************\ X* Load the label information on the named device * X\*******************************************************/ Xint sdgetdisklabel(unit) Xunsigned char unit; X{ X /*unsigned int n, m;*/ X char *errstring; X struct dos_partition *dos_partition_p; X struct sd_data *sd = sd_data + unit; X X /*******************************************************\ X * If the inflo is already loaded, use it * X \*******************************************************/ X if(sd->flags & SDHAVELABEL) return; X X bzero(&sd->disklabel,sizeof(struct disklabel)); X /*******************************************************\ X * make partition 3 the whole disk in case of failure * X * then get pdinfo * X \*******************************************************/ X sd->disklabel.d_partitions[RAW_PART].p_offset = 0; X sd->disklabel.d_partitions[RAW_PART].p_size = sd->params.disksize; X sd->disklabel.d_npartitions = MAXPARTITIONS; X sd->disklabel.d_secsize = 512; /* as long as it's not 0 */ X sd->disklabel.d_secpercyl = 100; /* as long as it's not 0 */ X /* readdisklabel divides by it */ X X /*******************************************************\ X * all the generic bisklabel extraction routine * X \*******************************************************/ X if(errstring = readdisklabel(makedev(0 ,(unit<<UNITSHIFT )+3) X , sdstrategy X , &sd->disklabel X , sd->dosparts X , 0 X , 0)) X { X printf("sd%d: %s\n",unit, errstring); X return(ENXIO); X } X /*******************************************************\ X * leave partition 2 "open" for raw I/O * X \*******************************************************/ X X sd->flags |= SDHAVELABEL; /* WE HAVE IT ALL NOW */ X return(ESUCCESS); X} X X/*******************************************************\ X* Find out form the device what it's capacity is * X\*******************************************************/ Xsd_size(unit, flags) X{ X struct scsi_read_cap_data rdcap; X union scsi_cmd scsi_cmd; X int size; X X /*******************************************************\ X * make up a scsi command and ask the scsi driver to do * X * it for you. * X \*******************************************************/ X bzero(&scsi_cmd, sizeof(scsi_cmd)); X scsi_cmd.generic.opcode = READ_CAPACITY; X X /*******************************************************\ X * If the command works, interpret the result as a 4 byte* X * number of blocks * X \*******************************************************/ X if (sd_scsi_cmd(unit, X &scsi_cmd, X sizeof(struct scsi_read_capacity), X &rdcap, X sizeof(rdcap), X 2000, X flags) != 0) X { X printf("could not get size of unit %d\n", unit); X return(0); X } else { X size = rdcap.addr_0 + 1 ; X size += rdcap.addr_1 << 8; X size += rdcap.addr_2 << 16; X size += rdcap.addr_3 << 24; X } X return(size); X} X X/*******************************************************\ X* Check with the device that it is ok, (via scsi driver)* X\*******************************************************/ Xsd_req_sense(unit, flags) X{ X char out_buf[18]; X union scsi_cmd scsi_cmd; X X bzero(&scsi_cmd, sizeof(scsi_cmd)); X scsi_cmd.generic.opcode = REQUEST_SENSE; X scsi_cmd.sense.length = 18; X X if (sd_scsi_cmd(unit, X &scsi_cmd, X sizeof(struct scsi_sense), X out_buf, X sizeof(out_buf), X 2000, X flags) != 0) X { X return(ENXIO); X } X else X return(0); X} X X/*******************************************************\ X* Get scsi driver to send a "start up" command * X\*******************************************************/ Xsd_start_unit(unit) X{ X union scsi_cmd scsi_cmd; X char ccb_data[32]; X X bzero(&scsi_cmd, sizeof(scsi_cmd)); X scsi_cmd.generic.opcode = START_STOP; X scsi_cmd.start_stop.start = 1; X X if (sd_scsi_cmd(unit, X &scsi_cmd, X sizeof(struct scsi_start_stop), X ccb_data, X sizeof(ccb_data), X 2000, X 0) != 0) { X return(ENXIO); X } else X return(0); X} X X/*******************************************************\ X* Tell the device to map out a defective block * X\*******************************************************/ Xsd_reassign_blocks(unit,block) X{ X union scsi_cmd scsi_cmd; X struct scsi_reassign_blocks_data rbdata; X X X bzero(&scsi_cmd, sizeof(scsi_cmd)); X bzero(&rbdata, sizeof(rbdata)); X scsi_cmd.generic.opcode = REASSIGN_BLOCKS; X X rbdata.length_msb = 0; X rbdata.length_lsb = sizeof(rbdata.defect_descriptor[0]); X rbdata.defect_descriptor[0].dlbaddr_3 = ((block >> 24) & 0xff); X rbdata.defect_descriptor[0].dlbaddr_2 = ((block >> 16) & 0xff); X rbdata.defect_descriptor[0].dlbaddr_1 = ((block >> 8) & 0xff); X rbdata.defect_descriptor[0].dlbaddr_0 = ((block ) & 0xff); X X if (sd_scsi_cmd(unit, X &scsi_cmd, X sizeof(struct scsi_reassign_blocks), X &rbdata, X sizeof(rbdata), X 5000, X 0) != 0) { X return(ENXIO); X } else X return(0); X} X X X#define b2tol(a) (((unsigned)(a##_1) << 8) + (unsigned)a##_0 ) X X/*******************************************************\ X* Get the scsi driver to send a full inquiry to the * X* device and use the results to fill out the disk * X* parameter structure. * X\*******************************************************/ X Xint sd_get_parms(unit, flags) X{ X struct sd_data *sd = sd_data + unit; X struct disk_parms *disk_parms = &sd->params; X union scsi_cmd scsi_cmd; X struct scsi_mode_sense_data scsi_sense; X int sectors; X X /*******************************************************\ X * First check if we have it all loaded * X \*******************************************************/ X if(sd->flags & SDVALID) return(0); X /*******************************************************\ X * First do a mode sense page 3 * X \*******************************************************/ X bzero(&scsi_cmd, sizeof(scsi_cmd)); X scsi_cmd.generic.opcode = MODE_SENSE; X scsi_cmd.mode_sense.page_code = 3; X scsi_cmd.mode_sense.length = 0x24; X /*******************************************************\ X * do the command, but we don't need the results * X * just print them for our interest's sake * X \*******************************************************/ X if (sd_debug) X { X if (sd_scsi_cmd(unit, X &scsi_cmd, X sizeof(struct scsi_mode_sense), X &scsi_sense, X sizeof(scsi_sense), X 2000, X flags) != 0) X { X printf("could not mode sense (3) for unit %d\n", unit); X sd->flags &= ~SDVALID; /* invalidated held info */ X return(ENXIO); X } X printf("unit %d: %d trk/zone, %d alt_sec/zone, %d alt_trk/zone, %d alt_trk/lun\n", X unit, X b2tol(scsi_sense.params.pgcode_3.trk_z), X b2tol(scsi_sense.params.pgcode_3.alt_sec), X b2tol(scsi_sense.params.pgcode_3.alt_trk_z), X b2tol(scsi_sense.params.pgcode_3.alt_trk_v)); X printf(" %d sec/trk, %d bytes/sec, %d interleave, %d %d bytes/log_blk\n", X b2tol(scsi_sense.params.pgcode_3.ph_sec_t), X b2tol(scsi_sense.params.pgcode_3.bytes_s), X b2tol(scsi_sense.params.pgcode_3.interleave), X sd_size(unit, flags), X _3btol(&scsi_sense.blksz_2)); X } X X X /*******************************************************\ X * do a "mode sense page 4" * X \*******************************************************/ X bzero(&scsi_cmd, sizeof(scsi_cmd)); X scsi_cmd.generic.opcode = MODE_SENSE; X scsi_cmd.mode_sense.page_code = 4; X scsi_cmd.mode_sense.length = 0x20; X /*******************************************************\ X * If the command worked, use the results to fill out * X * the parameter structure * X \*******************************************************/ X if (sd_scsi_cmd(unit, X &scsi_cmd, X sizeof(struct scsi_mode_sense), X &scsi_sense, X sizeof(scsi_sense), X 2000, X flags) != 0) X { X printf("could not mode sense (4) for unit %d\n", unit); X sd->flags &= ~SDVALID; /* invalidated held info */ X return(ENXIO); X } X X if (sd_debug) X printf(" %d cyls, %d heads, %d precomp, %d red_write, %d land_zone\n", X _3btol(&scsi_sense.params.pgcode_4.ncyl_2), X scsi_sense.params.pgcode_4.nheads, X b2tol(scsi_sense.params.pgcode_4.st_cyl_wp), X b2tol(scsi_sense.params.pgcode_4.st_cyl_rwc), X b2tol(scsi_sense.params.pgcode_4.land_zone)); X X /*******************************************************\ X * give a number of sectors so that sec * trks * cyls * X * is <= disk_size * X \*******************************************************/ X disk_parms->heads = scsi_sense.params.pgcode_4.nheads; X disk_parms->cyls = _3btol(&scsi_sense.params.pgcode_4.ncyl_2); X disk_parms->secsiz = _3btol(&scsi_sense.blksz_2); /* logical block size */ X X sectors = sd_size(unit, flags) * disk_parms->secsiz; X sectors /= SECSIZE; X sectors /= disk_parms->cyls; X sectors /= disk_parms->heads; X disk_parms->sectors = sectors; /* dubious on SCSI*/ X disk_parms->secsiz = SECSIZE; X X sd->flags |= SDVALID; X return(0); X} X X/*******************************************************\ X* close the device.. only called if we are the LAST * X* occurence of an open device * X\*******************************************************/ Xsdclose(dev) Xdev_t dev; X{ X unsigned char unit, part; X unsigned int old_priority; X X unit = UNIT(dev); X part = PARTITION(dev); X sd_data[unit].partflags[part] &= ~SDOPEN; X sd_data[unit].openparts &= ~(1 << part); X return(0); X} X X/*******************************************************\ X* ask the scsi driver to perform a command for us. * X* Call it through the switch table, and tell it which * X* sub-unit we want, and what target and lu we wish to * X* talk to. Also tell it where to find the command * X* how long int is. * X* Also tell it where to read/write the data, and how * X* long the data is supposed to be * X\*******************************************************/ Xint sd_scsi_cmd(unit,scsi_cmd,cmdlen,data_addr,datalen,timeout,flags) X Xint unit,flags; Xunion scsi_cmd *scsi_cmd; Xint cmdlen; Xint timeout; Xu_char *data_addr; Xint datalen; X{ X struct scsi_xfer *xs; X int retval; X int s; X struct sd_data *sd = sd_data + unit; X X if(scsi_debug & PRINTROUTINES) printf("\nsd_scsi_cmd%d ",unit); X if(sd->sc_sw) /* If we have a scsi driver */ X { X xs = sd_get_xs(unit,flags); /* should wait unless booting */ X if(!xs) X { X printf("sd_scsi_cmd%d: controller busy" X " (this should never happen)\n",unit); X return(EBUSY); X } X xs->flags |= INUSE; X /*******************************************************\ X * Fill out the scsi_xfer structure * X \*******************************************************/ X xs->flags |= flags; X xs->adapter = sd->ctlr; X xs->targ = sd->targ; X xs->lu = sd->lu; X xs->retries = SD_RETRIES; X xs->timeout = timeout; X xs->cmd = scsi_cmd; X xs->cmdlen = cmdlen; X xs->data = data_addr; X xs->datalen = datalen; X xs->resid = datalen; X xs->when_done = (flags & SCSI_NOMASK) X ?(int (*)())0 X :sd_done; X xs->done_arg = unit; X xs->done_arg2 = (int)xs; Xretry: xs->error = XS_NOERROR; X xs->bp = 0; X retval = (*(sd->sc_sw->scsi_cmd))(xs); X switch(retval) X { X case SUCCESSFULLY_QUEUED: X while(!(xs->flags & ITSDONE)) X sleep(xs,PRIBIO+1); X X case HAD_ERROR: X /*printf("err = %d ",xs->error);*/ X switch(xs->error) X { X case XS_NOERROR: X retval = ESUCCESS; X break; X case XS_SENSE: X retval = (sd_interpret_sense(unit,xs)); X break; X case XS_DRIVER_STUFFUP: X retval = EIO; X break; X case XS_TIMEOUT: X if(xs->retries-- ) X { X xs->flags &= ~ITSDONE; X goto retry; X } X retval = EIO; X break; X case XS_BUSY: X if(xs->retries-- ) X { X xs->flags &= ~ITSDONE; X goto retry; X } X retval = EIO; X break; X default: X retval = EIO; X printf("sd%d: unknown error category from scsi driver\n" X ,unit); X } X break; X case COMPLETE: X retval = ESUCCESS; X break; X case TRY_AGAIN_LATER: X if(xs->retries-- ) X { X xs->flags &= ~ITSDONE; X goto retry; X } X retval = EIO; X break; X default: X retval = EIO; X } X sd_free_xs(unit,xs,flags); X sdstart(unit); /* check if anything is waiting fr the xs */ X } X else X { X printf("sd%d: not set up\n",unit); X return(EINVAL); X } X return(retval); X} X/***************************************************************\ X* Look at the returned sense and act on the error and detirmine * X* The unix error number to pass back... (0 = report no error) * X\***************************************************************/ X Xint sd_interpret_sense(unit,xs) Xint unit; Xstruct scsi_xfer *xs; X{ X struct scsi_sense_data *sense; X int key; X int silent; X X /***************************************************************\ X * If the flags say errs are ok, then always return ok. * X \***************************************************************/ X if (xs->flags & SCSI_ERR_OK) return(ESUCCESS); X silent = (xs->flags & SCSI_SILENT); X X sense = &(xs->sense); X switch(sense->error_class) X { X case 7: X { X key=sense->ext.extended.sense_key; X switch(key) X { X case 0x0: X return(ESUCCESS); X case 0x1: X if(!silent) X { X printf("sd%d: soft error(corrected) ", unit); X if(sense->valid) X { X printf("block no. %d (decimal)", X (sense->ext.extended.info[0] <<24), X (sense->ext.extended.info[1] <<16), X (sense->ext.extended.info[2] <<8), X (sense->ext.extended.info[3] )); X } X printf("\n"); X } X return(ESUCCESS); X case 0x2: X if(!silent)printf("sd%d: not ready\n ", X unit); X return(ENODEV); X case 0x3: X if(!silent) X { X printf("sd%d: medium error ", unit); X if(sense->valid) X { X printf("block no. %d (decimal)", X (sense->ext.extended.info[0] <<24), X (sense->ext.extended.info[1] <<16), X (sense->ext.extended.info[2] <<8), X (sense->ext.extended.info[3] )); X } X printf("\n"); X } X return(EIO); X case 0x4: X if(!silent)printf("sd%d: non-media hardware failure\n ", X unit); X return(EIO); X case 0x5: X if(!silent)printf("sd%d: illegal request\n ", X unit); X return(EINVAL); X case 0x6: X if((sd_data[unit].flags & SDVALID) X && (sd_data[unit].openparts)) X { X sd_data[unit].flags & ~(SDVALID | SDHAVELABEL); X if(!silent)printf("sd%d: Unit attention.\n ", X unit); X return(EIO); X } X return(ESUCCESS); X case 0x7: X if(!silent) X { X printf("sd%d: attempted protection violation ", X unit); X if(sense->valid) X { X printf("block no. %d (decimal)\n", X (sense->ext.extended.info[0] <<24), X (sense->ext.extended.info[1] <<16), X (sense->ext.extended.info[2] <<8), X (sense->ext.extended.info[3] )); X } X printf("\n"); X } X return(EACCES); X case 0x8: X if(!silent) X { X printf("sd%d: block wrong state (worm)\n ", X unit); X if(sense->valid) X { X printf("block no. %d (decimal)\n", X (sense->ext.extended.info[0] <<24), X (sense->ext.extended.info[1] <<16), X (sense->ext.extended.info[2] <<8), X (sense->ext.extended.info[3] )); X } X printf("\n"); X } X return(EIO); X case 0x9: X if(!silent)printf("sd%d: vendor unique\n", X unit); X return(EIO); X case 0xa: X if(!silent)printf("sd%d: copy aborted\n ", X unit); X return(EIO); X case 0xb: X if(!silent)printf("sd%d: command aborted\n ", X unit); X return(EIO); X case 0xc: X if(!silent) X { X printf("sd%d: search returned\n ", X unit); X if(sense->valid) X { X printf("block no. %d (decimal)\n", X (sense->ext.extended.info[0] <<24), X (sense->ext.extended.info[1] <<16), X (sense->ext.extended.info[2] <<8), X (sense->ext.extended.info[3] )); X } X printf("\n"); X } X return(ESUCCESS); X case 0xd: X if(!silent)printf("sd%d: volume overflow\n ", X unit); X return(ENOSPC); X case 0xe: X if(!silent) X { X printf("sd%d: verify miscompare\n ", X unit); X if(sense->valid) X { X printf("block no. %d (decimal)\n", X (sense->ext.extended.info[0] <<24), X (sense->ext.extended.info[1] <<16), X (sense->ext.extended.info[2] <<8), X (sense->ext.extended.info[3] )); X } X printf("\n"); X } X return(EIO); X case 0xf: X if(!silent)printf("sd%d: unknown error key\n ", X unit); X return(EIO); X } X break; X } X case 0: X case 1: X case 2: X case 3: X case 4: X case 5: X case 6: X { X if(!silent)printf("sd%d: error class %d code %d\n", X unit, X sense->error_class, X sense->error_code); X if(sense->valid) X if(!silent)printf("block no. %d (decimal)\n", X (sense->ext.unextended.blockhi <<16), X + (sense->ext.unextended.blockmed <<8), X + (sense->ext.unextended.blocklow )); X } X return(EIO); X } X} X X X X Xint Xsdsize(dev_t dev) X{ X int unit = UNIT(dev), part = PARTITION(dev), val; X struct sd_data *sd; X X if (unit >= NSD) X return(-1); X X sd = &sd_data[unit]; X if((sd->flags & SDINIT) == 0) return(-1); X if (sd == 0 || (sd->flags & SDHAVELABEL) == 0) X val = sdopen (MAKESDDEV(major(dev), unit, RAW_PART), FREAD, S_IFBLK, 0); X if ( val != 0 || sd->flags & SDWRITEPROT) X return (-1); X X return((int)sd->disklabel.d_partitions[part].p_size); X} X Xsddump() X{ X printf("sddump() -- not implemented\n"); X return(-1); X} X X X X/* X * from:@(#)wd.c 7.2 (Berkeley) 5/9/91 X */ X X/* TODO:peel out buffer at low ipl, speed improvement */ X X X#if NWD > 0 X X#include "machine/cpu.h" X#include "i386/isa/isa_device.h" X#include "i386/isa/icu.h" X#include "i386/isa/wdreg.h" X#include "syslog.h" X#include "vm/vm.h" X X X#define wdnoreloc(dev) (minor(dev) & 0x80) /* ignore partition table */ X#define wddospart(dev) (minor(dev) & 0x40) /* use dos partitions */ X#define WDRAW 3 /* 'd' partition isn't a partition! */ X X#define b_cylin b_resid /* cylinder number for doing IO to */ X /* shares an entry in the buf struct */ X Xwdattach(dev) X{ X du = wddrives[unit] = (struct disk *) X malloc (sizeof(struct disk), M_TEMP, M_NOWAIT); X du->dk_unit = unit; X du->dk_port = dvp->id_iobase; X} X X Xextern char *vmmap; /* poor name! */ X Xint Xwddump(dev_t dev) /* dump core after a system crash */ X{ X register struct disk *sd; /* disk unit to do the IO */ X register struct bt_bad *bt_ptr; X long num; /* number of sectors to write */ X int unit, part, wdc; X long blkoff, blknum, blkcnt; X long cylin, head, sector, stat; X long secpertrk, secpercyl, nblocks, i; X char *addr; X extern int Maxmem; X static wddoingadump = 0 ; X extern caddr_t CADDR1; X X addr = (char *) 0; /* starting address */ X X /* toss any characters present prior to dump */ X while (sgetc(1)) X ; X X /* size of memory to dump */ X num = Maxmem; X unit = wdunit(dev); /* eventually support floppies? */ X part = PARTITION(dev); /* file system */ X /* check for acceptable drive number */ X if (unit >= _NWD) return(ENXIO); X X sd = wddrives[unit]; X if (sd == 0) return(ENXIO); X /* was it ever initialized ? */ X if (sd->dk_state < OPEN) return (ENXIO) ; X if (sd->flags & DKFL_WRITEPROT) return(ENXIO); X wdc = sd->dk_port; X X /* Convert to disk sectors */ X num = (u_long) num * NBPG / sd->disklabel.d_secsize; X X /* check if controller active */ X /*if (wdtab.b_active) return(EFAULT); */ X if (wddoingadump) return(EFAULT); X X nblocks = sd->disklabel.d_partitions[part].p_size; X blkoff = sd->disklabel.d_partitions[part].p_offset; X X/*pg("xunit %x, nblocks %d, dumplo %d num %d\n", part,nblocks,dumplo,num);*/ X /* check transfer bounds against partition size */ X if ((dumplo < 0) || ((dumplo + num) > nblocks)) X return(EINVAL); X X /*wdtab.b_active = 1; /* mark controller active for if we X panic during the dump */ X wddoingadump = 1 ; i = 100000 ; X X blknum = dumplo + blkoff; X while (num > 0) { X pmap_enter(kernel_pmap, CADDR1, trunc_page(addr), VM_PROT_READ, TRUE); X X X /* error check the xfer */ X X if ((unsigned)addr % (1024*1024) == 0) printf("%d ", num/2048) ; X /* update block count */ X num--; X blknum++ ; X (int) addr += 512; X X /* operator aborting dump? */ X if (sgetc(1)) X return(EINTR); X } X return(0); X} X#endif X X END-of-scsi/sd.c exit