[alt.sources] ST-0X 386/ix driver pt 2/6

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)