tin@szebra.szebra.uucp (Tin Le) (09/17/90)
Seagate ST-0x 386/ix driver v1.0 #!/bin/sh # this is st02.02 (part 2 of a multipart archive) # do not concatenate these parts, unpack them in order with /bin/sh # file README continued # if test ! -r _shar_seq_.tmp; then echo 'Please unpack part 1 first!' exit 1 fi (read Scheck if test "$Scheck" != 2; then echo Please unpack part "$Scheck" next! exit 1 else exit 0 fi ) < _shar_seq_.tmp || exit 1 if test ! -f _shar_wnt_.tmp; then echo 'x - still skipping README' else echo 'x - continuing file README' sed 's/^X//' << 'SHAR_EOF' >> 'README' && pins A-B shorted CE000H pins A-B & C-D shorted DE000H X The memory address set on the card must match the value defined in the driver. Check the driver source for the correct address. If you want to use a different address, change the define in the source. X X Interrupt selection X No jumper installed interrupts disabled (default configuration) E-F shorted IRQ3 F-G shorted IRQ5 X Interrupts must be enabled to use my driver under unix. The interrupt selected must match the value in file "config" in the modules/scsi directory. Check the config file for the correct value; change the value in config file if you wish to use some other value. X NOTE: at least in the card I received, there were no jumper connectors in E, F and G. There were just holes in the card. You have to solder the connection yourself. It is not a difficult task if you have ever done any soldering, and luckily the card does not cost very much if something goes wrong... It is better to ground the soldering iron before doing anything. X If I recall correctly, I am using IRQ5, and soldered F&G together with a short wire. A friend of mine had both of those interrupts in use, and soldered F to some other interrupt number on the bus and everything works fine. However, I can't help with that. For more info, see IBM AT technical manual. (You might get some help by sending mail to jnopanen@hupu.hut.fi). X It seems like Seagate has been thinking that the controller would never be used with unix... X X I think the 0ws jumper should be connected on most (almost every?) machines. On my machine it raised the transfer rate from about 500kB/sec to about 700kB/sec under msdos. The transfer rate is really cpu-bound as the data transfers are done in software (the card does not support dma), so faster machines might get faster transfer rates (mine is a slow 16 MHz 386). X X Tatu Yl|nen ylo@hupu.hut.fi X SHAR_EOF echo 'File README is complete' && chmod 0644 README || echo 'restore of README failed' Wc_c="`wc -c < 'README'`" test 10877 -eq "$Wc_c" || echo 'README: original size 10877, current size' "$Wc_c" rm -f _shar_wnt_.tmp fi # ============= README.2 ============== if test -f 'README.2' -a X"$1" != X"-c"; then echo 'x - skipping README.2 (File already exists)' rm -f _shar_wnt_.tmp else > _shar_wnt_.tmp echo 'x - extracting README.2 (Text)' sed 's/^X//' << 'SHAR_EOF' > 'README.2' && Copyright (c) 1990 Tin Le X ST0x SCSI driver version 1.0 X Sunday 9/16/90 X First, to cover my behind (don't you love lawyers? :) ) X DISCLAIMER ---------- X X This software has been tested to the best of my abilities. X It works for me. However, you use this at your own risks. X README ------ X X SCSI driver for Seagate ST-02 Host Adapter for Interactive X 386/ix v2.0.2. Usage rights are release into the public X domain. You may use and distribute this package, but you X may not charge for it. X X This is a modified version of the ST-01 driver by Tatu Ylonen. X His driver was for Microport UNIX v3.00e, I adapted it for X 386/ix, fixed some problems (changed char to unsigned char), X optimized things a little bit more. X X The difference between the ST-01 and ST-02 is that the ST-02 X also include floppy ctlr (both 5 1/4" and 3 1/2"). Since Tatu X wrote his driver, Seagate apparently changed the revision of X the ST-0X boards. They now uses 16K BIOS instead of 8K. X Luckily, the hardware addresses seemed to have been kept the X same (anyone who knows how I can get more technical information X on these boards, please let me know - email to tin@szebra.uucp X or uunet!claris!szebra!tin). X X Here is a recap of the featurres and drawbacks of the boards: X X Advantages: X X - will work with most SCSI disks X - no limit on disk or partition size X - each physical driver can have up to 15 partitions X - support multiple drives (up to 7) on a single ctrlr X - transfer rate faster than MFM disk X - the ST-02 (ST-01) will work in the system along with X other controller you have (ST506 or RLL). X - driver is free and complete SCSI subsystem can be put X together at low cost X - you have source code! X X Disadvantages: X X - does not use DMA X - character devices not supported (no raw I/O) X - you can not boot from SCSI disk X - lots of data movement at each interrupt, which can X lead to contention with the serial ports X X Besides the drives Tatu used, some of the other ones that works X are: a CDC Imprimis 94161-156 (150MB formatted), Seagate ST296N X (80MB formatted). I used it on my Usenet news node, and also X XBBS. As I mentioned, under heavy disk usage, the serial port X will start dropping characters. However, I run my Telebit+ at X 19200 bps so my guess is that 2400 or less should be OK. I X might eventually rewrite the driver interrupt in assembler to X help speed up things, but I doubt it'd make much difference. X There's only so much that can be done in moving large amounts X of data using interrupts driven approach. My suggestion is to X use the FAS driver with a 16550AN (that's what I did, and yes X it works!). X X I also have a WD1003 ST506 16 bit controller (since I can't X boot on SCSI - yet!) in the system controlling a Micropolis 72MB X and 2 floppies. X X INSTALLATION ------------ X NOTE: You must be root during this entire installation process! X X I am assuming that you are using Interactive 386/ix v2.0.2, X as that is the only *NIX I have available to me for my 386. X If (when) I decide to move to newer versions, I will definitely X port the driver. X X BEFORE YOU DO ANYTHING, BACK UP YOUR DATA FILES! X X Backup this file: X X /etc/conf/cf.d/mdevice ---------> /etc/conf/cf.d/mdevice.org X NOTE: The ST-01 and ST-02 comes with BIOS (either 8K or 16K), you WILL HAVE TO REMOVE it or the driver won't work. At least it didn't for me until I removed the BIOS. Save it somewhere for a time you decided to sell the board or <ugh, shudder!> go back to MSDOS! X X There are 2 methods you can use to install the driver. You can X use my automated install script, or you can do it by hand. I'll X describe how to do it manually in case something went wrong with X the script. X Using install script X X To use the install script, go to some directory where you have X enough room to unpack the driver package. I recommend /tmp or X /usr/tmp. Make a directory there call scsi. Unpack the package X in there. Then execute install by: sh install. That should X do most of the work for you. Go to the next section, running X kconfig when it tells you to. X Manual installation X X Go to the /etc/conf/pack.d directory, create a scsi directory X there and unpack the files in it. X X Change dir (cd) into /etc/conf/pack.d/scsi X X You will now need to copy some of the files into the correct X directories. The files and their directories are: X X node.d ----------------> /etc/conf/node.d/scsi X sdevice.d ----------------> /etc/conf/sdevice.d/scsi X X Example: cp node.d /etc/conf/node.d/scsi X cp sdevice.d /etc/conf/sdevice.d/scsi X X You will need to either edit /etc/conf/cf.d/mdevice to add the X lines in mdevice or you can do the following: X X cat mdevice >> /etc/conf/cf.d/mdevice X X If you have the SDS, then you can recompile the source files. X Everything is setup in the makefile for that, so you just need X to type make. X X If you don't have the SDS, then you can use the included X pre-compiled object (Driver.o). I am currently using this in X my own system, so I know it works. X X A WARNING: DO NOT USE gcc as it uses a few routines in gcc-lib X that the regular kernel libraries do not have. The resulting X kernel will be unusable! X Running kconfig X X After all the config files have been setup and the driver X compiled, you will need to run kconfig to relink the kernel. X X /etc/kconfig X X After asking you about Root Directory (just press Enter here), it X will give you a menu of 3 choices: X X MAIN MENU X X 1) CONFIGURE KERNEL X 2) BUILD A KERNEL X 3) INSTALL A KERNEL X X Enter Choice [1-3,q]: X X Chose choice 2 to build a kernel. That's it. kconfig will build X the kernel, then after about 10-15 minutes (depending on how fast X your system is), it will ask if you want to install the kernel. X At this time, if you are ready to install, then type yes. X WARNING: installing a new kernel mean that you will have to reboot so make sure no one else is on the system and that you don't have anything important running! X X When the system rebooted, the new kernel will be install and you X should see the driver copyright message at boot up. If you have X a SCSI disk connected to the controller, then the driver will X also print its geometry information (manufacturer id, size). X X Unless you had already somehow formatted and partitioned the X disk before hand, you will now need to do just that. Run the X program scsipart to low level format the disk and partition X it. X X Interleave: I am currently using 3:1 interleave and block size X of 512 bytes, that seems to be the best for my configuration X (386/20 Mhz interleaved memory). For those of you lucky enough X to have faster systems, you can problably use 1:1 interleave. X Just remember that you will need to experiment a little bit X to find the best combination for your applications requirements. X SUMMARY ------- X X I will enhance and fix bugs for the package as time permits. X Please send me any fixes and enhancements you've made so that X I can incorporate it in for the next release. I'd like to X keep the changes coordinated so that it is easier on everyone X in tracking software versions (and system problems). X X Comments, fixes/bug reports, fan mails to: X X tin@szebra.uucp or uunet!claris!szebra!tin X -- Tin Le X SHAR_EOF chmod 0644 README.2 || echo 'restore of README.2 failed' Wc_c="`wc -c < 'README.2'`" test 7169 -eq "$Wc_c" || echo 'README.2: original size 7169, current size' "$Wc_c" rm -f _shar_wnt_.tmp fi # ============= config ============== if test -f 'config' -a X"$1" != X"-c"; then echo 'x - skipping config (File already exists)' rm -f _shar_wnt_.tmp else > _shar_wnt_.tmp echo 'x - extracting config (Text)' sed 's/^X//' << 'SHAR_EOF' > 'config' && * * config file for scsi driver on Microport System V/386 * Copyright (c) 9.6.1988 Tatu Yl|nen * All rights reserved. * X character(9) block(9) X prefix = scsi X intvec = 5 X intpri = SPL5 X functions = init, open, close, read, write, ioctl, strategy SHAR_EOF chmod 0644 config || echo 'restore of config failed' Wc_c="`wc -c < 'config'`" test 260 -eq "$Wc_c" || echo 'config: original size 260, current size' "$Wc_c" rm -f _shar_wnt_.tmp fi # ============= install ============== if test -f 'install' -a X"$1" != X"-c"; then echo 'x - skipping install (File already exists)' rm -f _shar_wnt_.tmp else > _shar_wnt_.tmp echo 'x - extracting install (Text)' sed 's/^X//' << 'SHAR_EOF' > 'install' && #!/bin/sh # Install script for ST-0X SCSI driver # # 9/16/90 Tin Le X if test ! `who am i | cut -f1 -d' '` = "root" ; then X echo "You must be root to do this!" X exit 1 fi X PATH=/bin:/usr/bin:/usr/local/bin export PATH X CONFROOT=/etc/conf ST02DIR=/etc/conf/pack.d/scsi X echo;echo echo "Checking for /etc/conf directory ..." if test ! -d /etc/conf ; then X echo "You don't have the kernel config package loaded!" X echo "Please load it first before installing this driver." X exit 1 fi X echo "/etc/conf found, checking for /etc/conf/pack.d directory ..." if test ! -d /etc/conf/pack.d ; then X echo "/etc/conf/pack.d directory not found!" X echo "Did you loaded the kernel config package correctly?" X exit 1 fi X echo "/etc/conf/pack.d found, checking for scsi directory ..." if test ! -d ${ST02DIR} ; then X echo "${ST02DIR} does not exist" X echo "creating it ..." X mkdir /etc/conf/pack.d/scsi X echo "moving driver files into it ..." X mv * ${ST02DIR} else X echo "${ST02DIR} exist!" X echo "will save files there in ${ST02DIR}/OLD directory" X mkdir ${ST02DIR}/OLD X mv ${ST02DIR}/* ${ST02DIR}/OLD X mv * ${ST02DIR} fi cd ${ST02DIR} X echo "Copying sdevice.d to ${CONFROOT}/sdevice.d" cp sdevice.d ${CONFROOT}/sdevice.d/scsi echo "Copying node.d to ${CONFROOT}/node.d" cp node.d ${CONFROOT}/node.d/scsi X echo "Adding mdevice to ${CONFROOT}/cf.d/mdevice" # Back it up first! cp ${CONFROOT}/cf.d/mdevice ${CONFROOT}/cf.d/mdevice.org cat mdevice >> ${CONFROOT}/cf.d/mdevice X echo "The package comes with a precompiled Driver.o file." echo "You will have to use it if your system do not have the Software" echo "Development Package. Or you could recompile it." echo -n "Do you wish to use the included Driver.o file? [yes] " read ans case $ans in y*|Y*) echo "Using the included Driver.o file" ;; *) echo "Recompiling the source files" ; X touch *.c X make ;; esac X echo; echo echo "All the data and config files have been set up." echo "You need to run kconfig now to relink the kernel." X SHAR_EOF chmod 0755 install || echo 'restore of install failed' Wc_c="`wc -c < 'install'`" test 1975 -eq "$Wc_c" || echo 'install: original size 1975, current size' "$Wc_c" rm -f _shar_wnt_.tmp fi # ============= makefile ============== if test -f 'makefile' -a X"$1" != X"-c"; then echo 'x - skipping makefile (File already exists)' rm -f _shar_wnt_.tmp else > _shar_wnt_.tmp echo 'x - extracting makefile (Text)' sed 's/^X//' << 'SHAR_EOF' > 'makefile' && # # makefile for scsi driver # # Copyright (c) 8.6.1988 Tatu Yl|nen # Copyright (c) 16.9.1990 Tin Le # All rights reserved. # # 8/90 Tin # Added scsiasm.o to makefile (using ld -r) # Added Driver.o, td, scsipart targets # X CC = cc #CFLAGS = -O -DASM -fstrength-reduce -fpcc-struct-return -DDEBUG # -DDEBUG0 #CFLAGS = -O -fstrength-reduce -fpcc-struct-return -DDEBUG # -DDEBUG0 CFLAGS = -O -DDEBUG -DASM # -DDEBUG0 X all: scsi.o scsiasm.o scsipart Driver.o td X td: td.c X cc td.c -o td -O X Driver.o: scsi.o scsiasm.o X ld -r scsi.o scsiasm.o X mv a.out Driver.o X scsiasm.o: scsiasm.s X scsi.o: scsi.c scsi.h X scsipart: scsipart.c scsi.h X cc -o scsipart scsipart.c X SHAR_EOF chmod 0644 makefile || echo 'restore of makefile failed' Wc_c="`wc -c < 'makefile'`" test 673 -eq "$Wc_c" || echo 'makefile: original size 673, current size' "$Wc_c" rm -f _shar_wnt_.tmp fi # ============= mdevice ============== if test -f 'mdevice' -a X"$1" != X"-c"; then echo 'x - skipping mdevice (File already exists)' rm -f _shar_wnt_.tmp else > _shar_wnt_.tmp echo 'x - extracting mdevice (Text)' sed 's/^X//' << 'SHAR_EOF' > 'mdevice' && scsi Iocrwi icbHo scsi 9 60 1 7 -1 SHAR_EOF chmod 0644 mdevice || echo 'restore of mdevice failed' Wc_c="`wc -c < 'mdevice'`" test 36 -eq "$Wc_c" || echo 'mdevice: original size 36, current size' "$Wc_c" rm -f _shar_wnt_.tmp fi # ============= node.d ============== if test -f 'node.d' -a X"$1" != X"-c"; then echo 'x - skipping node.d (File already exists)' rm -f _shar_wnt_.tmp else > _shar_wnt_.tmp echo 'x - extracting node.d (Text)' sed 's/^X//' << 'SHAR_EOF' > 'node.d' && scsi rscsi0s c 15 scsi rscsi1s c 15 SHAR_EOF chmod 0644 node.d || echo 'restore of node.d failed' Wc_c="`wc -c < 'node.d'`" test 36 -eq "$Wc_c" || echo 'node.d: original size 36, current size' "$Wc_c" rm -f _shar_wnt_.tmp fi # ============= scsi.c ============== if test -f 'scsi.c' -a X"$1" != X"-c"; then echo 'x - skipping scsi.c (File already exists)' rm -f _shar_wnt_.tmp else > _shar_wnt_.tmp echo 'x - extracting scsi.c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'scsi.c' && /* X 9/16/90 Tin Le X - Released to the world version 1.0 X X Modified for Interactive 386/ix v2.0.2 with some bug fixes X and minor enhancements. X Will work with ST-02. X SCSI disk driver for unix system V (Microport system V/386) This driver uses the ST-01 controller. This supports multiple initiators and multiple targets. X Copyright (c) 9.6.1988 Tatu Yl|nen Copyright (c) 16.9.1988 Tin Le X All rights reserved. X */ X #include <fcntl.h> X #include <sys/sysmacros.h> #include <sys/types.h> #include <sys/param.h> #include <sys/signal.h> #include <sys/errno.h> #include <sys/dir.h> #include <sys/file.h> #include <sys/user.h> #include <sys/buf.h> #include <sys/iobuf.h> #include <sys/immu.h> #include <sys/region.h> #include <sys/proc.h> #include "scsi.h" X #define COPYRIGHT "SCSI disk driver V1.1 Copyright (c) 9.6.1988 Tatu Yl|nen\n\tCopyright (c) 8.8.1990 Tin Le" X /* #define ASM */ /* use certain routines coded in assembly */ X #define TICKSPERSECOND HZ /* system timer ticks per second */ #define RWTIMEOUT 5 /* timeout for read/write waiting for reconnect */ #define MYSLEEPPRI (PZERO+1) /* sleeping priority (allow signals) */ #define PAGESIZE 4096 /* page size in sptalloc */ X #define UNITNO(minornum) ((minornum)>>4) /* minor device to unit */ #define PARTNO(minornum) ((minornum)&15) /* minor device to partition */ #define BLTOSEC(unit,bl) ((long)(bl)*NBPSCTR/d[unit].blocksize) X /* converts block number to sector number */ #define BLPTOSEC(unit,part,bl) (BLTOSEC(unit,bl)+d[unit].parts[part].start) X /* calculates sector number from block and partition */ X #define splnointrs() spl6() /* disable any interrupts to this driver (clock!)*/ X #define NULL 0 X #define SCSIBASE 0x000cb000l /* address of the scsi controller(no rom)*/ #define SCSICONTROLOFS 0x00000a00l /* control/status port offset */ #define SCSIDATAOFS 0x00000c00l /* data port offset */ #define SCSIDATAPORTSZ 1024 /* size of data port */ #define SCSISIZE 4096 /* size of controller memory area */ X #define MYADDR 0x80 /* my address as bit mask */ X #define CMDENABLE 0x80 /* scsi enable */ #define CMDENINTR 0x40 /* enable scsi interrupts */ #define CMDPARENB 0x20 /* enable scsi parity generation */ #define CMDSTARB 0x10 /* start arbitration bit */ #define CMDATTN 0x08 /* scsi attention */ #define CMDBSY 0x04 /* scsi busy */ #define CMDSEL 0x02 /* scsi select */ #define CMDRST 0x01 /* scsi reset */ X #define STARBCOMPL 0x80 /* arbitration complete bit */ #define STPARERR 0x40 /* parity error bit */ #define STSEL 0x20 /* scsi select */ #define STREQ 0x10 /* scsi req */ #define STCD 0x08 /* scsi c/d */ #define STIO 0x04 /* scsi i/o */ #define STMSG 0x02 /* scsi msg */ #define STBSY 0x01 /* scsi busy */ X #define CMDBASE (CMDPARENB|CMDENINTR) /* cmd when doing nothing */ X #define SCSIREAD 0x28 /* read command code (10-byte) */ #define SCSIWRITE 0x2a /* write command code (10-byte) */ #define SCSIINQUIRY 0x12 /* inquiry command (6-byte) */ #define SCSIREADCAPACITY 0x25 /* read drive capacity and block size */ #define SCSIMODESELECT 0x15 /* select format parameters */ #define SCSIFORMATUNIT 0x04 /* hard format the scsi drive */ #define SCSIREQSENSE 0x03 /* request sense command */ #define SCSITESTREADY 0x00 /* test unit ready command */ X #define MSGMYIDENTIFY 0xc0 /* our identify message to send to target */ X #define MSGCOMPLETE 0x00 /* command complete */ #define MSGSAVEDATAPTR 0x02 /* save data pointer */ #define MSGRESTOREPTR 0x03 /* restore pointer */ #define MSGDISCONNECT 0x04 /* disconnect message */ #define MSGIDETECTERR 0x05 /* initiator detected error */ #define MSGABORT 0x06 /* scsi abort message */ #define MSGMSGREJECT 0x07 /* message reject */ #define MSGNOP 0x08 /* no operation message */ #define MSGIDENTIFY 0x80 /* identify message from target */ X #define COK 0 /* command completed successfully */ #define CNOCONNECT 1 /* no connection could be made to the drive */ #define CBUSBUSY 2 /* the bus is busy and cannot be cleared */ #define CTIMEOUT 3 /* timeout waiting for the drive */ #define CERROR 4 /* an error was returned by the target */ #define CBUSY 5 /* the drive is busy - wait and retry later */ #define CDISCONNECT 6 /* target disconnected; this is not an error */ X #ifdef DEBUG0 static char *Cresults[] = { X "COK", "CNOCONNECT", "CBUSBUSY", "CTIMEOUT", X "CERROR", "CBUSY", "CDISCONNECT", "BAD_RESULT_CODE" }; #endif X static unchar *baseaddr=NULL; /* controller base address */ static unchar *cmdport; /* controller command port */ unchar *scsidataport; /* controller data port */ X static SCSIDRIVE d[SCSIMAXDRIVES]; /* drive information */ X static struct buf scsibuf; /* used for raw io */ static unchar rawiobuf[SCSIDATAPORTSZ]; /* data copied temporarily here */ X static char timeouting=0; static char intrserviced=0; X /* marks the unit as not busy and starts any pending io */ static void marknotbusy(); X /* This generates a hard reset on the scsi bus by asserting the reset line */ X static void resetscsibus() { X long l; X int a; X X printf("scsi: sending hard reset to scsi bus\n"); X *cmdport=CMDBASE|CMDENABLE|CMDRST; X for (l=0;l<10000l;l++); /* keep rst asserted for a while */ X *cmdport=CMDBASE; X for (l=0;l<500000l;l++); /* give some time to recover before returning */ X for (a=0;a<SCSIMAXDRIVES;a++) X d[a].connected=0; /* do this just in case */ } X /* This arbitrates for the scsi bus and selects the desired target. This X returns a C* result code. This will also set the connected flag X if appropriate. If there are possibly recoverable errors, this will X retry. The calling procedure should not retry if this returns X failure. */ X static int arbitrate(unit) int unit; { X long l; X int arbcnt,bsycnt; /* retry counts */ X X arbcnt=0; X bsycnt=0; X retryarb: X *cmdport=CMDBASE; X *scsidataport=MYADDR; X *cmdport=(CMDBASE&~CMDENINTR)|CMDSTARB; X /* wait for arbitration complete */ X for (l=0;l<300000l;l++) X if (*cmdport & STARBCOMPL) X goto gotarb; X /* arbitration timeout - someone is keeping the bus reserved. */ X *cmdport=CMDBASE; X if (arbcnt >= 2) /* retry twice, then give up */ X { X printf("scsi: arbitration timeout - someone is sitting on the bus?\n"); X return CBUSBUSY; X } X resetscsibus(); /* reset the bus and hope the condition clears */ X arbcnt++; X goto retryarb; X gotarb: X arbcnt=0; X *scsidataport|=1<<unit; X *cmdport=CMDBASE|CMDENABLE|CMDSEL|CMDATTN; X for (l=0;l<100000l;l++) X if (*cmdport & STBSY) X goto gotbusy; X /* timeout waiting for busy */ X *cmdport=CMDBASE; X if (bsycnt >= 2) X { #ifdef DEBUG0 X printf("scsi arbitrate: returning CNOCONNECT\n"); #endif X return CNOCONNECT; /* probably no drive present */ X } X bsycnt++; X for (l=0;l<20000l;l++); /* give some time for the drive */ #ifdef DEBUG0 X printf("scsi: busy timeout on drive %d\n",unit); #endif X goto retryarb; X gotbusy: X d[unit].connected=1; X if (!d[unit].nomsgs) X { X *cmdport=CMDBASE|CMDENABLE|CMDATTN; X for (l=0;l<200000l;l++) X if ((*cmdport & (STMSG|STCD|STIO|STREQ)) == (STMSG|STCD|0|STREQ)) X goto gotmsgreq; X /* timeout waiting for msg out */ X printf("scsi: timeout identify msg out - drive %d does not support messages?\n", X unit); X d[unit].nomsgs=1; /* don't try messages again */ X *cmdport=CMDBASE|CMDENABLE; X return COK; X gotmsgreq: X *scsidataport=MSGMYIDENTIFY; /* this enables disconnect */ X /* fall to successful completion */ X } #ifdef DEBUG X if (!(*cmdport & STBSY)) X { X printf("scsi: after successful arbitrate !STBSY\n"); X for (l=0;l<10000000l;l++); X arbcnt++; X goto retryarb; X } #endif /* DEBUG */ X *cmdport=CMDBASE|CMDENABLE; X return COK; } X #ifndef ASM X /* This copies data to the scsi data port as fast as possible. This could X even be coded in assembly language for efficiency. */ X static void sendtoscsi(buf,len) register unchar *buf; register int len; { X while (len--) X *scsidataport=(*buf++); } X /* This reads data from the scsi data port as fast as possible. */ X static void getfromscsi(buf,len) register unchar *buf; register int len; { X while (len--) X *buf++=(*scsidataport); } X #endif /* ASM */ X /* This implements the scsi data out phase. There are several operating X modes for this. 1) normal as fast as possible io 2) slow io where we X check req individually for each character 3) moving data directly from X user space. If an error is encountered (such as a protection fault when X moving data from user space), this will return 0. Moving data from X user space is only implemented in "fast" mode. */ X static int dataout(unit) int unit; { X register int le; X long l; X register char slow; X int copyin(); X X slow=d[unit].xferslow; X for (;d[unit].xferlen > 0;d[unit].xferlen-=le,d[unit].xferbuf+=le) X { X for (l=0;l<100000l;l++) X if (*cmdport & STREQ) X goto gotreq; X /* timeout */ X break; X gotreq: X if ((*cmdport & (STMSG|STCD|STIO)) != (0|0|0)) X break; X if (slow) X { X le=1; X *scsidataport=(*d[unit].xferbuf); X continue; X } X le=d[unit].xferlen; X if (le > SCSIDATAPORTSZ) X le=SCSIDATAPORTSZ; X if (le > d[unit].blocksize) X le=d[unit].blocksize; X if (d[unit].xferphys) X { X if (le > sizeof(rawiobuf)) X le=sizeof(rawiobuf); X if (copyin(d[unit].xferbuf,rawiobuf,le) == -1) X return 0; X sendtoscsi(rawiobuf,le); X } X else X sendtoscsi(d[unit].xferbuf,le); X } X while ((*cmdport & (STMSG|STCD|STIO|STREQ)) == (0|0|0|STREQ)) X { X *scsidataport=0; X for (l=0;l<4000l;l++) X if (*cmdport & STREQ) X break; X } X return 1; } X /* this implements the scsi data in phase. This copies data from X scsi bus to system memory. There are three modes of operation: X 1) "slow" transfer to kernel memory 2) "fast" transfer to kernel X memory 3) "fast" transfer to user memory */ X static int datain(unit) int unit; { X register int le; X long l; X register char slow; X int copyout(); X /* slow=d[unit].xferslow; */ X slow=1; X d[unit].xferphys=0; X for (;d[unit].xferlen > 0;d[unit].xferlen-=le,d[unit].xferbuf+=le) X { X for (l=0;l<100000l;l++) X if (*cmdport & STREQ) X goto gotreq; X /* timeout */ X break; X gotreq: X if ((*cmdport & (STMSG|STCD|STIO)) != (0|0|STIO)) X break; X if (slow) X { X le=1; X *d[unit].xferbuf=(*scsidataport); X continue; X } X le=d[unit].xferlen; X if (le > SCSIDATAPORTSZ) X le=SCSIDATAPORTSZ; X if (le > d[unit].blocksize) X le=d[unit].blocksize; X if (d[unit].xferphys) X { /* directly to user space */ X if (le > sizeof(rawiobuf)) X le=sizeof(rawiobuf); X getfromscsi(rawiobuf,le); X if (copyout(rawiobuf,d[unit].xferbuf,le) == -1) X return 0; X } X else X getfromscsi(d[unit].xferbuf,le); X } X while ((*cmdport & (STMSG|STCD|STIO|STREQ)) == (0|0|STIO|STREQ)) X { X le=(*scsidataport); X for (l=0;l<4000l;l++) X if (*cmdport & STREQ) X break; X } X return 1; } X /* This is called when we are connected to the target on the scsi bus. X This will do any exchange of data with the target. The dialog is X controlled by the target. This will remain connected until the X target sends a disconnect message, the command is complete, or a timeout X is encountered. There should be no interrupts while this is executing, X as the unit should be connected all the time. This returns a C* completion X status. Normally, this should return quite fast. This will never sleep X and will also be called at interrupt time. With dumb drives not supporting X disconnect (are there any?) this would block the system for the duration X of this call. This will only mark the drive not busy if the command X completed successfully. If an error is returned, the drive has not X been marked not busy. */ X static int doxfernosleep(unit) int unit; { X register int a; X long l; X X for (l=0; l<1000000l || d[unit].xfertimeout == 0; l++) X { X if (!(*cmdport & STBSY)) X { #ifdef DEBUG X printf("scsi doxfernosleep: !STBSY unit %d\n",unit); #endif X d[unit].connected=0; X return CERROR; /* we are no longer connected??? */ X } X if (!(*cmdport & STREQ)) X continue; /* loop until target requesting something */ #ifdef DEBUG1 X printf("scsi doxfernosleep: new state=%x\n",*cmdport); #endif X switch ((*cmdport & (STMSG|STCD|STIO)) & 0xff) X { X case 0|0|0: /* data out */ X if (!dataout(unit)) X { #ifdef DEBUG X printf("scsi: dataout returned error; unit=%d\n",unit); #endif X return CERROR; X } X break; X case 0|0|STIO: /* data in */ X if (!datain(unit)) X { #ifdef DEBUG X printf("scsi: datain returned error; unit=%d\n",unit); #endif X return CERROR; X } X break; X case 0|STCD|0: /* command out */ X *scsidataport=(*d[unit].xfercmd++); X break; X case 0|STCD|STIO: /* status in */ X d[unit].xferstatus=(*scsidataport); X break; X case STMSG|STCD|0: /* msg out */ X /* we should never get here. We don't want to send a message. X Lets just drop attention and hope the drive understands. */ #ifdef DEBUG0 X printf("scsi: unexpected msg out state; status=%x\n",*cmdport); #endif X *scsidataport=MSGNOP; /* send a no-operation message */ X *cmdport=CMDBASE|CMDENABLE; X break; X case STMSG|STCD|STIO: /* msg in */ X a=(*scsidataport) & 0xff; X switch (a) X { X case MSGCOMPLETE: X d[unit].connected=0; X *cmdport=CMDBASE; #ifdef DEBUG0 X printf("scsi: COMMAND complete message received\n"); #endif X if (d[unit].xferstatus == 0) /* completed succesfully */ X { X marknotbusy(unit,COK); X return COK; X } X return CERROR; X case MSGSAVEDATAPTR: X d[unit].savedbuf=d[unit].xferbuf; X d[unit].savedlen=d[unit].xferlen; X break; X case MSGRESTOREPTR: X d[unit].xferbuf=d[unit].savedbuf; X d[unit].xferlen=d[unit].savedlen; X d[unit].xfercmd=d[unit].savedcmd; X break; X case MSGDISCONNECT: X d[unit].connected=0; X d[unit].xfertime=1; X *cmdport=CMDBASE; #ifdef DEBUG0 X printf("scsi: disconnected\n"); #endif X return CDISCONNECT; X case MSGMSGREJECT: X break; /* the target rejected some message... Who cares. */ X case MSGNOP: X break; /* this should not be sent by the target, but... */ X case MSGIDENTIFY: X break; /* we don't care about targets identify messages */ X default: X if (a & 0x80) X break; /* assume it is an identify message */ X printf("scsi: unknown message received from drive %d: %x\n", X unit,a); X break; X } X break; X default: X /* unexpected stack state. Now I don't know what to do. Lets X hope the drive changes to another state. */ #ifdef DEBUG X printf("scsi: unexpected bus state: status=%x\n",*cmdport); #endif X break; X } X } X return CTIMEOUT; } X /* This implements polled wait for reconnect. This is mainly used at X system initialization time when the interrupt system may not be fully X initialized. This returns true if reconnect was encountered. X If there is no successful reconnect, this will time out after a few X seconds and return false. */ X static int polledwaitreconnect(unit) int unit; { X long l; X unsigned char ch; X X *cmdport=CMDBASE&~CMDENINTR; X for (l=0;l<4000000l;l++) X { X if ((*cmdport & (STSEL|STIO|STBSY)) != (STSEL|STIO|0)) X continue; X ch=(*scsidataport); X if (!(ch & MYADDR)) X { #ifdef DEBUG X printf("scsi: polled reselection was not for me: %x\n",ch); #endif X continue; X } X ch&=~MYADDR; X if (!(ch & (1 << unit))) X { #ifdef DEBUG X printf("scsi: reselecting (polled) unit other than expected: %x\n", X ch); #endif X continue; X } X *cmdport=(CMDBASE&~CMDENINTR)|CMDBSY|CMDENABLE; X for (l=0;l<200000l;l++) X if (!(*cmdport & STSEL)) X break; X for (l=0;l<200000l;l++) X if (!(*cmdport & STBSY)) X break; X *cmdport=CMDBASE|CMDENABLE; X d[unit].connected=1; X return 1; X } X *cmdport=CMDBASE; #ifdef DEBUG X printf("scsi: timeout polled wait for reselection from %d\n",unit); #endif X return 0; } X /* This starts the scsi command. Interrupts may be enabled when this is X called. When this retuns, either the drive must have been marked not X busy (error or completion), or the target has disconnected and the drive X will be marked not busy when an interrupt or timeout comes. A failure X to mark the drive not busy will block the drive from all future X requests. If retries are made for a command, this will be called to X start the retry. */ X static int startscsi(unit) int unit; { X int a; X X d[unit].xferbuf=d[unit].savedbuf=d[unit].origbuf; X d[unit].xferlen=d[unit].savedlen=d[unit].origlen; X d[unit].xfercmd=d[unit].savedcmd=d[unit].origcmd; X X startagain: X #ifdef DEBUG0 X printf("scsi: arbitrating for %d\n",unit); #endif X a=arbitrate(unit); X if (a != COK) /* arbitrate does the necessary retries */ X return a; X #ifdef DEBUG0 X printf("scsi: arbitration complete\n"); #endif X while (1) X { X a=doxfernosleep(unit); #ifdef DEBUG0 X printf("scsi: doxfernosleep returned %d=%s\n", X a, Cresults[(a>=0 && a<7 ? a : 7)]); #endif X if (a == CDISCONNECT) X { /* The target disconnected */ X if (d[unit].xferpolled) X { #ifdef DEBUG0 X printf("scsi: polled wait\n"); #endif X if (!polledwaitreconnect(unit)) X goto retry; #ifdef DEBUG0 X printf("scsi: polled wait complete - reconnected\n"); #endif X continue; X } X if (d[unit].currentbuf) X { /* We are doing io for a buffer */ X /* All we have to do is to return; intr will call iodone. */ X d[unit].xfertime=1; /* enable timeouts */ X return CDISCONNECT; X } X X /* disconnect; we do not have a buffer but may use intrs */ X /* This is not too efficient, as the delay from wakeup to X continuing execution might be substantial, but this is not X a typical case, as transfers do not normally go to X internal buffers. */ X d[unit].xfertime=1; /* enable timeouts */ X if (sleep(&d[unit].connected,MYSLEEPPRI|PCATCH) == 1) X { /* caught a signal */ X d[unit].busy=0; /* I guess this is an atomic operation */ X return CERROR; X } X if (!d[unit].connected) X goto retry; /* it must have been a timeout */ X continue; X } X if (a == COK || a == CNOCONNECT || a == CBUSBUSY) X { X if (a != COK) X marknotbusy(unit,a); X return a; X } X goto retry; X } X retry: X /* a possibly recoverable error was encountered */ #ifdef DEBUG X printf("scsi: startscsi: retrying or failing\n"); #endif X if (d[unit].xferretries > 1) X { X d[unit].xferretries--; X goto startagain; X } X if (a == CTIMEOUT || a == CBUSBUSY) X resetscsibus(); /* in case the drive was hanging on the bus */ X d[unit].connected=0; X *cmdport=CMDBASE; X marknotbusy(unit,a); X return a; /* too many retries - return error */ } X /* This executes the given command on the unit. This returns command status X (C* constants). There is no need to retry the operation after calling X this. */ X static int doscsicmd(unit,cmd,buf,len,timeout,retries,slow,phys,polled,bp) int unit, /* drive number */ X len, /* buffer size */ X timeout, /* timeout in 1/10 secs if != 0 */ X retries, /* number of tries before returning failure; 1=try only once */ X slow, /* set to true if slow transfer (only true for read & write) */ X phys, /* set to true if xfer directly to/from user space (raw io) */ X polled; /* set to true if polled transfer */ unchar *cmd, /* command to execute */ X *buf; /* transfer buffer address */ struct buf *bp; /* io buffer being executed, or NULL */ { X int x; X #ifdef DEBUG0 X printf("scsi: cmd unit=%d buf=%x len=%d timeout=%d retries=%d slow=%d phys=%d polled=%d bp=%x\n", X unit,buf,len,timeout,retries,slow,phys,polled,bp); #endif X X x=splnointrs(); X if (d[unit].busy) X { X if (bp) X { X splx(x); X return CBUSY; X } X while (d[unit].busy) X { X splx(x); X if (sleep(&d[unit].busy,MYSLEEPPRI) == 1) X { /* caught a signal */ X return CERROR; X } X x=splnointrs(); X } X } X d[unit].origbuf=buf; X d[unit].origlen=len; X d[unit].origcmd=cmd; X d[unit].xferslow=slow; X d[unit].xferstatus=0x01; /* indicates error */ X d[unit].xfertimeout=timeout?timeout+1:0; X d[unit].xfertime=0; X d[unit].xferretries=retries; X d[unit].xferphys=phys; X d[unit].xferpolled=polled; X d[unit].currentbuf=bp; X d[unit].busy=1; X splx(x); X X return startscsi(unit); } X static int dorw(unit,sec,buf,len,flags,polled,bp) int unit; long sec; unchar *buf; int len,flags,polled; struct buf *bp; { X unchar cmd[10]; X int nblocks,a; X X a=d[unit].blocksize; X if (a == 0) X a=512; X nblocks=(len+a-1)/a; X #ifdef DEBUG0 X printf("scsi: dorw: unit=%d sec=%d buf=%x len=%d flags=%x polled=%d bp=%x\n", X unit,sec,buf,len,flags,polled,bp); #endif X X cmd[0]=(flags & B_READ)?SCSIREAD:SCSIWRITE; X cmd[1]=0x00; /* LU & RADDR */ X cmd[2]=sec >> 24; X cmd[3]=sec >> 16; X cmd[4]=sec >> 8; X cmd[5]=sec; X cmd[6]=0; X cmd[7]=nblocks >> 8; X cmd[8]=nblocks; X cmd[9]=0; X X return doscsicmd(unit,cmd,buf,len,RWTIMEOUT,3,0,flags & B_PHYS,polled,bp); } X /* This starts an io operation on the given buffer. This is called when X a new buffer is added to the io queue, and when a previous operation has X completed, to start io on the next buffer. If the unit is busy, this will X do nothing. If it is not busy, this will start the request. This should X be called with splnointrs. Any routines called by this will not change X the interrupt level to a lower value. */ SHAR_EOF true || echo 'restore of scsi.c failed' fi echo 'End of part 2' echo 'File scsi.c is continued in part 3' echo 3 > _shar_seq_.tmp exit 0 -- +----------------------------------------------------------------- Station Zebra ....!{claris,zorch}!szebra!tin Sunnyvale, CA (408) 739-1520 24hrs Telebit+ 300-19200bps Pub *NIX, Usenet and mail (no fee)