/* * Ported for use with the UltraStor 14f by Gary Close (gclose@wvnvms.wvnet.edu) * Thanks to Julian Elischer for advice and help with this port. * * Written by Julian Elischer (julian@tfs.com) * for TRW Financial Systems for use under the MACH(2.5) operating system. * * TRW Financial Systems, in accordance with their agreement with Carnegie * Mellon University, makes this software available to CMU to distribute * or use in any manner that they see fit as long as this message is kept with * the software. For this reason TFS also grants any other persons or * organisations permission to use or modify this software. * * TFS supplies this software to be publicly redistributed * on the understanding that TFS is not responsible for the correct * functioning of this software in any circumstances. * * $Id: ultra14f.c,v 1.18 1994/01/21 06:26:00 glass Exp $ */ #include #include #include #include #include #include #include #include #include #ifdef MACH /* EITHER CMU OR OSF */ #include #include #include #ifdef OSF /* OSF ONLY */ #include #include #include #include #else OSF /* CMU ONLY */ #include #include #endif OSF #endif MACH /* end of MACH specific */ #ifdef __NetBSD__ /* NetBSD specific */ #define isa_dev isa_device #define dev_unit id_unit #define dev_addr id_iobase #include #include #include #include #endif __NetBSD__ /* */ #ifdef DDB int Debugger(); #else DDB #define Debugger() panic("should call debugger here") #endif /* DDB */ #ifdef MACH int Debugger(); #endif MACH typedef struct {unsigned char addr[4]; } physaddr; typedef struct {unsigned char len[4]; } physlen; #ifdef MACH extern physaddr kvtophys(); #define PHYSTOKV(x) phystokv(x) #define KVTOPHYS(x) kvtophys(x) #endif MACH #ifdef __NetBSD__ #define PHYSTOKV(x) ((x) + KERNBASE) #define KVTOPHYS(x) vtophys(x) #endif __NetBSD__ extern int delaycount; /* from clock setup code */ #define NUM_CONCURRENT 16 /* number of concurrent ops per board */ #define UHA_NSEG 33 /* number of dma segments supported */ #define FUDGE(X) (X>>1) /* our loops are slower than spinwait() */ /* */ /************************** board definitions *******************************/ /* * I/O Port Interface */ #define UHA_LMASK (0x000) /* local doorbell mask reg */ #define UHA_LINT (0x001) /* local doorbell int/stat reg */ #define UHA_SMASK (0x002) /* system doorbell mask reg */ #define UHA_SINT (0x003) /* system doorbell int/stat reg */ #define UHA_ID0 (0x004) /* product id reg 0 */ #define UHA_ID1 (0x005) /* product id reg 1 */ #define UHA_CONF1 (0x006) /* config reg 1 */ #define UHA_CONF2 (0x007) /* config reg 2 */ #define UHA_OGM0 (0x008) /* outgoing mail ptr 0 least sig */ #define UHA_OGM1 (0x009) /* outgoing mail ptr 1 least mid */ #define UHA_OGM2 (0x00a) /* outgoing mail ptr 2 most mid */ #define UHA_OGM3 (0x00b) /* outgoing mail ptr 3 most sig */ #define UHA_ICM0 (0x00c) /* incoming mail ptr 0 */ #define UHA_ICM1 (0x00d) /* incoming mail ptr 1 */ #define UHA_ICM2 (0x00e) /* incoming mail ptr 2 */ #define UHA_ICM3 (0x00f) /* incoming mail ptr 3 */ /* * UHA_LMASK bits (read only) */ #define UHA_LDIE 0x80 /* local doorbell int enabled */ #define UHA_SRSTE 0x40 /* soft reset enabled */ #define UHA_ABORTEN 0x10 /* abort MSCP enabled */ #define UHA_OGMINTEN 0x01 /* outgoing mail interrupt enabled */ /* * UHA_LINT bits (read) */ #define UHA_LDIP 0x80 /* local doorbell int pending */ /* * UHA_LINT bits (write) */ #define UHA_ADRST 0x40 /* adapter soft reset */ #define UHA_SBRST 0x20 /* scsi bus reset */ #define UHA_ASRST 0x60 /* adapter and scsi reset */ #define UHA_ABORT 0x10 /* abort MSCP */ #define UHA_OGMINT 0x01 /* tell adapter to get mail */ /* * UHA_SMASK bits (read) */ #define UHA_SINTEN 0x80 /* system doorbell interupt Enabled */ #define UHA_ABORT_COMPLETE_EN 0x10 /* abort MSCP command complete int Enabled */ #define UHA_ICM_ENABLED 0x01 /* ICM interrupt enabled /* * UHA_SMASK bits (write) */ #define UHA_ENSINT 0x80 /* enable system doorbell interrupt */ #define UHA_EN_ABORT_COMPLETE 0x10 /* enable abort MSCP complete int */ #define UHA_ENICM 0x01 /* enable ICM interrupt */ /* * UHA_SINT bits (read) */ #define UHA_SINTP 0x80 /* system doorbell int pending */ #define UHA_ABORT_SUCC 0x10 /* abort MSCP successful */ #define UHA_ABORT_FAIL 0x18 /* abort MSCP failed */ /* * UHA_SINT bits (write) */ #define UHA_ABORT_ACK 0x18 /* acknowledge status and clear */ #define UHA_ICM_ACK 0x01 /* acknowledge ICM and clear */ /* * UHA_CONF1 bits (read only) */ #define UHA_DMA_CH5 0x00 /* DMA channel 5 */ #define UHA_DMA_CH6 0x40 /* 6 */ #define UHA_DMA_CH7 0x80 /* 7 */ #define UHA_IRQ15 0x00 /* IRQ 15 */ #define UHA_IRQ14 0x10 /* 14 */ #define UHA_IRQ11 0x20 /* 11 */ #define UHA_IRQ10 0x30 /* 10 */ /*********************************** * ha_status error codes \***********************************/ #define UHA_NO_ERR 0x00 /* No error supposedly */ #define UHA_SBUS_ABORT_ERR 0x84 /* scsi bus abort error */ #define UHA_SBUS_TIMEOUT 0x91 /* scsi bus selection timeout */ #define UHA_SBUS_OVER_UNDER 0x92 /* scsi bus over/underrun */ #define UHA_BAD_SCSI_CMD 0x96 /* illegal scsi command */ #define UHA_AUTO_SENSE_ERR 0x9b /* auto request sense err */ #define UHA_SBUS_RES_ERR 0xa3 /* scsi bus reset error */ #define UHA_BAD_SG_LIST 0xff /* invalid scatter gath list */ /* */ struct uha_dma_seg { physaddr addr; physlen len; }; /* */ struct mscp { unsigned char opcode:3; #define U14_HAC 0x01 /*host adapter command*/ #define U14_TSP 0x02 /*target scsi pass through command*/ #define U14_SDR 0x04 /*scsi device reset*/ unsigned char xdir:2; /*xfer direction*/ #define U14_SDET 0x00 /*determined by scsi command*/ #define U14_SDIN 0x01 /*scsi data in*/ #define U14_SDOUT 0x02 /*scsi data out*/ #define U14_NODATA 0x03 /*no data xfer*/ unsigned char dcn:1; /*disable disconnect for this command*/ unsigned char ca:1; /*Cache control*/ unsigned char sgth:1; /*scatter gather flag*/ unsigned char target:3; unsigned char chan:2; /*scsi channel (always 0 for 14f)*/ unsigned char lun:3; physaddr data; physlen datalen; physaddr link; unsigned char link_id; unsigned char sg_num; /*number of scat gath segs */ /*in s-g list if sg flag is*/ /*set. starts at 1, 8bytes per*/ unsigned char senselen; unsigned char cdblen; unsigned char cdb[12]; unsigned char ha_status; unsigned char targ_status; physaddr sense; /* if 0 no auto sense */ /*-----------------end of hardware supported fields----------------*/ struct mscp *next; /* in free list */ struct scsi_xfer *xs; /* the scsi_xfer for this cmd */ long int delta; /* difference from previous*/ struct mscp *later,*sooner; int flags; #define MSCP_FREE 0 #define MSCP_ACTIVE 1 #define MSCP_ABORTED 2 struct uha_dma_seg uha_dma[UHA_NSEG]; struct scsi_sense_data mscp_sense; }; struct mscp *uha_soonest = (struct mscp *)0; struct mscp *uha_latest = (struct mscp *)0; long int uha_furtherest = 0; /* longest time in the timeout queue */ /* */ struct uha_data { int flags; #define UHA_INIT 0x01; int baseport; struct mscp mscps[NUM_CONCURRENT]; struct mscp *free_mscp; int our_id; /* our scsi id */ int vect; int dma; } uha_data[NUHA]; int uhaprobe(); int uha_attach(); int uhaintr(); int uha_scsi_cmd(); int uha_timeout(); int uha_abort(); struct mscp *cheat; void uhaminphys(); long int uha_adapter_info(); unsigned long int scratch; #ifdef MACH struct isa_driver uhadriver = { uhaprobe, 0, uha_attach, "uha", 0, 0, 0}; int (*uhaintrs[])() = {uhaintr, 0}; #endif MACH #ifdef __NetBSD__ struct isa_driver uhadriver = { uhaprobe, uha_attach, "uha"}; #endif __NetBSD__ static uha_unit = 0; int uha_debug = 0; #define UHA_SHOWMSCPS 0x01 #define UHA_SHOWINTS 0x02 #define UHA_SHOWCMDS 0x04 #define UHA_SHOWMISC 0x08 #define FAIL 1 #define SUCCESS 0 #define PAGESIZ 4096 struct scsi_switch uha_switch[NUHA]; /* */ /***********************************************************************\ * Function to send a command out through a mailbox * \***********************************************************************/ uha_send_mbox( int unit ,struct mscp *mscp) { int port = uha_data[unit].baseport; int spincount = FUDGE(delaycount) * 1000; /* 1s should be enough */ int s = splbio(); while( ((inb(port + UHA_LINT) & (UHA_LDIP)) != (0)) && (spincount--)); if(spincount == -1) { printf("uha%d: board not responding\n",unit); Debugger(); } outl(port + UHA_OGM0,KVTOPHYS(mscp)); outb(port + UHA_LINT, (UHA_OGMINT)); splx(s); } /***********************************************************************\ * Function to send abort to 14f * \***********************************************************************/ uha_abort( int unit ,struct mscp *mscp) { int port = uha_data[unit].baseport; int spincount = FUDGE(delaycount) * 1; int abortcount = FUDGE(delaycount) * 2000; int s = splbio(); while(((inb(port + UHA_LINT) & (UHA_LDIP)) != (0)) && (spincount--)); if(spincount == -1); { printf("uha%d: board not responding\n",unit); Debugger(); } outl(port + UHA_OGM0,KVTOPHYS(mscp)); outb(port + UHA_LINT,UHA_ABORT); while((abortcount--) && (!(inb(port + UHA_SINT) & UHA_ABORT_FAIL))); if(abortcount == -1) { printf("uha%d: board not responding\n",unit); Debugger(); } if((inb(port + UHA_SINT) & 0x10) != 0) { outb(port + UHA_SINT,UHA_ABORT_ACK); return(1); } else { outb(port + UHA_SINT,UHA_ABORT_ACK); return(0); } } /***********************************************************************\ * Function to poll for command completion when in poll mode * \***********************************************************************/ uha_poll(int unit ,int wait) /* in msec */ { int port = uha_data[unit].baseport; int spincount = FUDGE(delaycount) * wait; /* in msec */ int stport = port + UHA_SINT; int start = spincount; retry: while( (spincount--) && (!(inb(stport) & UHA_SINTP))); if(spincount == -1) { printf("uha%d: board not responding\n",unit); return(EIO); } if ((int)cheat != PHYSTOKV(inl(port + UHA_ICM0))) { printf("discarding %x ",inl(port + UHA_ICM0)); outb(port + UHA_SINT, UHA_ICM_ACK); spinwait(50); goto retry; }/* don't know this will work */ uhaintr(unit); return(0); } /*******************************************************\ * Check if the device can be found at the port given * * and if so, set it up ready for further work * * as an argument, takes the isa_dev structure from * * autoconf.c * \*******************************************************/ uhaprobe(dev) struct isa_dev *dev; { int unit = uha_unit; dev->dev_unit = unit; uha_data[unit].baseport = dev->dev_addr; if(unit >= NUHA) { printf("uha: unit number (%d) too high\n",unit); return(0); } /*try and initialize unit at this location*/ if (uha_init(unit) != 0) { return(0); } /* if its there put in it's interrupt and DRQ vectors */ dev->id_irq = (1 << uha_data[unit].vect); dev->id_drq = uha_data[unit].dma; uha_unit ++; return(8); } /***********************************************\ * Attach all the sub-devices we can find * \***********************************************/ uha_attach(dev) struct isa_dev *dev; { static int firsttime; static int firstswitch[NUHA]; int masunit = dev->id_masunit; int r; if (!firstswitch[masunit]) { firstswitch[masunit] = 1; uha_switch[masunit].name = "uha"; uha_switch[masunit].scsi_cmd = uha_scsi_cmd; uha_switch[masunit].scsi_minphys = uhaminphys; uha_switch[masunit].open_target_lu = 0; uha_switch[masunit].close_target_lu = 0; uha_switch[masunit].adapter_info = uha_adapter_info; for (r = 0; r < 8; r++) { uha_switch[masunit].empty[r] = 0; uha_switch[masunit].used[r] = 0; uha_switch[masunit].printed[r] = 0; } } r = scsi_attach(masunit, uha_data[masunit].our_id, &uha_switch[masunit], &dev->id_physid, &dev->id_unit, dev->id_flags); /* only one for all boards */ if(firsttime==0) { firsttime = 1; uha_timeout(0); } return r; } /***********************************************\ * Return some information to the caller about * * the adapter and it's capabilities * \***********************************************/ long int uha_adapter_info(unit) int unit; { return(2); /* 2 outstanding requests at a time per device */ } /***********************************************\ * Catch an interrupt from the adaptor * \***********************************************/ uhaintr(unit) { struct mscp *mscp; u_char uhastat; unsigned long int mboxval; int port = uha_data[unit].baseport; if(scsi_debug & PRINTROUTINES) printf("uhaintr "); #if defined(OSF) if (!uha_attached[unit]) { return(1); } #endif /* defined(OSF) */ while(inb(port + UHA_SINT) & UHA_SINTP) { /***********************************************\ * First get all the information and then * * acknowlege the interrupt * \***********************************************/ uhastat = inb(port + UHA_SINT); mboxval = inl(port + UHA_ICM0); outb(port + UHA_SINT,UHA_ICM_ACK); if(scsi_debug & TRACEINTERRUPTS) printf("status = 0x%x ",uhastat); /***********************************************\ * Process the completed operation * \***********************************************/ mscp = (struct mscp *)(PHYSTOKV(mboxval)); if(uha_debug & UHA_SHOWCMDS ) { uha_show_scsi_cmd(mscp->xs); } if((uha_debug & UHA_SHOWMSCPS) && mscp) printf("",mscp); uha_remove_timeout(mscp); uha_done(unit,mscp); } return(1); } /***********************************************\ * We have a mscp which has been processed by the * * adaptor, now we look to see how the operation * * went. * \***********************************************/ uha_done(unit,mscp) int unit; struct mscp *mscp; { struct scsi_sense_data *s1,*s2; struct scsi_xfer *xs = mscp->xs; if(scsi_debug & (PRINTROUTINES | TRACEINTERRUPTS)) printf("uha_done "); /***********************************************\ * Otherwise, put the results of the operation * * into the xfer and call whoever started it * \***********************************************/ if ( (mscp->ha_status == UHA_NO_ERR) || (xs->flags & SCSI_ERR_OK)) { /* All went correctly OR errors expected */ xs->resid = 0; xs->error = 0; } else { s1 = &(mscp->mscp_sense); s2 = &(xs->sense); if(mscp->ha_status != UHA_NO_ERR) { switch(mscp->ha_status) { case UHA_SBUS_TIMEOUT: /* No response */ if (uha_debug & UHA_SHOWMISC) { printf("timeout reported back\n"); } xs->error = XS_TIMEOUT; break; case UHA_SBUS_OVER_UNDER: if (uha_debug & UHA_SHOWMISC) { printf("scsi bus xfer over/underrun\n"); } xs->error = XS_DRIVER_STUFFUP; break; case UHA_BAD_SG_LIST: if (uha_debug & UHA_SHOWMISC) { printf("bad sg list reported back\n"); } xs->error = XS_DRIVER_STUFFUP; break; default: /* Other scsi protocol messes */ xs->error = XS_DRIVER_STUFFUP; if (uha_debug & UHA_SHOWMISC) { printf("unexpected ha_status: %x\n", mscp->ha_status); } } } else { if (mscp->targ_status != 0) /**************************************************************************\ * I have no information for any possible value of target status field * * other than 0 means no error!! So I guess any error is unexpected in that * * event!! * \**************************************************************************/ { if (uha_debug & UHA_SHOWMISC) { printf("unexpected targ_status: %x\n", mscp->targ_status); } xs->error = XS_DRIVER_STUFFUP; } } } done: xs->flags |= ITSDONE; uha_free_mscp(unit,mscp, xs->flags); if(xs->when_done) (*(xs->when_done))(xs->done_arg,xs->done_arg2); } /***********************************************\ * A mscp (and hence a mbx-out is put onto the * * free list. * \***********************************************/ uha_free_mscp(unit,mscp, flags) struct mscp *mscp; { unsigned int opri; if(scsi_debug & PRINTROUTINES) printf("mscp%d(0x%x)> ",unit,flags); if (!(flags & SCSI_NOMASK)) opri = splbio(); mscp->next = uha_data[unit].free_mscp; uha_data[unit].free_mscp = mscp; mscp->flags = MSCP_FREE; /***********************************************\ * If there were none, wake abybody waiting for * * one to come free, starting with queued entries* \***********************************************/ if (!mscp->next) { wakeup((caddr_t) &uha_data[unit].free_mscp); } if (!(flags & SCSI_NOMASK)) splx(opri); } /***********************************************\ * Get a free mscp (and hence mbox-out entry) * \***********************************************/ struct mscp * uha_get_mscp(unit,flags) { unsigned opri; struct mscp *rc; if(scsi_debug & PRINTROUTINES) printf("next; rc->flags = MSCP_ACTIVE; } if (!(flags & SCSI_NOMASK)) splx(opri); return(rc); } /***********************************************\ * Start the board, ready for normal operation * \***********************************************/ uha_init(unit) int unit; { unsigned char ad[4]; volatile unsigned char model; volatile unsigned char submodel; unsigned char config_reg1; unsigned char config_reg2; unsigned char dma_ch; unsigned char irq_ch; unsigned char uha_id; int port = uha_data[unit].baseport; int i; int resetcount = FUDGE(delaycount) * 4000; model = inb(port + UHA_ID0); submodel = inb(port + UHA_ID1); if ((model != 0x56) & (submodel != 0x40)) { /* printf("ultrastor 14f not responding\n"); */ return(ENXIO); } printf("uha%d reading board settings, ",unit); config_reg1 = inb(port + UHA_CONF1); config_reg2 = inb(port + UHA_CONF2); dma_ch = (config_reg1 & 0xc0); irq_ch = (config_reg1 & 0x30); uha_id = (config_reg2 & 0x07); switch(dma_ch) { case UHA_DMA_CH5: uha_data[unit].dma = 5; printf("dma=5 "); break; case UHA_DMA_CH6: uha_data[unit].dma = 6; printf("dma=6 "); break; case UHA_DMA_CH7: uha_data[unit].dma = 7; printf("dma=7 "); break; default: printf("illegal dma jumper setting\n"); return(EIO); } switch(irq_ch) { case UHA_IRQ10: uha_data[unit].vect = 10; printf("int=10 "); break; case UHA_IRQ11: uha_data[unit].vect = 11; printf("int=11 "); break; case UHA_IRQ14: uha_data[unit].vect = 14; printf("int=14 "); break; case UHA_IRQ15: uha_data[unit].vect = 15; printf("int=15 "); break; default: printf("illegal int jumper setting\n"); return(EIO); } /* who are we on the scsi bus */ printf("id=%x\n",uha_id); uha_data[unit].our_id = uha_id; /***********************************************\ * link up all our MSCPs into a free list * \***********************************************/ for (i=0; i < NUM_CONCURRENT; i++) { uha_data[unit].mscps[i].next = uha_data[unit].free_mscp; uha_data[unit].free_mscp = &uha_data[unit].mscps[i]; uha_data[unit].free_mscp->flags = MSCP_FREE; } /***********************************************\ * Note that we are going and return (to probe) * \***********************************************/ outb(port + UHA_LINT, UHA_ASRST); while( (resetcount--) && (!(inb(port + UHA_LINT)))); if(resetcount == -1) { printf("uha%d: board timed out during reset\n",unit); return(ENXIO); } outb(port + UHA_SMASK, 0x81); /* make sure interrupts are enabled */ uha_data[unit].flags |= UHA_INIT; return(0); } #ifndef min #define min(x,y) (x < y ? x : y) #endif min void uhaminphys(bp) struct buf *bp; { #ifdef MACH #if !defined(OSF) bp->b_flags |= B_NPAGES; /* can support scat/gather */ #endif /* defined(OSF) */ #endif MACH if(bp->b_bcount > ((UHA_NSEG-1) * PAGESIZ)) { bp->b_bcount = ((UHA_NSEG-1) * PAGESIZ); } } /***********************************************\ * start a scsi operation given the command and * * the data address. Also needs the unit, target * * and lu * \***********************************************/ int uha_scsi_cmd(xs) struct scsi_xfer *xs; { struct scsi_sense_data *s1,*s2; struct mscp *mscp; struct uha_dma_seg *sg; int seg; /* scatter gather seg being worked on */ int i = 0; int rc = 0; int thiskv; unsigned long int thisphys,nextphys; int unit =xs->adapter; int bytes_this_seg,bytes_this_page,datalen,flags; struct iovec *iovp; int s; unsigned int stat; int port = uha_data[unit].baseport; unsigned long int templen; if(scsi_debug & PRINTROUTINES) printf("uha_scsi_cmd "); /***********************************************\ * get a mscp (mbox-out) to use. If the transfer * * is from a buf (possibly from interrupt time) * * then we can't allow it to sleep * \***********************************************/ flags = xs->flags; if(xs->bp) flags |= (SCSI_NOSLEEP); /* just to be sure */ if(flags & ITSDONE) { printf("Already done?"); xs->flags &= ~ITSDONE; } if(!(flags & INUSE)) { printf("Not in use?"); xs->flags |= INUSE; } if (!(mscp = uha_get_mscp(unit,flags))) { xs->error = XS_DRIVER_STUFFUP; return(TRY_AGAIN_LATER); } cheat = mscp; if(uha_debug & UHA_SHOWMSCPS) printf("",mscp); if(scsi_debug & SHOWCOMMANDS) { uha_show_scsi_cmd(xs); } mscp->xs = xs; /***********************************************\ * Put all the arguments for the xfer in the mscp * \***********************************************/ if (flags & SCSI_RESET) { mscp->opcode = 0x04; mscp->ca = 0x01; } else { mscp->opcode = 0x02; mscp->ca = 0x01; } if (flags & SCSI_DATA_IN) { mscp->xdir = 0x01; } if (flags & SCSI_DATA_OUT) { mscp->xdir = 0x02; } if (xs->lu != 0) { xs->error = XS_DRIVER_STUFFUP; uha_free_mscp(unit,mscp,flags); return(HAD_ERROR); } mscp->dcn = 0x00; mscp->chan = 0x00; mscp->target = xs->targ; mscp->lun = xs->lu; mscp->link.addr[0] = 0x00; mscp->link.addr[1] = 0x00; mscp->link.addr[2] = 0x00; mscp->link.addr[3] = 0x00; mscp->link_id = 0x00; mscp->cdblen = xs->cmdlen; scratch = KVTOPHYS(&(mscp->mscp_sense)); mscp->sense.addr[0] = (scratch & 0xff); mscp->sense.addr[1] = ((scratch >> 8) & 0xff); mscp->sense.addr[2] = ((scratch >> 16) & 0xff); mscp->sense.addr[3] = ((scratch >> 24) & 0xff); mscp->senselen = sizeof(mscp->mscp_sense); mscp->ha_status = 0x00; mscp->targ_status = 0x00; if(xs->datalen) { /* should use S/G only if not zero length */ scratch = KVTOPHYS(mscp->uha_dma); mscp->data.addr[0] = (scratch & 0xff); mscp->data.addr[1] = ((scratch >> 8) & 0xff); mscp->data.addr[2] = ((scratch >> 16) & 0xff); mscp->data.addr[3] = ((scratch >> 24) & 0xff); sg = mscp->uha_dma ; seg = 0; mscp->sgth = 0x01; if(flags & SCSI_DATA_UIO) { iovp = ((struct uio *)xs->data)->uio_iov; datalen = ((struct uio *)xs->data)->uio_iovcnt; xs->datalen = 0; while ((datalen) && (seg < UHA_NSEG)) { scratch = (unsigned long)iovp->iov_base; sg->addr.addr[0] = (scratch & 0xff); sg->addr.addr[1] = ((scratch >> 8) & 0xff); sg->addr.addr[2] = ((scratch >> 16) & 0xff); sg->addr.addr[3] = ((scratch >> 24) & 0xff); xs->datalen += *(unsigned long *)sg->len.len = iovp->iov_len; if(scsi_debug & SHOWSCATGATH) printf("(0x%x@0x%x)" ,iovp->iov_len ,iovp->iov_base); sg++; iovp++; seg++; datalen--; } } else { /***********************************************\ * Set up the scatter gather block * \***********************************************/ if(scsi_debug & SHOWSCATGATH) printf("%d @0x%x:- ",xs->datalen,xs->data); datalen = xs->datalen; thiskv = (int)xs->data; thisphys = KVTOPHYS(thiskv); templen = 0; while ((datalen) && (seg < UHA_NSEG)) { bytes_this_seg = 0; /* put in the base address */ sg->addr.addr[0] = (thisphys & 0xff); sg->addr.addr[1] = ((thisphys >> 8) & 0xff); sg->addr.addr[2] = ((thisphys >> 16) & 0xff); sg->addr.addr[3] = ((thisphys >> 24) & 0xff); if(scsi_debug & SHOWSCATGATH) printf("0x%x",thisphys); /* do it at least once */ nextphys = thisphys; while ((datalen) && (thisphys == nextphys)) /*********************************************\ * This page is contiguous (physically) with * * the the last, just extend the length * \*********************************************/ { /* how far to the end of the page */ nextphys = (thisphys & (~(PAGESIZ - 1))) + PAGESIZ; bytes_this_page = nextphys - thisphys; /**** or the data ****/ bytes_this_page = min(bytes_this_page ,datalen); bytes_this_seg += bytes_this_page; datalen -= bytes_this_page; /* get more ready for the next page */ thiskv = (thiskv & (~(PAGESIZ - 1))) + PAGESIZ; if(datalen) thisphys = KVTOPHYS(thiskv); } /********************************************\ * next page isn't contiguous, finish the seg * \********************************************/ if(scsi_debug & SHOWSCATGATH) printf("(0x%x)",bytes_this_seg); sg->len.len[0] = (bytes_this_seg & 0xff); sg->len.len[1] = ((bytes_this_seg >> 8) & 0xff); sg->len.len[2] = ((bytes_this_seg >> 16) & 0xff); sg->len.len[3] = ((bytes_this_seg >> 24) & 0xff); templen += bytes_this_seg; sg++; seg++; } } /*end of iov/kv decision */ mscp->datalen.len[0] = (templen & 0xff); mscp->datalen.len[1] = ((templen >> 8) & 0xff); mscp->datalen.len[2] = ((templen >> 16) & 0xff); mscp->datalen.len[3] = ((templen >> 24) & 0xff); mscp->sg_num = seg; if(scsi_debug & SHOWSCATGATH) printf("\n"); if (datalen) { /* there's still data, must have run out of segs! */ printf("uha_scsi_cmd%d: more than %d DMA segs\n", unit,UHA_NSEG); xs->error = XS_DRIVER_STUFFUP; uha_free_mscp(unit,mscp,flags); return(HAD_ERROR); } } else { /* No data xfer, use non S/G values */ mscp->data.addr[0] = 0x00; mscp->data.addr[1] = 0x00; mscp->data.addr[2] = 0x00; mscp->data.addr[3] = 0x00; mscp->datalen.len[0] = 0x00; mscp->datalen.len[1] = 0x00; mscp->datalen.len[2] = 0x00; mscp->datalen.len[3] = 0x00; mscp->xdir = 0x03; mscp->sgth = 0x00; mscp->sg_num = 0x00; } /***********************************************\ * Put the scsi command in the mscp and start it * \***********************************************/ bcopy(xs->cmd, mscp->cdb, xs->cmdlen); /***********************************************\ * Usually return SUCCESSFULLY QUEUED * \***********************************************/ if (!(flags & SCSI_NOMASK)) { s = splbio(); uha_send_mbox(unit,mscp); uha_add_timeout(mscp,xs->timeout); splx(s); if(scsi_debug & TRACEINTERRUPTS) printf("cmd_sent "); return(SUCCESSFULLY_QUEUED); } /***********************************************\ * If we can't use interrupts, poll on completion* \***********************************************/ uha_send_mbox(unit,mscp); if(scsi_debug & TRACEINTERRUPTS) printf("cmd_wait "); do { if(uha_poll(unit,xs->timeout)) { if (!(xs->flags & SCSI_SILENT)) printf("cmd fail\n"); if(!(uha_abort(unit,mscp))) { printf("abort failed in wait\n"); uha_free_mscp(unit,mscp,flags); } xs->error = XS_DRIVER_STUFFUP; splx(s); return(HAD_ERROR); } } while (!(xs->flags & ITSDONE));/* something (?) else finished */ splx(s); scsi_debug = 0;uha_debug = 0; if(xs->error) { return(HAD_ERROR); } return(COMPLETE); } /* * +----------+ +----------+ +----------+ * uha_soonest--->| later |--->| later|--->| later|--->0 * | [Delta] | | [Delta] | | [Delta] | * 0<---|sooner |<---|sooner |<---|sooner |<---uha_latest * +----------+ +----------+ +----------+ * * uha_furtherest = sum(Delta[1..n]) */ uha_add_timeout(mscp,time) struct mscp *mscp; int time; { int timeprev; struct mscp *prev; int s = splbio(); if(prev = uha_latest) /* yes, an assign */ { timeprev = uha_furtherest; } else { timeprev = 0; } while(prev && (timeprev > time)) { timeprev -= prev->delta; prev = prev->sooner; } if(prev) { mscp->delta = time - timeprev; if( mscp->later = prev->later) /* yes an assign */ { mscp->later->sooner = mscp; mscp->later->delta -= mscp->delta; } else { uha_furtherest = time; uha_latest = mscp; } mscp->sooner = prev; prev->later = mscp; } else { if( mscp->later = uha_soonest) /* yes, an assign*/ { mscp->later->sooner = mscp; mscp->later->delta -= time; } else { uha_furtherest = time; uha_latest = mscp; } mscp->delta = time; mscp->sooner = (struct mscp *)0; uha_soonest = mscp; } splx(s); } uha_remove_timeout(mscp) struct mscp *mscp; { int s = splbio(); if(mscp->sooner) { mscp->sooner->later = mscp->later; } else { uha_soonest = mscp->later; } if(mscp->later) { mscp->later->sooner = mscp->sooner; mscp->later->delta += mscp->delta; } else { uha_latest = mscp->sooner; uha_furtherest -= mscp->delta; } mscp->sooner = mscp->later = (struct mscp *)0; splx(s); } extern int hz; #define ONETICK 500 /* milliseconds */ #define SLEEPTIME ((hz * 1000) / ONETICK) uha_timeout(arg) int arg; { struct mscp *mscp; int unit; int s = splbio(); unsigned int stat; int port = uha_data[unit].baseport; while( mscp = uha_soonest ) { if(mscp->delta <= ONETICK) /***********************************************\ * It has timed out, we need to do some work * \***********************************************/ { unit = mscp->xs->adapter; printf("uha%d:%d device timed out\n",unit ,mscp->xs->targ); if(uha_debug & UHA_SHOWMSCPS) uha_print_active_mscp(); /***************************************\ * Unlink it from the queue * \***************************************/ uha_remove_timeout(mscp); if((uha_abort(unit,mscp) !=1) || (mscp->flags = MSCP_ABORTED)) { printf("AGAIN"); mscp->xs->retries = 0; /* I MEAN IT ! */ uha_done(unit,mscp,FAIL); } else /* abort the operation that has timed out */ { printf("\n"); uha_add_timeout(mscp,2000 + ONETICK); mscp->flags = MSCP_ABORTED; } } else /***********************************************\ * It has not timed out, adjust and leave * \***********************************************/ { mscp->delta -= ONETICK; uha_furtherest -= ONETICK; break; } } splx(s); timeout(uha_timeout,arg,SLEEPTIME); } uha_show_scsi_cmd(struct scsi_xfer *xs) { u_char *b = (u_char *)xs->cmd; int i = 0; if(!(xs->flags & SCSI_RESET)) { printf("uha%d:%d:%d-" ,xs->adapter ,xs->targ ,xs->lu); while(i < xs->cmdlen ) { if(i) printf(","); printf("%x",b[i++]); } printf("-\n"); } else { printf("uha%d:%d:%d-RESET-\n" ,xs->adapter ,xs->targ ,xs->lu ); } } uha_print_mscp(mscp) struct mscp *mscp; { printf("mscp:%x op:%x cmdlen:%d senlen:%d\n" ,mscp ,mscp->opcode ,mscp->cdblen ,mscp->senselen); printf(" sg:%d sgnum:%x datlen:%d hstat:%x tstat:%x delta:%d flags:%x\n" ,mscp->sgth ,mscp->sg_num ,mscp->datalen ,mscp->ha_status ,mscp->targ_status ,mscp->delta ,mscp->flags); uha_show_scsi_cmd(mscp->xs); } uha_print_active_mscp() { struct mscp *mscp; mscp = uha_soonest; while(mscp) { uha_print_mscp(mscp); mscp = mscp->later; } printf("Furtherest = %d\n",uha_furtherest); }