/* ASPI interface code. By John Wilson . Copyright (C) 1999-2000 by Digby's Bitpile, Inc. All rights reserved. This program may be freely copied as long as source code is available which includes this notice. 02/07/1999 JMBW Created. */ #include #include #include #include "st.h" /* set this flag to use ASPI async I/O */ #define ASYNC 0 #pragma pack (1) /* DOS ASPI SCSI request block for SCSI I/O command */ typedef struct { byte ascmd; /* ASPI command */ volatile byte assts; /* ASPI status (written by ASPI manager) */ byte ashst; /* host adapter # */ byte asflg; /* SCSI request flags */ byte asrsvd1[4]; /* reserved for expansion */ byte astrg; /* target # */ byte aslun; /* LUN # */ dword aslen; /* data allocation length */ byte assal; /* SENSE allocation length */ void far *asbuf; /* 16:16 real mode FAR ptr to data buffer */ void far *aslnk; /* 16:16 real mode FAR ptr to next SRB */ byte ascdl; /* CDB (command descriptor block) length */ byte ashas; /* host adapter status */ byte astgs; /* target status */ void (far *aspst)(); /* 16:16 real mode FAR ptr to post routine */ byte asrsvd2[34]; /* reserved for ASPI work space */ #define ASCDBL 12 /* max length of CDB */ #define ASSNSL 18 /* length of sense data following CDB */ byte ascdb[ASCDBL+ASSNSL]; /* SCSI command descriptor block */ /* followed by sense data */ } srb; /* DOS ASPI SCSI request block for ABORT command */ typedef struct { byte ascmd; /* ASPI command */ volatile byte assts; /* ASPI status (written by ASPI manager) */ byte ashst; /* host adapter # */ byte asflg; /* SCSI request flags */ byte asrsvd1[4]; /* reserved for expansion */ void far *asabo; /* 16:16 real mode FAR ptr to SRB to abort */ } srbabort; #ifndef __WATCOMC__ #define cdecl /**/ #endif void cdecl (far *aspi)(void far *); /* pointer to ASPI manager entry */ /* init our connection to ASPI */ void scsiinit() { int fd; /* file descriptor for ASPI mgr */ union REGS in, out; /* intdos() regs */ /* open ASPI manager device driver */ fd=open("\\DEV\\SCSIMGR$",0); if(fd==-1) { perror("Error opening SCSIMGR$"); exit(1); } /* do DOS "IOCTL READ" to get entry address */ in.x.dx=(unsigned short)&aspi; /* variable must be in DGROUP */ in.x.cx=4; /* size of a DWORD pointer */ in.x.bx=fd; /* handle */ in.x.ax=0x4402; /* func=IOCTL READ */ intdos(&in,&out); if(out.x.cflag||out.x.ax!=4) { /* (error or didn't get 4 bytes) */ fprintf(stderr,"?IOCTL read error\n"); exit(1); } close(fd); } /* sort out the aftermath of a SCSI I/O command */ static int aspistatus(srb *s,scsireq *r) { if(s->assts==0x01) return(0); /* happy return */ /* error of some kind */ if(s->ashas) /* host adapter problem */ return(SCIOE); /* generic I/O error */ /* must be a target error, see if it's a check condition */ if(s->astgs==0x02) { /* check condition */ /* copy sense data out of SRB */ /* (use the smaller of the two sizes) */ memcpy(r->srsns,s->ascdb+s->ascdl, (SRSNSLsrsns[2]&0x0F) { /* check sense key in sense data */ case 0x02: /* not ready */ return(SCOFL); /* say device offline */ case 0x06: /* unit attention */ return(SCATN); default: /* other checks are sorted out by caller */ return(SCCHK); } } else return(SCIOE); /* other target device problem */ } volatile static int aspidone; /* ASPI "done" flag (just for demo) */ /* "post" routine, called by ASPI manager on completion of ASPI command -- */ /* the point of all these nasty attributes is that the routine must save */ /* and restore all regs, the SRB is passed on the stack and cleared by the */ /* caller (rather than a "RETF 4" instruction), and DS is not known to point */ /* at DGROUP on entry */ #ifdef __WATCOMC__ #pragma aux aspipost parm [] modify [ax bx cx dx si di bp ds es]; #pragma aux aspipost parm caller; static void _loadds far aspipost(srb far *s) { aspidone=1; /* tell mainline we're done */ } #else // maybe could do it with in-line assembly code? ASPI spec says save/restore // all regs, so this: // push ax // push ds // mov ax,seg aspidone // mov ds,ax // mov aspidone,1 // pop ds // pop ax // retf // ought to do it, the problem I had with Watcom was that it was including a // stack check that destroyed AX before it even got to my assembly code (and // was wrong anyway since the call isn't made on our stack), but the #pragmas // fix that by telling it that the argument is passed on the stack and all regs // must be preserved. anyway make sure it's a FAR routine, it's supposed to // return with RETF rather than RET #endif /* execute a SCSI command and wait for completion */ int xscsiw(scsireq *r) { static srb s; s.ascmd=0x02; /* ASPI cmd = send SCSI I/O command */ s.assts=0; /* just for neatness */ s.ashst=r->srhst; /* copy SCSI device name */ s.astrg=r->srtrg; s.aslun=r->srlun; switch(r->srdir) { /* sort out DMA direction */ case +1: s.asflg=011; /* read from device, post on completion */ break; case -1: s.asflg=021; /* write to device, post on completion */ break; case 0: s.asflg=031; /* no data phase, post on completion */ } s.aslen=(dword)r->srsiz; /* length of xfr (if any) */ s.assal=ASSNSL; /* sense allocation length (for check conds) */ s.asbuf=r->srbuf; /* buf addr */ s.aslnk=(void far *)0; /* no next SRB */ s.ascdl=r->srcdl; /* CDB length */ memcpy(s.ascdb,r->srcdb,s.ascdl); /* copy CDB (known to fit) */ s.ascdb[1]=(s.ascdb[1]&037)|(s.aslun<<5); /* put LUN in 2nd byte of CDB */ /* for now, this routine waits for completion */ /* should have an async version too which calls back when done */ #if ASYNC /// poll the flag set by the async post routine s.aspst=aspipost; /* addr of "post" routine */ aspidone=0; /* not done yet */ (*aspi)(&s); /* call ASPI */ while(!aspidone) ; /* wait for aspipost() call */ #else /// just poll the completion status (avoids upcall problems with C) s.asflg&=~1; /* clear "post" bit after all */ (*aspi)(&s); /* start the transfer */ while(!s.assts) ; /* wait for non-zero completion code */ #endif return(aspistatus(&s,r)); /* sort out status */ } /* abort an outstanding ASPI I/O command (currently not used) */ static void aspiabort(srb *r) { static srbabort s; s.ascmd=0x03; /* ASPI cmd = abort SCSI I/O command */ s.assts=0; /* just for neatness */ s.ashst=r->ashst; /* copy SCSI HA number */ s.asflg=0; /* no flags */ s.asabo=r; /* SRB addr */ (*aspi)(&s); /* call ASPI */ while(!r->assts) ; /* wait for abort to take effect */ } /* abort an outstanding SCSI command */ void scabo(scsireq *r) { //// should find SRB corresponding to scsireq block and aspiabort it //// but I've only got one staticly allocated SRB, for now }