Return to BSD News archive
Newsgroups: comp.unix.bsd Path: sserve!manuel.anu.edu.au!munnari.oz.au!uunet!timbuk.cray.com!hemlock.cray.com!overby From: overby@cray.com (Glen Overby) Subject: [Source] Seagate ST01/ST02 SCSI driver Message-ID: <1992Nov1.154143.1034@hemlock.cray.com> Summary: source for sys.i386bsd/i386/isa/st01-[1s].[cs] Lines: 1880 Nntp-Posting-Host: cypress32 Organization: Yuppie suburbs Date: 1 Nov 92 15:41:43 CST /* This is a re-release of my Seagate (and maybe Future Domain) driver for 0.1 The README describes it all. I've deposited a copy on agate in incoming/ST01.shar */ #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # If this archive is complete, you will see the following message at the end: # "End of shell archive." # # Contents: # README st01-1.c st01-s.s conf.c.diff # # Wrapped by overby@cray.com on Sun Nov 1 15:36:44 1992 # PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f README -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"README\" else echo shar: Extracting \"README\" \(3794 characters\) sed "s/^X//" >README <<'END_OF_README' XHistory: X5/28/92 First release of my ST-01 driver: works on 386BSD 0.0 X10/31/92 Second release for 0.1 and assmebly-level copy X XHardware: X XThis driver is written to support a Seagate ST-01/ST-02 controler. This Xis supposedly a re-labeled Future Domain (unsure what model number). If Xyou are successful using it with a Future Domain (or if you're not), Xplease tell me. X XThe Seagate ST-01 is a simiple non-DMA SCSI host adapter which resides Xentirely in memory (no I/O space is taken). X XIt uses interrupts (IRQ3 or IRQ5) to notify the CPU of a reconnect from target, Xand will insert wait-states durring data in/out phases to synchronize the host XCPU with the SCSI device. X XYou may configure the board to reside at any of it's possible addresses and Xinterrupt numbers (just be sure they match your configuration file). If you Xplan to use the assembly-level copy routines, you MUST enable the 0WS X(wait-state) jumper. X XIf you do NOT have an ISA disk drive, you should disable the IDA disk register, Xotherwise the wd driver will think that there is a WD controler available. X X Jumpers X XST-01B ST-02 XJumper/Connector Description X X W1 JP1 BIOS address selection X W2 JP2 0WS Enable X W3 JP3 IRQ Enable X JP6 Register Enable/Disable X W5 J7 Channel Activity LED connector X J2 J2 50-pin SCSI connector X J3 J6 Drive power connector X XSource: Seagate ST01/ST02 Product Manual, Rev. G Pub No. 36027-002 X XThe Driver X XThe code to talk to the ST-01 controler itself is derived from a Xdriver for Microport System V/386 Copyright 1988 by by Tatu Yl|nen Xwith BSD-specific driver code liberally stolen from sys/vax/uda/uba.c Xand sys/i386/isa/wd.c. X XINSTALLATION X XCopy st01-1.c to src/sys/i386/isa XCopy st01-s.s to src/sys/i386/isa X Xadd the following to your configuration file: X Xcontroler sg0 at isa? bio irq 5 iomem 0xC8000 iosiz 0x2000 vector sgintr Xdisk dk8 at sg0 drive 0 X XThis assumes your controler is at the first ST01 address and uses IRQ5. X XNOTE: Interrupts MUST be enabled on the card! The factory setting Xdoes not enable interrupts. X XIf you do NOT enable interrupts, you must go into the code and disable the Xdriver's use of SCSI disconnects (which certainly can't hurt performance Xif you have one device). X Xadd to src/sys/i386/conf/files.i386: X Xi386/isa/st01-1.c optional sg device-driver Xi386/isa/st01-s.s optional sg device-driver X Xadd the driver to src/sys/i386/i386/conf.c (conf.c.diff) X XCheck the driver source (st01-1.c) for the proper definition of "ASM": Xdefined if you want to use the assembly-level data copy, undefined otherwise. XIf you have problems, undefine it. X Xconfigure your system with 'config'. X XManually edit "swap386bsd.c" to add a swap space entry: X Xstruct swdevt swdevt[] = { X { makedev(5, 1), 0, 0 }, /* sg0b */ X { makedev(0, 1), 0, 0 }, /* wd0b */ X { 0, 0, 0 } X}; X Xthis is requred because 'config' doesn't recognise 'sg' on the 'swap' Xparameter. X Xfinally, compile your new system! X XLIMITATIONS X XI have only tested the code with one SCSI target, a Seagate ST-296N X80MB hard drive, allthough there is nothing in the code intended to Xprevent it from working with a full bus. This probably won't work for Xcartridge tape drives. X XPROBLEMS X XThe config code for 386BSD does not do anything with 'disk' Xdefinitions. This should be changed to be like what a VAX does and Xdefine a structure for each disk. Then the device driver will get a Xisa_device structure for each configured SCSI target. X XFUTURE PLANS X XI think the code that handles the low-level SCSI bus handling is Xinefficient and rather grody (too many gotos for my liking). I have Xbegun sketching out a completely new low-level interface. I'll make it Xpart of Julian's generic SCSI system. X XGlen Overby Xoverby@plains.nodak.edu / overby@{cobber,nic}.cord.edu / overby@cray.com X END_OF_README if test 3794 -ne `wc -c <README`; then echo shar: \"README\" unpacked with wrong size! fi # end of overwriting check fi if test -f st01-1.c -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"st01-1.c\" else echo shar: Extracting \"st01-1.c\" \(41764 characters\) sed "s/^X//" >st01-1.c <<'END_OF_st01-1.c' X/*- X * Seagate ST-01/02 driver. X * $Id: st01.c,v 1.4 92/05/19 22:19:30 overby Exp Locker: overby $ X * X * Derived from: X * sys/i386/isa/wd.c @(#)wd.c 7.2 (Berkeley) 5/9/91 X * sys/vax/uba/uda.c @(#)uda.c 7.32 (Berkeley) 2/13/91 X * (Chris Torek's driver) X * sys/vax/vax/mscpvar.h @(#)mscpvar.h 7.3 (Berkeley) 6/28/90 X * Header: scsi.c,v 1.3 90/01/23 11:33:00 brian Locked X */ X X#include "sg.h" X#if NSG > 0 X X#define DEBUG3 X#define ASM X X#include "param.h" X#include "dkbad.h" X#include "systm.h" X#include "conf.h" X#include "file.h" X#include "stat.h" X#include "ioctl.h" X#include "disklabel.h" X#include "buf.h" X#include "uio.h" X#include "i386/isa/isa_device.h" X#include "i386/isa/icu.h" X#include "syslog.h" X#include "vm/vm.h" X X/* sys/vax/vax/mscpvar.h @(#)mscpvar.h 7.3 (Berkeley) 6/28/90 */ X/* X * The following macro appends a buffer to a drive queue or a drive to X * a controller queue, given the name of the forward link. Use as X * `APPEND(dp, &um->um_tab, b_forw)' or `APPEND(bp, dp, av_forw)', X * where `bp' is a transfer request, `dp' is a drive queue, and `um_tab' X * is a controller queue. (That is, the forward link for controller X * queues is `b_forw'; for drive queues, it is `av_forw'.) X */ X#define APPEND(bp, queue, link) { \ X (bp)->link = NULL; \ X if ((queue)->b_actf == NULL) \ X (queue)->b_actf = (bp); \ X else \ X (queue)->b_actl->link = (bp); \ X (queue)->b_actl = (bp); \ X} X X/* wd.c */ X#define MAXTRANSFER 32 /* max size of transfer in page clusters */ X X Xint sgprobe(), sgattach(), sgintr(); Xstruct isa_driver sgdriver = { X sgprobe, sgattach, "sg", X}; X X/* X * Target info, per Target X */ Xstruct target_info { X int ti_rw; X int ti_flags; X int ti_state; /* open/closed state */ X X daddr_t ti_dsize; /* size in sectors */ X daddr_t ti_ssize; /* sector size */ X int ti_type; /* drive type */ X u_long ti_mediaid; /* media id */ X struct ti_geom { /* geometry information */ X#ifdef notyet X u_short rg_nsectors; /* sectors/track */ X u_short rg_ngroups; /* track groups */ X u_short rg_ngpc; /* groups/cylinder */ X u_short rg_ntracks; /* ngroups*ngpc */ X u_short rg_ncyl; /* ti_dsize/ntracks/nsectors */ X u_short rg_rctsize; /* size of rct */ X u_short rg_rbns; /* replacement blocks per track */ X u_short rg_nrct; /* number of rct copies */ X#endif X } ti_geom; X int ti_wlabel; /* label sector is currently writable */ X X u_long ti_openpart; /* partitions open */ X u_long ti_bopenpart; /* block partitions open */ X u_long ti_copenpart; /* character partitions open */ X X struct disklabel ti_label; X struct dos_partition ti_part[NDOSPART]; X struct dkbad ti_bad; /* bad sector table */ X X} t_info[NSG * 8]; X X/* X * Software state, per target X */ X#define CLOSED 0 X#define WANTOPEN 1 X#define RDLABEL 2 X#define OPEN 3 X#define OPENRAW 4 X X/* X * flags... X */ X#define UNIT_ONLINE 0x01000 X X#define SGFL_DOSPART 0x00001 /* has DOS partition table */ X#define SGFL_QUIET 0x00002 /* report errors back, but don't complain */ X#define SGFL_SINGLE 0x00004 /* sector at a time mode */ X#define SGFL_ERROR 0x00008 /* processing a disk error */ X#define SGFL_BSDLABEL 0x00010 /* has a BSD disk label */ X#define SGFL_BADSECT 0x00020 /* has a bad144 badsector table */ X#define SGFL_WRITEPROT 0x00040 /* manual unit write protect */ X X/* X * Active SCSI commands (one for each target) X */ Xtypedef struct target_status { X char *command, *data, *status; /* send/recieve buffers */ X int length; X struct { /* saved send/recieve buffers */ X char *command, *data, *status; X int length; X } save; X char command_buf[12], status_buf[2]; /* the buffers themselves */ X volatile char *cmdport, *dataport; /* HACK! controller addresses */ X int connected, /* TRUE if we're connected right now */ X busy, /* TRUE if a command is in progress */ X retries, /* how bad are things? */ X nomsgs, /* don't say we can do disconnects */ X xfertimeout, xferstatus, xfertime, xferslow, X blocksize, X target_id; /* SCSI target ID we're talking with */ X} ACTIVE; X XACTIVE active[NSG * 8]; /* one for each target on each controler */ X Xstruct isa_device *sgcinfo[NSG]; /* Seagate Controler info */ Xstruct isa_device *sgtinfo[NSG * 8]; /* SCSI Target info */ X Xstruct buf sgqueue[NSG * 8]; /* per-drive transfer queue */ X X/* wd.c X * Device to unit number and partition and back X * bits use X * 0-3 partition X * 4-5 unit X * 6 dos partitions X * 7 ignore partition table X */ X#define sgnoreloc(dev) (minor(dev) & 0x80) /* ignore partition table */ X#define sgdospart(dev) (minor(dev) & 0x40) /* use dos partitions */ X#define scsiunit(dev) ((minor(dev) & 0x38) >> 3) X#define scsipart(dev) (minor(dev) & 0x7) X#define makesgdev(maj, unit, part) (makedev(maj,((unit<<3)+part))) X#define SGRAW 3 /* 'd' partition isn't a partition! */ X X/* X#define TARGETSHIFT 3 X#define TARGETMASK 7 X#define PARTMASK 7 X X#define scsiunit(dev) ((minor(dev) >> TARGETSHIFT) & TARGETMASK) X#define scsipart(dev) (minor(dev) & PARTMASK) X#define scsiminor(u, p) (((u) << TARGETSHIFT) | (p)) X*/ X X#define SCSIRAMOFS 0x00001800 X#define SCSICMDOFS 0x00001a00l X#define SCSIDATOFS 0x00001c00l X X X/* Internal command returns [st01.c] */ X#define COK 0 /* command completed successfully */ X#define CNOCONNECT 1 /* no connection could be made to the drive */ X#define CBUSBUSY 2 /* the bus is busy and cannot be cleared */ X#define CTIMEOUT 3 /* timeout waiting for the drive */ X#define CERROR 4 /* an error was returned by the target */ X#define CBUSY 5 /* the drive is busy - wait and retry later */ X#define CDISCONNECT 6 /* target disconnected; this is not an error */ X X/* ST-01 Status Register */ X#define STARBCOMPL 0x80 /* arbitration complete bit */ X#define STPARERR 0x40 /* parity error bit */ X#define STSEL 0x20 /* scsi select */ X#define STREQ 0x10 /* scsi req */ X#define STCD 0x08 /* scsi c/d */ X#define STIO 0x04 /* scsi i/o */ X#define STMSG 0x02 /* scsi msg */ X#define STBSY 0x01 /* scsi busy */ X Xstatic int scsi_arbitrate(), scsi_machine(); X Xint int_happened =0; Xint resel_fail =0; Xint resel_loop =0; X X/* X * Probe routine X */ Xsgprobe(dvp) X struct isa_device *dvp; X{ char *st01c; X st01c = dvp->id_maddr; /* only uses memory, no I/O */ X#ifdef DEBUG1 X printf("st01 id_maddr=0x%lx\n", st01c); X#endif X#ifdef lint X st01intr(0); X#endif X X *(st01c+SCSIRAMOFS) = 0xa5; /* write to RAM */ X *(st01c+SCSIRAMOFS+1) = 0x5a; /* write to RAM */ X X /*printf("st01 rom 0x%x 0x%x\n", *st01c, *(st01c+1));*/ X#ifdef DEBUG1 X printf("st01 ram 0x%x (@0x%lx)\n", X *(st01c+SCSIRAMOFS), (st01c+SCSIRAMOFS)); X#endif X /*if( *st01c != (char) 0xa5 && *st01c == (char) 0x55 && *(st01c+SCSIRAMOFS) == (char) 0xa5)*/ X if( *(st01c+SCSIRAMOFS) == (char) 0xa5 && *(st01c+SCSIRAMOFS+1) == (char) 0x5a ) { X#ifdef DEBUG1 X printf("st01 OK!\n"); X#endif X return(1); X } X return (0); X} X X/* X * attach each target if possible. X */ Xsgattach(dvp) X struct isa_device *dvp; X{ X int target; /* = dvp->id_unit; */ X X#ifdef DEBUG1 X printf("sgattach: %d\n", dvp->id_unit); X#endif X X sgcinfo[dvp->id_unit] = dvp; X X for(target = 0; target <1; target++) { X actsetup(dvp, &active[target], target); X sgslave(&active[target]); X } X} X X/* set up stuff in *active for this controler/target */ Xactsetup(dvp, active, target) X struct isa_device *dvp; X ACTIVE *active; X int target; X{ X active->cmdport = dvp->id_maddr + SCSICMDOFS; X active->dataport = dvp->id_maddr + SCSIDATOFS; X active->target_id = 1<<target; X active->connected = active->busy = active->retries = 0; X#ifdef DEBUG1 X printf("C = 0x%x D = 0x%x T = 0x%x \n",active->cmdport, X active->dataport, active->target_id); X#endif X} X X/* Look for a slave device; print out everything about it. */ Xsgslave(t) X ACTIVE *t; X{ int a; X char *sense_err(); X X/* initdrive(unit) */ X if( (a = testready( t )) != COK) { X if( a == CERROR || a == CBUSY ) { /* Bad News */ X t->target_id = 0; /* mark it vaporware */ X return(a); X } X } X if(t->status_buf[0] & 0x2) { X sense_err(t); X } X} X X/* X * Open a drive. X */ X/*ARGSUSED*/ Xsgopen(dev, flag, fmt) X dev_t dev; X int flag, fmt; X{ X register int unit; X register struct disklabel *lp; X register struct partition *pp; X register struct target_info *ti; X int s, i, part, mask, error = 0; X daddr_t start, end; X X printf("sgopen: dev=0x%x flag=0x%x fmt=0x%x\n", dev, flag, fmt); X /* X * Make sure this is a reasonable open request. X */ X unit = scsiunit(dev); X if (unit >= NSG /*|| (ti = &t_info[unit])->ti_flags) /* & AVAILABLE == 0)*/) X { printf("sgopen: Unit %d higher than max (%d)\n", unit, NSG); X return (ENXIO); X } X X ti = &t_info[unit]; X s = splhigh(); X /* X * Wait for the state to settle X */ X while(ti->ti_state != OPEN && ti->ti_state != OPENRAW && X ti->ti_state != CLOSED) X if (error = tsleep((caddr_t)ti->ti_state, (PZERO + 1) | PCATCH, X devopn, 0)) { X splx(s); X return(error); X } X /* X * If not on line, or we are not sure of the label, reinitialise X * the drive. X */ X if (ti->ti_flags & UNIT_ONLINE == 0 || X (ti->ti_state != OPEN && ti->ti_state != OPENRAW)) /* OPEN? */ X error = targ_init(ti, dev, flag); X X splx(s); X if (error) { X printf("sgopen: targ_init error\n"); X return (error); X } X X ti->ti_flags |= UNIT_ONLINE; X part = scsipart(dev); X lp = &ti->ti_label; X if (part >= lp->d_npartitions) { X printf("sgopen: part %d > max %d\n", part, lp->d_npartitions); X return (ENXIO); X } X X /* X * Warn if a partition is opened that overlaps another X * already open, unless either is the `raw' partition X * (whole disk). X */ X#define RAWPART 2 /* 'c' partition */ /* XXX */ X mask = 1 << part; X if ((ti->ti_openpart & mask) == 0 && part != RAWPART) { X pp = &lp->d_partitions[part]; X start = pp->p_offset; X end = pp->p_offset + pp->p_size; X for (pp = lp->d_partitions, i = 0; X i < lp->d_npartitions; pp++, i++) { X if (pp->p_offset + pp->p_size <= start || X pp->p_offset >= end || i == SGRAW /*RAWPART*/) X continue; X if (ti->ti_openpart & (1 << i)) X log(LOG_WARNING, X "sg%d%c: overlaps open partition (%c)\n", X unit, part + 'a', i + 'a'); X } X } X switch (fmt) { X case S_IFCHR: X ti->ti_copenpart |= mask; X break; X case S_IFBLK: X ti->ti_bopenpart |= mask; X break; X } X ti->ti_openpart |= mask; X return (0); X} X X/* ARGSUSED */ Xsgclose(dev, flags, fmt) X dev_t dev; X int flags, fmt; X{ X register int unit; X register struct target_info *ti; X int s, mask = (1 << scsipart(dev)); X X unit = scsiunit(dev); X ti = &t_info[unit]; X X switch (fmt) { X case S_IFCHR: X ti->ti_copenpart &= ~mask; X break; X case S_IFBLK: X ti->ti_bopenpart &= ~mask; X break; X } X ti->ti_openpart = ti->ti_copenpart | ti->ti_bopenpart; X X /* X * Should wait for I/O to complete on this partition even if X * others are open, but wait for work on blkflush(). X */ X if (ti->ti_openpart == 0) { X s = splhigh(); X while (sgqueue[unit].b_actf) X sleep((caddr_t)&sgqueue[unit], PZERO - 1); X splx(s); X ti->ti_state = CLOSED; X ti->ti_wlabel = 0; X } X return (0); X} X X/* X * Initialise a drive. If it is not already, bring it on line, X * and set a timeout on it in case it fails to respond. X * When on line, read in the pack label. X */ Xtarg_init(ti, dev, flags) X register struct target_info *ti; X int dev, flags; X{ X register struct disklabel *lp; X register int unit; X char *msg, *readdisklabel(); X int s, i, sgstrategy(); X extern int cold; X char *inquiry(), *readcapacity(), *db; X X if (flags & O_NDELAY) X return (0); X X unit = scsiunit(dev); X X ti->ti_state = RDLABEL; X X /* X * Read SCSI-level device info X */ X if((db=inquiry( &active[unit] )) != NULL) { X *(db+31) = '\0'; *(db+45) = '\0'; X printf("open: %d(0x%x) %s HW %d FW %d ROM %d SER %s\n", X unit, dev, X db+8, *(db+32)&0xff, *(db+33)&0xff, *(db+34)&0xff, db+36); X } X X if((db=readcapacity( &active[unit] )) != NULL) { X ti->ti_dsize = ((db[0]&0xff) << 24) | ((db[1]&0xff) << 16) | X ((db[2]&0xff) << 8) | (db[3]&0xff); X ti->ti_ssize = ((db[4]&0xff) << 24) | ((db[5]&0xff) << 16) | X ((db[6]&0xff) << 8) | (db[7]&0xff); X printf("open: Capacity: %ld sector size=%ld\n", ti->ti_dsize, ti->ti_ssize); X } X X /* X * Set up default sizes until we have the label, or longer X * if there is none. Set secpercyl, as readdisklabel wants X * to compute b_cylin (although we do not need it), and set X * nsectors in case diskerr is called. X */ X lp = &ti->ti_label; X lp->d_secsize = DEV_BSIZE; X lp->d_secperunit = ti->ti_dsize; X lp->d_secpercyl = 1; X lp->d_npartitions = 1; X lp->d_secsize = 512; X lp->d_secperunit = ti->ti_dsize; X lp->d_nsectors = 17; /*ti->ti_geom.rg_nsectors;*/ X lp->d_partitions[0].p_size = lp->d_secperunit; X lp->d_partitions[0].p_offset = 0; X X /* X * Read pack label. X */ X if ((msg = readdisklabel( X makesgdev(major(dev), scsiunit(dev), SGRAW), X sgstrategy, X lp, ti->ti_part, &ti->ti_bad, 0)) != NULL) { X X if (cold) X printf(": %s", msg); X else X log(LOG_ERR, "sd%d: %s", unit, msg); X#ifdef COMPAT_42 X if (udamaptype(unit, lp)) X ra->ra_state = OPEN; X else X ra->ra_state = OPENRAW; X#else X ti->ti_state = OPENRAW; X printf("MakeFakeLabel!\n"); X sg_makefakelabel(ti, lp); X#endif X } else { X ti->ti_flags |= SGFL_BSDLABEL; X ti->ti_flags &= ~SGFL_WRITEPROT; X if (ti->ti_label.d_flags & D_BADSECT) X ti->ti_flags |= SGFL_BADSECT; X ti->ti_state = OPEN; X } X X wakeup((caddr_t)ti->ti_state); X return (0); X} X Xsg_makefakelabel(ti, lp) X register struct target_info *ti; X register struct disklabel *lp; X{ X lp->d_secsize = ti->ti_ssize; X lp->d_nsectors = 17; X lp->d_ntracks = 10; X lp->d_ncylinders = 1024; X lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks; X lp->d_npartitions = 8; X lp->d_partitions[0].p_offset = 0; X lp->d_partitions[0].p_size = ti->ti_dsize; X} X Xsgsize(dev_t dev) X{ X int unit = scsiunit(dev), part = scsipart(dev), val; X struct target_info *ti; X X if (unit >= NSG) X return(-1); X X ti = &t_info[unit]; Xprintf("sgsize: open? %d dev 0x%x unit %d\n", ti->ti_flags & SGFL_WRITEPROT, X dev, unit); X X if (ti->ti_state == CLOSED) X val = sgopen (makesgdev(major(dev), unit, SGRAW), FREAD, S_IFBLK, 0); X/* if (val != 0 || ti->ti_flags & SGFL_WRITEPROT) X return (-1);*/ X Xprintf("sgsize rc = %d\n", (int)ti->ti_label.d_partitions[part].p_size); X return((int)ti->ti_label.d_partitions[part].p_size); X} X X/* X * Queue a transfer request, and if possible, hand it to the controller. X * X * This routine is broken into two so that the internal version X * udastrat1() can be called by the (nonexistent, as yet) bad block X * revectoring routine. X */ Xsgstrategy(bp) X register struct buf *bp; X{ X register int unit; X register struct target_info *ti; X struct partition *pp; X int p; X daddr_t sz, maxsz; X X#ifdef DEBUG2 X printf("sgstrat: dev= 0x%x blk=%d\n", bp->b_dev, bp->b_blkno); X#endif X X /* X * Make sure this is a reasonable drive to use. X */ X if ((unit = scsiunit(bp->b_dev)) >= NSG || sgcinfo[unit] == NULL || X (ti = &t_info[unit])->ti_state == CLOSED) { X printf("sgstrat: %d is 'unreasonable'\n", unit); X bp->b_error = ENXIO; X goto bad; X } X X /* "soft" write protect check */ X if ((ti->ti_flags & SGFL_WRITEPROT) && (bp->b_flags & B_READ) == 0) { X bp->b_error = EROFS; X bp->b_flags |= B_ERROR; X goto bad; X } X X /* have partitions and want to use them? */ X if ((ti->ti_flags & SGFL_BSDLABEL) != 0 && scsipart(bp->b_dev) != SGRAW) { X X /* X * do bounds checking, adjust transfer. if error, process. X * if end of partition, just return X */ X if (bounds_check_with_label(bp, &ti->ti_label, ti->ti_wlabel) <= 0) X goto bad; X /* otherwise, process transfer request */ X X p = scsipart(bp->b_dev); X if ((ti->ti_openpart & (1 << p)) == 0) { X printf("sgstrat: partition %d not open\n", p); X bp->b_flags |= B_ERROR; X bp->b_error = ENODEV; X goto bad; X } X } X X sgstrat1(bp); X return; Xbad: X biodone(bp); X} X X/* X * Work routine for udastrategy. X */ Xsgstrat1(bp) X register struct buf *bp; X{ X register int unit = scsiunit(bp->b_dev); X register struct buf *dp; X struct target_info *ti; X int s = splhigh(); X X /* X * Append the buffer to the drive queue, and if it is not X * already there, the drive to the controller queue. (However, X * if the drive queue is marked to be requeued, we must be X * awaiting an on line or get unit status command; in this X * case, leave it off the controller queue.) X */ X dp = &sgqueue[unit]; X APPEND(bp, dp, av_forw); X X /*disksort(dp, bp);*/ /* more efficient... someday */ X X/* X if (dp->b_active == 0 && (ui->ui_flags & UNIT_REQUEUE) == 0) { X APPEND(dp, &um->um_tab, b_forw); X dp->b_active++; X } X */ X X /* X * Start activity on the controller. Note that unlike other X * Unibus drivers, we must always do this, not just when the X * controller is not active. X */ X sgstart(dp,s); X splx(s); X} X Xsgstart(hp) X struct buf *hp; X{ X int unit, count, rc; X register struct buf *bp; X long l; X register struct target_info *ti; X X ACTIVE *a; X u_long blknum; X X do { X if(!(bp = hp->av_forw)) return; X X unit = scsiunit(bp->b_dev); X ti = &t_info[unit]; X X if((a = &active[unit])->busy) { X#ifdef DEBUG2 X printf("sgstart: Already Busy\n"); X#endif X return; X } X a->busy = 1; X X blknum = (unsigned long) bp->b_blkno * DEV_BSIZE / 512; /*xx->dk_dd.d_secsize;*/ X count = bp->b_bcount; X X#ifdef DEBUG2 Xprintf("sgstart: b_blkno= %lu", bp->b_blkno); Xprintf(" LBA=%lu TL=%d PO=%lu\n", blknum, count, ti->ti_label.d_partitions[scsipart(bp->b_dev)].p_offset); X/*printf("sgstart: b_flags=0x%x\n", bp->b_flags);*/ X#endif X X /* add on partition offsets */ X blknum += ti->ti_label.d_partitions[scsipart(bp->b_dev)].p_offset; X X if((u_long) bp->b_blkno * DEV_BSIZE % 512 /*xx->dk_dd.d_secsize*/ || X bp->b_bcount >= MAXTRANSFER * CLBYTES ) { X bp->b_flags |= B_ERROR; X bp->b_error = EINVAL; X X hp->av_forw = bp->av_forw; X X biodone(bp); X a->busy = a->connected = 0; X return; X } X X a->command_buf[0] = (bp->b_flags & B_READ) ? 0x08 : 0x0A; /* SCSIREAD : SCSIWRITE; */ X a->command_buf[1] = (blknum >> 16) & 0x1f; X a->command_buf[2] = (blknum >> 8) & 0xff; X a->command_buf[3] = blknum & 0xff; X a->command_buf[4] = count / 512; X a->command_buf[5] = 0; X X a->command = a->command_buf; a->status = a->status_buf; X a->data = (char *)bp->b_un.b_daddr; X a->length = count; X a->nomsgs = 0; X a->xferslow = 0; X X rc = scsi_arbitrate(a); X#ifdef DEBUG1 X printf("sgstart: arbitrate = %d\n", rc); X#endif X } while(talk_scsi(hp, a) == 0); X} X X Xtalk_scsi(hp, a) X struct buf *hp; X register ACTIVE *a; X{ register struct buf *bp; X int rc; X X if(!(bp = hp->av_forw)) return; X X rc = scsi_machine(a); X#ifdef DEBUG1 X printf("talk: SCSI_machine = %d <%d:%d>\n", rc, resel_fail, resel_loop); X resel_fail = 0; X#endif X X switch(rc) { X case CDISCONNECT: X a->connected = 0; X#ifdef DEBUG1 X printf("talk: Disconnect!\n"); X#endif X return(1); X case CTIMEOUT: X case CERROR: X bp->b_flags |= B_ERROR; bp->b_error = EIO; X break; X case COK: X#ifdef DEBUG1 X printf("talk: I/O Complete... \n"); X#endif X bp->b_flags |= B_DONE; bp->b_resid = 0; X break; X default: X printf("talk: unexpected scsi_machine return 0x%x\n", rc); X return(1); X } X X a->busy = a->connected = 0; X X hp->av_forw = bp->av_forw; X biodone(bp); X return(0); X} X Xsgintr(unit) X{ ACTIVE *a; X int t, t1, rc; X struct buf *hp, *bp; X X int_happened++; X X a = &active[0]; /* just for a starting place */ X if(!(*a->cmdport & STSEL)) { X printf("sgintr: ignored (No SEL)\n"); X return; X } X if((t=reconnect(a))) { X for(rc=1,t1=t;t1>>1;rc++) ; X#ifdef DEBUG1 X printf("sgintr: 0x%x (%d) reconnected <%d>\n",t,rc,resel_fail); X resel_fail = 0; X#endif X if(!(a = &active[rc-1])->busy) { X printf("sgintr: uh-oh! 0x%x (%d) it's not busy\n", t, rc); X } X a->connected = 1; X X if(talk_scsi((hp=&sgqueue[rc-1]), a) == 0) { X#ifdef DEBUG1 X printf("sgintr: try another\n"); X#endif X sgstart(hp); X } X } else { X resel_fail++; X /*printf("sgintr: reconnect failure\n");*/ X } X} X Xsgioctl(dev, cmd, addr, flag) X dev_t dev; X caddr_t addr; X{ X register struct target_info *ti; X int unit, rc = 0; X X ti = &t_info[(unit=scsiunit(dev))]; X X switch(cmd) { X case DIOCGDINFO: X *(struct disklabel *)addr = ti->ti_label; X break; X X case DIOCGPART: X ((struct partinfo *)addr)->disklab = &ti->ti_label; X ((struct partinfo *)addr)->part = X &ti->ti_label.d_partitions[scsipart(dev)]; X break; X X case DIOCSDINFO: X printf("sgioctl: DIOCSDINFO\n"); X if ((flag & FWRITE) == 0) X rc = EBADF; X else X rc = setdisklabel(&ti->ti_label, X (struct disklabel *)addr, X 0, /*(dk->dk_state == OPENRAW) ? 0 : dk->dk_openpart*/ X ti->ti_part); X if(rc == 0) X ti->ti_flags |= SGFL_BSDLABEL; X X /*if (error == 0 && dk->dk_state == OPENRAW && X vdreset_drive(vddinfo[unit])) X dk->dk_state = OPEN;*/ X /*wdsetctlr(dev, du);*/ X break; X X case DIOCWLABEL: X printf("sgioctl: DIOCWLABEL\n"); X if ((flag & FWRITE) == 0) X rc = EBADF; X else X ti->ti_wlabel = *(int *)addr; X break; X X case DIOCWDINFO: X printf("sgioctl: DIOCWDINFO\n"); X if ((flag & FWRITE) == 0) X rc = EBADF; X else if ((rc = setdisklabel(&ti->ti_label, X (struct disklabel *)addr, X 0, /*(dk->dk_state == OPENRAW) ? 0 : dk->dk_openpart*/ X ti->ti_part)) == 0) { X int wlab; X X ti->ti_flags |= SGFL_BSDLABEL; X /*if (rc == 0 && dk->dk_state == OPENRAW && X vdreset_drive(vddinfo[unit])) X dk->dk_state = OPEN; */ X /*wdsetctlr(dev, du);*/ X X /* simulate opening partition 0 so write succeeds */ X /* dk->dk_openpart |= (1 << 0); /* XXX */ X wlab = ti->ti_wlabel; X ti->ti_wlabel = 1; X rc = writedisklabel(dev, sgstrategy, X &ti->ti_label, X ti->ti_part); X /*scsipart(dev));*/ X /*dk->dk_openpart = dk->dk_copenpart | dk->dk_bopenpart;*/ X ti->ti_wlabel = wlab; X } X break; X X default: X rc = ENOTTY; X } X return(rc); X} X Xtestready(a) X ACTIVE *a; X{ int rc; X X a->command_buf[0] = 0; /*SCSITESTREADY;*/ X a->command_buf[1] = 0; a->command_buf[2] = 0; X a->command_buf[3] = 0; a->command_buf[4] = 0; X a->command_buf[5] = 0; X X a->command = a->command_buf; a->status = a->status_buf; X a->data = 0; a->length = 0; X a->nomsgs = 0; X X rc = scsi_arbitrate(a); X#ifdef DEBUG1 X printf("Arbitrate = %d\n", rc); X#endif X if(rc == COK) X rc = scsi_machine(a); X#ifdef DEBUG1 X printf("testready: scsi_machine = %d status = 0x%x\n", rc, a->status_buf[0]); X#endif X return(rc); X} X Xchar * Xsense_err(a) X ACTIVE *a; X{ int rc; X X static char sensedata[128]; X X a->command_buf[0] = 0x03; /*SCSI_INQUIRY;*/ X a->command_buf[1] = 0; a->command_buf[2] = 0; X a->command_buf[3] = 0; a->command_buf[4] = 27; X a->command_buf[5] = 0; X X a->command = a->command_buf; a->status = a->status_buf; X a->data = sensedata; a->length = 27; X a->nomsgs = 0; X a->xferslow = 1; a->blocksize = 512; X X rc = scsi_arbitrate(a); X#ifdef DEBUG1 X printf("sense: arbitrate = %d\n", rc); X#endif X if(rc == COK) X rc = scsi_machine(a); X#ifdef DEBUG1 X printf("sense: scsi_machine = %d status = 0x%x\n", rc, a->status_buf[0]); X#else X printf("sense error:\n"); X dumpmem(sensedata, 27); X#endif X return((rc == COK) ? sensedata : (char *)0); X} X Xchar * Xinquiry(a) X ACTIVE *a; X{ int rc; X X static char inqdata[128]; X X a->command_buf[0] = 0x12; /*SCSI_INQUIRY;*/ X a->command_buf[1] = 0; a->command_buf[2] = 0; X a->command_buf[3] = 0; a->command_buf[4] = 66; X a->command_buf[5] = 0; X X a->command = a->command_buf; a->status = a->status_buf; X a->data = inqdata; a->length = 66; X a->nomsgs = 0; X a->xferslow = 1; a->blocksize = 512; X X rc = scsi_arbitrate(a); X#ifdef DEBUG1 X printf("inquiry: arbitrate = %d\n", rc); X#endif X if(rc == COK) X rc = scsi_machine(a); X#ifdef DEBUG1 X printf("inquiry: scsi_machine = %d\n", rc); X printf("inquiry: Status Ret = 0x%x \n", a->status_buf[0]); X /*dumpmem(inqdata, 66);*/ X#endif X return((rc == COK) ? inqdata : (char *)0); X} X Xchar * Xreadcapacity(a) X ACTIVE *a; X{ int rc; X X static char capdata[128]; X unsigned int capacity, blocksize; X X a->command_buf[0] = 0x25; /*SCSI_READ-CAPACITY;*/ X a->command_buf[1] = 0; a->command_buf[2] = 0; X a->command_buf[3] = 0; a->command_buf[4] = 0; X a->command_buf[5] = 0; a->command_buf[6] = 0; X a->command_buf[7] = 0; a->command_buf[8] = 0; X a->command_buf[9] = 0; X X a->command = a->command_buf; a->status = a->status_buf; X a->data = capdata; a->length = 8; X a->nomsgs = 0; X X a->xferslow = 1; a->blocksize = 512; X X rc = scsi_arbitrate(a); X#ifdef DEBUG1 X printf("ReadCapacity: arbitrate = %d\n", rc); X#endif X if(rc == COK) X rc = scsi_machine(a); X#ifdef DEBUG1 X printf("ReadCapacity: scsi_machine = %d status = 0x%x\n", rc, a->status_buf[0]); X/* dumpmem(capdata, 8); X capacity = ((capdata[0]&0xff) << 24) | (capdata[1] << 16) | (capdata[2] << 8) | (capdata[3] & 0xff); X blocksize = (capdata[4] << 24) | (capdata[5] << 16) | (capdata[6] << 8) | capdata[7]; X printf("ReadCapacity: %u blocksize %u\n", capacity, blocksize); */ X#endif X return((rc == COK) ? capdata : (char *)0); X} X X Xdumpmem(mem, len) X char *mem; X int len; X{ X X char *line; X int off; X X for(off=0; off<len; off++,mem++) { X if(off%16 == 0) printf("\n0x%04x ", off); X if((*mem&0x7f) > ' ' && (*mem&0x7f) <= '~') X printf(" %c ", *mem&0x7f); X else printf("%02x ", *mem & 0xff); X } X printf("\n"); X} X/*#define DEBUG*/ X X/* Header: scsi.c,v 1.3 90/01/23 11:33:00 brian Locked */ X/* X XSCSI disk driver for unix system V (Microport system V/386) XThis driver uses the ST-01 controller. This supports multiple initiators Xand multiple targets. X XCopyright (c) 9.6.1988 Tatu Yl|nen X All rights reserved. X XFixes and mods by Brian E. Litzinger 12/1/1989 X*/ X X/*#define COPYRIGHT "scsi disk driver V1.0 Copyright (c) 9.6.1988 Tatu Yl|nen\n V1.1 changes by Brian E. Litzinger 12/1/1989"*/ X X/*#define ASM /* use certain routines coded in assembly */ X X#define RWTIMEOUT 5 /* timeout for read/write waiting for reconnect */ X X#define splnointrs() splbio() /* disable any interrupts to this driver (clock!)*/ X X/*#define NULL 0*/ X X#define MYTARGETID 0x80 /* my address as bit mask */ X X/* ST-01 Control Register */ X#define CMDENABLE 0x80 /* scsi enable */ X#define CMDENINTR 0x40 /* enable scsi interrupts */ X#define CMDPARENB 0x20 /* enable scsi parity generation */ X#define CMDSTARB 0x10 /* start arbitration bit */ X#define CMDATTN 0x08 /* scsi attention */ X#define CMDBSY 0x04 /* scsi busy */ X#define CMDSEL 0x02 /* scsi select */ X#define CMDRST 0x01 /* scsi reset */ X X#define CMDBASE (CMDPARENB|CMDENINTR) /* cmd when doing nothing */ X X/* ST-01 Status Register */ X X/* SCSI CCS commands */ X#define SCSIREAD 0x28 /* read command code (10-byte) */ X#define SCSIWRITE 0x2a /* write command code (10-byte) */ X#define SCSIINQUIRY 0x12 /* inquiry command (6-byte) */ X#define SCSIREADCAPACITY 0x25 /* read drive capacity and block size */ X#define SCSIMODESELECT 0x15 /* select format parameters */ X#define SCSIFORMATUNIT 0x04 /* hard format the scsi drive */ X#define SCSIREQSENSE 0x03 /* request sense command */ X#define SCSITESTREADY 0x00 /* test unit ready command */ X X#define MSGMYIDENTIFY 0xc0 /* our identify message to send to target */ X X/* SCSI Reply Message */ X#define MSGCOMPLETE 0x00 /* command complete */ X#define MSGSAVEDATAPTR 0x02 /* save data pointer */ X#define MSGRESTOREPTR 0x03 /* restore pointer */ X#define MSGDISCONNECT 0x04 /* disconnect message */ X#define MSGIDETECTERR 0x05 /* initiator detected error */ X#define MSGABORT 0x06 /* scsi abort message */ X#define MSGMSGREJECT 0x07 /* message reject */ X#define MSGNOP 0x08 /* no operation message */ X#define MSGIDENTIFY 0x80 /* identify message from target */ X X#define SCSIDATAPORTSZ 1024 /* size of data port on st01 */ X Xstatic char timeouting=0; Xstatic char intrserviced=0; X#ifdef DEBUG Xstatic int scsidebug=8; X#endif X X/* This generates a hard reset on the scsi bus by asserting the reset line */ X Xstatic scsi_resetbus(a) X ACTIVE *a; X{ X long l; X int u; X X printf("st01: sending hard reset to scsi bus\n"); X *a->cmdport = CMDBASE|CMDENABLE|CMDRST; X for (l=0;l<100000L;l++); /* keep rst asserted for a while */ X *a->cmdport = CMDBASE; X for (l=0;l<5000000L;l++); /* give some time to recover before returning */ X /*for (u=0;u<SCSIMAXDRIVES;u++) X d[u].connected=0; /* do this just in case */ X} X 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 Xstatic int scsi_arbitrate(a) X ACTIVE *a; X{ X long l; X int arbcnt, bsycnt; /* retry counts */ X X register int o, n; X X arbcnt = bsycnt = 0; X retryarb: X *a->cmdport = CMDBASE; X *a->dataport= MYTARGETID; X *a->cmdport = (CMDBASE&~CMDENINTR)|CMDSTARB; X /* wait for arbitration complete */ X for (l=0;l<3000000L;l++) { X if ((n=*a->cmdport) & STARBCOMPL) X goto gotarb; X if(o!=n) { /*printf("0x%02x\n",n);*/ o=n; } X } X /* arbitration timeout - someone is keeping the bus reserved. */ X *a->cmdport = CMDBASE; X if (arbcnt >= 2) /* retry twice, then give up */ X { X printf("st01: arbitration timeout - someone is sitting on the bus?\n"); X return CBUSBUSY; X } X scsi_resetbus(); /* reset the bus and hope the condition clears */ X arbcnt++; X goto retryarb; X gotarb: X arbcnt=0; X *a->dataport = MYTARGETID | a->target_id; X *a->cmdport = (a->nomsgs) ? X CMDBASE|CMDENABLE|CMDSEL : X CMDBASE|CMDENABLE|CMDSEL|CMDATTN; X for (l=0;l<2000000L;l++) X if (*a->cmdport & STBSY) X goto gotbusy; X /* timeout waiting for busy */ X *a->cmdport = CMDBASE; X if (bsycnt >= 2) { X#ifdef DEBUG X if (scsidebug>0) X printf("st01: arbitrate returning CNOCONNECT\n"); X#endif X return CNOCONNECT; /* probably no drive present */ X } X bsycnt++; X for (l=0;l<2000l;l++); /* give some time for the drive */ X printf("st01: busy timeout on drive %d\n",a->target_id); X goto retryarb; X gotbusy: X a->connected = 1; X if (!a->nomsgs) X { X *a->cmdport = CMDBASE|CMDENABLE|CMDATTN; X for (l=0;l<500000;l++) X if ((*a->cmdport & (STMSG|STCD|STIO|STREQ)) == (STMSG|STCD|0|STREQ)) X goto gotmsgreq; X /* timeout waiting for msg out */ X printf("st01: timeout identify msg out - drive %d does not support messages?\n", X a->target_id); X a->nomsgs = 1; /* don't try messages again */ X *a->cmdport = CMDBASE|CMDENABLE; X return COK; X gotmsgreq: X *a->dataport = MSGMYIDENTIFY; /* this enables disconnect */ X /* fall to successful completion */ X } X#ifdef DEBUG X if (scsidebug>5) X if (!(*a->cmdport & STBSY)) { X printf("st01: after successful arbitrate !STBSY\n"); X for (l=0;l<10000000l;l++); X arbcnt++; X goto retryarb; X } X#endif X *a->cmdport = CMDBASE|CMDENABLE; X return COK; X} X 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 Xstatic sendtoscsi(buf,len,port) Xregister char *buf; Xregister int len; Xvolatile register char *port; X{ X /*printf("sendtoscsi: 0x%x %d\n", buf, len);*/ X while (len--) X *port = (*buf++); X} X X/* This reads data from the scsi data port as fast as possible. */ Xstatic getfromscsi(buf,len,port) Xregister char *buf; Xregister int len; Xvolatile register char *port; X{ /*printf("getfromscsi: 0x%x %d 0x%x\n", buf, len, port);/**/ X while (len--) X *buf++ = *port; X} X X#endif /* ASM */ X 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 en 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 Xstatic int Xdataout(a) X ACTIVE *a; X{ X register int le; X register long l; X X#ifdef DEBUG X /*if (scsidebug==-3)*/ X printf("dataout: xfering 0x%x\n", a->length); X#endif X for (;a->length > 0;a->length -= le, a->data += le) X { X for (l=0;l<100000;l++) X if (*a->cmdport & STREQ) X goto gotreq; X /* timeout */ X break; X gotreq: X if ((*a->cmdport & (STMSG|STCD|STIO)) != (0|0|0)) X break; X if (a->xferslow) X { X le=1; X *a->dataport = *a->data; X continue; X } X le= a->length; X if (le > SCSIDATAPORTSZ) X le=SCSIDATAPORTSZ; X if (le > a->blocksize) X le= a->blocksize; X /* elide transfer from user space crap */ X sendtoscsi(a->data, le, a->dataport); X } X while ((*a->cmdport & (STMSG|STCD|STIO|STREQ)) == (0|0|0|STREQ)) X { printf("."); X *a->dataport = 0; X for (l=0;l<100000;l++) X if (*a->cmdport & STREQ) X break; X } X return 1; X} X 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 Xstatic int Xdatain(a) X ACTIVE *a; X{ X register int le; X long l; X X#ifdef DEBUG X /*if (scsidebug==-3) */ X printf("datain: xfering %x\n",a->length); X#endif X for (;a->length > 0; a->length -= le, a->data += le) { X for (l=0;l<1000000;l++) X if (*a->cmdport & STREQ) X goto gotreq; X#ifdef DEBUG X /*if (scsidebug==-3)*/ X printf("datain: timed out waiting for STREQ!\n"); X#endif X /* timeout */ X break; X gotreq: X if ((*a->cmdport & (STMSG|STCD|STIO)) != (0|0|STIO)) X break; X if (a->xferslow) { X le=1; *a->data = (*a->dataport); X continue; X } X X le = a->length; X if (le > SCSIDATAPORTSZ) X le = SCSIDATAPORTSZ; X if (le > a->blocksize) X le = a->blocksize; X /* elide transfer to user space crap */ X getfromscsi(a->data, le, a->dataport); X#ifdef DEBUG X /*if (scsidebug==-3) */ X printf(" xferd 0x%x", le); X#endif X } X#ifdef DEBUG X /*if (scsidebug==-3)*/ X printf("\n"); X#endif X while ((*a->cmdport & (STMSG|STCD|STIO|STREQ)) == (0|0|STIO|STREQ)) { X printf("."); X le = (*a->dataport); X for (l=0; l < 100000;l++) X if (*a->cmdport & STREQ) X break; X } X return 1; X} X 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 if 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 Xstatic int Xscsi_machine(a) XACTIVE *a; X{ X int msg; X long l; X int i; X for (l=0;l<100000 || a->xfertimeout == 0;l++) { X if (!(*a->cmdport & STBSY)) { X#ifdef DEBUG X if (scsidebug>5) X printf("st01: doxfernosleep: !STBSY unit %d\n",a->target_id); X#endif X a->connected=0; X return CERROR; /* we are no longer connected??? */ X } X if (!(*a->cmdport & STREQ)) X continue; /* loop until target requesting something */ X#ifdef DEBUG1 X if ((scsidebug==-1) | (scsidebug>5)) X printf("st01: scsmachine: new state=%x ",*a->cmdport); X#endif X switch ((*a->cmdport & (STMSG|STCD|STIO)) & 0xff) X { X case 0|0|0: /* data out */ X#ifdef DEBUG1 X printf("DATA OUTPUT phase\n"); X#endif X if (!dataout(a)) { X#ifdef DEBUG X if (scsidebug>0) X printf("st01: dataout returned error; unit=%d\n",a->target_id); X#endif X return CERROR; X } X break; X case 0|0|STIO: /* data in */ X#ifdef DEBUG1 X printf("DATA INPUT phase\n"); X#endif X if (!datain(a)) X { X#ifdef DEBUG X if (scsidebug>0) X printf("st01: datain returned error; unit=%d\n",a->target_id); X#endif X return CERROR; X } X break; X case 0|STCD|0: /* command out */ X#ifdef DEBUG1 X printf("command out 0x%x\n", *a->command); X#endif X *a->dataport=(*a->command++); X break; X case 0|STCD|STIO: /* status in */ X *a->status=(*a->dataport); X#ifdef DEBUG1 X printf("status in 0x%x\n", *a->status); X#endif 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. */ X#ifdef DEBUG X if ((scsidebug==-2) || (scsidebug==-1) || (scsidebug>0)) X printf("st01: unexpected msg out state; status=%x, ignored\n",*a->cmdport); X#endif X *a->dataport = MSGNOP; /* send a no-operation message */ X *a->cmdport = CMDBASE|CMDENABLE; X break; X case STMSG|STCD|STIO: /* msg in */ X msg = (*a->dataport) & 0xff; X#ifdef DEBUG1 X printf("msg in 0x%x\n", msg); X#endif X switch (msg) X { X case MSGCOMPLETE: X a->connected = 0; X *a->cmdport = CMDBASE; X#ifdef DEBUG X if (scsidebug>3) X printf("command MSGCOMPLETE received\n"); X#endif X if (a->xferstatus == 0) /* completed succesfully */ X { X /* marknotbusy(unit,COK);*/ X#ifdef DEBUG X if (scsidebug>3) X printf("st01: MSGCOMPLETE successful\n"); X#endif X return COK; X } X#ifdef DEBUG X if (scsidebug>0) X printf("st01: MSGCOMPLETE failed\n"); X#endif X return CERROR; X case MSGSAVEDATAPTR: X a->save.data = a->data; X a->save.length = a->length; X#ifdef DEBUG X if (scsidebug>3) X printf("st01: MSGSAVEDATAPTR received\n"); X#endif X break; X case MSGRESTOREPTR: X a->command = a->save.command; X a->data = a->save.data; X a->status = a->save.status; X a->length = a->save.length; X#ifdef DEBUG X if (scsidebug>3) X printf("st01: MSGRESTOREPPTR received\n"); X#endif X break; X case MSGDISCONNECT: X a->connected=0; X a->xfertime=1; X *a->cmdport = CMDBASE; X#ifdef DEBUG X if (scsidebug>3) X printf("st01: MSGDISCONNECT received\n"); X#endif X return CDISCONNECT; X case MSGMSGREJECT: X#ifdef DEBUG1 X printf("st01: MSGMSGREJECT\n"); X#endif X break; /* the target rejected some message... Who cares. */ X case MSGNOP: X#ifdef DEBUG1 X printf("st01: MSGNOP\n"); X#endif X break; /* this should not be sent by the target, but... */ X case MSGIDENTIFY: X#ifdef DEBUG1 X printf("st01: MSGIDENTIFY\n"); X#endif X break; /* we don't care about targets identify messages */ X default: X if (msg & 0x80) { X#ifdef DEBUG1 X printf("st01: default Identify\n"); X#endif X break; /* assume it is an identify message */ X } X printf("st01: unknown message received from drive %d: %x\n", X a->target_id, msg); 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. */ X printf("st01: unexpected bus state: status=%x\n",*a->cmdport); X break; X } X } X#ifdef DEBUG X if (scsidebug>0) X printf("st01: doxfernosleep timed out\n"); X#endif X return CTIMEOUT; X} X X/* This performs the actual reconnect. The return value is the target that X * requested the reconnect. If it is 0 (false), the reconnect was not for us. X */ Xreconnect(a) X ACTIVE *a; X{ long l; X unsigned char ch; X X#ifdef KLUDGE X for(l=10;l && ((*a->cmdport) & (STSEL|STIO|STBSY)) != (STSEL|STIO|0);l--) X ; X resel_loop = (int) l; X#endif X X X if (!((ch=*a->cmdport) & (STSEL))) { X#ifdef DEBUG1 X printf("st01: wrong state 0x%x\n",ch); X#endif X return(0); X } X X for(l=5;l && ((*a->cmdport) & (STSEL|STIO|STBSY)) != (STSEL|STIO|0);l--) X ; X resel_loop = (int) l; X X ch=(*a->dataport); X if (!(ch & MYTARGETID)) { X#ifdef DEBUG3 X /* if (scsidebug>0)*/ X printf("st01: polled releselection was not for me: %x\n",ch); X#endif X return(0); X } X ch&=~MYTARGETID; X if (ch != a->target_id) { X#ifdef DEBUG3 X /* if (scsidebug>0)*/ X printf("st01: reselecting (polled) unit other than expected: %x\n", ch); X#endif X return(0); X } X *a->cmdport=(CMDBASE&~CMDENINTR)|CMDBSY|CMDENABLE; X for (l=0;l<50000;l++) X if (!(*a->cmdport & STSEL)) X break; X for (l=0;l<50000;l++) X if (!(*a->cmdport & STBSY)) X break; X *a->cmdport=CMDBASE|CMDENABLE; X /*a->connected=1;*/ X#ifdef DEBUG X if (scsidebug>3) X printf("st01: polledwaitreconnect returned (true)\n"); X#endif X return ch; X} X X#endif END_OF_st01-1.c if test 41764 -ne `wc -c <st01-1.c`; then echo shar: \"st01-1.c\" unpacked with wrong size! fi # end of overwriting check fi if test -f st01-s.s -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"st01-s.s\" else echo shar: Extracting \"st01-s.s\" \(1234 characters\) sed "s/^X//" >st01-s.s <<'END_OF_st01-s.s' 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 X#static sendtoscsi(buf,len,port) X#register char *buf; X#register int len; X#volatile register char *port; X X#NO_APP Xgcc_compiled.: X.text X .align 2 X.globl _sendtoscsi X_sendtoscsi: X pushl %ebp X movl %esp,%ebp X pushl %ebx X pushl %edi X pushl %esi X pushl %ecx X movl 8(%ebp),%esi # buf X movl 16(%ebp),%edi # port X movl 12(%ebp),%ecx # len X rep X movsb X# jmp L6 X#L4: X# movb (%esi),%bl X# movb %bl,(%edi) X# incl %esi X#L6: X# decl %ecx X# cmpl $-1,%ecx X# jne L4 X popl %ecx X popl %esi X popl %edi X leal -4(%ebp),%esp X popl %ebx X leave X ret X .align 2 X# This reads data from the scsi data port as fast as possible X#static getfromscsi(buf,len,port) X#register char *buf; X#register int len; X#volatile register char *port; X.globl _getfromscsi X_getfromscsi: X pushl %ebp X movl %esp,%ebp X pushl %ebx X pushl %edi X pushl %esi X pushl %ecx X movl 8(%ebp),%edi # buf X movl 16(%ebp),%esi # port X movl 12(%ebp),%ecx # len X rep X movsb X# jmp L12 X#L10: X# movb (%esi),%al X# movb %al,(%edi) X# incl %edi X#L12: X# decl %ecx X# cmpl $-1,%ecx X# jne L10 X popl %ecx X popl %esi X popl %edi X leal -4(%ebp),%esp X popl %ebx X leave X ret X X# rep X# movsb X END_OF_st01-s.s if test 1234 -ne `wc -c <st01-s.s`; then echo shar: \"st01-s.s\" unpacked with wrong size! fi # end of overwriting check fi if test -f conf.c.diff -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"conf.c.diff\" else echo shar: Extracting \"conf.c.diff\" \(1172 characters\) sed "s/^X//" >conf.c.diff <<'END_OF_conf.c.diff' X*** conf.c.orig Sun Nov 1 15:34:13 1992 X--- sgconf.c Sat Oct 31 16:28:06 1992 X*************** X*** 102,107 **** X--- 102,120 ---- X #define fdsize NULL X #endif X X+ #include "sg.h" X+ #if NSG > 0 X+ int sgopen(),sgclose(),sgstrategy(),sgioctl(),sgsize(); X+ #define sgdump enxio X+ #else X+ #define sgopen enxio X+ #define sgclose enxio X+ #define sgstrategy enxio X+ #define sgioctl enxio X+ #define sgdump enxio X+ #define sgsize NULL X+ #endif X+ X int swstrategy(),swread(),swwrite(); X X struct bdevsw bdevsw[] = X*************** X*** 116,121 **** X--- 129,136 ---- X wtdump, wtsize, B_TAPE }, X { asopen, asclose, asstrategy, asioctl, /*4*/ X asdump, assize, NULL }, X+ { sgopen, sgclose, sgstrategy, sgioctl, /*5*/ X+ sgdump, sgsize, NULL } X }; X int nblkdev = sizeof (bdevsw) / sizeof (bdevsw[0]); X X*************** X*** 214,219 **** X--- 229,237 ---- X { asopen, asclose, rawread, rawwrite, /*D*/ X asioctl, enodev, nullop, NULL, X seltrue, enodev, asstrategy }, X+ { sgopen, sgclose, rawread, rawwrite, /*E*/ X+ sgioctl, enodev, nullop, NULL, X+ seltrue, enodev, sgstrategy }, X }; X int nchrdev = sizeof (cdevsw) / sizeof (cdevsw[0]); X END_OF_conf.c.diff if test 1172 -ne `wc -c <conf.c.diff`; then echo shar: \"conf.c.diff\" unpacked with wrong size! fi # end of overwriting check fi echo shar: End of shell archive. exit 0