2015 lines
52 KiB
C
2015 lines
52 KiB
C
/*
|
||
* (Free/Net/386)BSD ST01/02, Future Domain TMC-885, TMC-950 SCSI driver for
|
||
* Julians SCSI-code
|
||
*
|
||
* Copyright 1994, Kent Palmkvist (kentp@isy.liu.se)
|
||
* Copyright 1994, Robert Knier (rknier@qgraph.com)
|
||
* Copyright 1992, 1994 Drew Eckhardt (drew@colorado.edu)
|
||
* Copyright 1994, Julian Elischer (julian@tfs.com)
|
||
*
|
||
* Others that has contributed by example code is
|
||
* Glen Overby (overby@cray.com)
|
||
* Tatu Yllnen
|
||
* Brian E Litzinger
|
||
*
|
||
* Redistribution and use in source and binary forms, with or without
|
||
* modification, are permitted provided that the following conditions
|
||
* are met:
|
||
* 1. Redistributions of source code must retain the above copyright
|
||
* notice, this list of conditions and the following disclaimer.
|
||
* 2. Redistributions in binary form must reproduce the above copyright
|
||
* notice, this list of conditions and the following disclaimer in the
|
||
* documentation and/or other materials provided with the distribution.
|
||
*
|
||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND
|
||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||
* ARE DISCLAIMED. IN NO EVENT SHALL THE DEVELOPERS BE LIABLE
|
||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||
* SUCH DAMAGE.
|
||
*/
|
||
|
||
/*
|
||
*
|
||
* kentp 940307 alpha version based on newscsi-03 version of Julians SCSI-code
|
||
* kentp 940314 Added possibility to not use messages
|
||
* rknier 940331 Added fast transfer code
|
||
* rknier 940407 Added assembler coded data transfers
|
||
*
|
||
* $Id: seagate.c,v 1.2 1994/10/27 05:23:09 phk Exp $
|
||
*/
|
||
|
||
/*
|
||
* What should really be done:
|
||
*
|
||
* Add missing tests for timeouts
|
||
* Restructure interrupt enable/disable code (runs to long with int disabled)
|
||
* Find bug? giving problem with tape status
|
||
* Add code to handle Future Domain 840, 841, 880 and 881
|
||
* adjust timeouts (startup is very slow)
|
||
* add code to use tagged commands in SCSI2
|
||
* Add code to handle slow devices better (sleep if device not disconnecting)
|
||
* Fix unnecessary interrupts
|
||
*/
|
||
|
||
/* Note to users trying to share a disk between DOS and unix:
|
||
* The ST01/02 is a translating host-adapter. It is not giving DOS
|
||
* the same number of heads/tracks/sectors as specified by the disk.
|
||
* It is therefore important to look at what numbers DOS thinks the
|
||
* disk has. Use these to disklabel your disk in an appropriate manner
|
||
*/
|
||
|
||
#include <sys/types.h>
|
||
#ifdef KERNEL /* don't laugh.. look for main() */
|
||
#include "sea.h"
|
||
#include <sys/param.h>
|
||
#include <sys/systm.h>
|
||
#include <sys/errno.h>
|
||
#include <sys/ioctl.h>
|
||
#include <sys/malloc.h>
|
||
#include <sys/buf.h>
|
||
#include <sys/proc.h>
|
||
#include <sys/user.h>
|
||
#include <sys/kernel.h>
|
||
#include <sys/devconf.h>
|
||
#include <i386/isa/isa_device.h>
|
||
#endif /* KERNEL */
|
||
#include <scsi/scsi_all.h>
|
||
#include <scsi/scsiconf.h>
|
||
|
||
#ifndef KERNEL
|
||
#define NSEA 1
|
||
#endif /* !KERNEL */
|
||
|
||
#define SEA_SCB_MAX 8 /* allow maximally 8 scsi control blocks */
|
||
#define SCB_TABLE_SIZE 8 /* start with 8 scb entries in table */
|
||
#define BLOCK_SIZE 512 /* size of READ/WRITE areas on SCSI card */
|
||
|
||
/*
|
||
* defining PARITY causes parity data to be checked
|
||
*/
|
||
#define PARITY 1
|
||
|
||
/*
|
||
* defining SEA_BLINDTRANSFER will make DATA IN and DATA OUT to be done with
|
||
* blind transfers, i.e. no check is done for scsi phase changes. This will
|
||
* result in data loss if the scsi device does not send its data using
|
||
* BLOCK_SIZE bytes at a time.
|
||
* If SEA_BLINDTRANSFER defined and SEA_ASSEMBLER also defined will result in
|
||
* the use of blind transfers coded in assembler. SEA_ASSEMBLER is no good
|
||
* without SEA_BLINDTRANSFER defined.
|
||
*/
|
||
#define SEA_BLINDTRANSFER 1 /* do blind transfers */
|
||
#define SEA_ASSEMBLER 1 /* Use assembly code for fast transfers */
|
||
|
||
/*
|
||
* defining SEANOMSGS causes messages not to be used (thereby disabling
|
||
* disconnects)
|
||
*/
|
||
/* #define SEANOMSGS 1 */
|
||
|
||
/*
|
||
* defining SEA_NODATAOUT makes dataout phase being aborted
|
||
*/
|
||
/* #define SEA_NODATAOUT 1 */
|
||
|
||
/*
|
||
* defining SEA_SENSEFIRST make REQUEST_SENSE opcode to be placed first
|
||
*/
|
||
/* #define SEA_SENSEFIRST 1 */
|
||
|
||
/* Debugging definitions. Should not be used unless you want a lot of
|
||
printouts even under normal conditions */
|
||
|
||
/* #define SEADEBUG 1 */ /* General info about errors */
|
||
/* #define SEADEBUG1 1 */ /* Info about internal results and errors */
|
||
/* #define SEADEBUG2 1 */ /* Display a lot about timeouts etc */
|
||
/* #define SEADEBUG3 1 */
|
||
/* #define SEADEBUG4 1 */
|
||
/* #define SEADEBUG5 1 */
|
||
/* #define SEADEBUG6 1 */ /* Display info about queue-lengths */
|
||
/* #define SEADEBUG7 1 */ /* Extra check on STATUS before phase check */
|
||
/* #define SEADEBUG8 1 */ /* Disregard non-BSY state in
|
||
sea_information_transfer */
|
||
/* #define SEADEBUG9 1 */ /* Enable printouts */
|
||
/* #define SEADEBUG11 1 */ /* stop everything except access to scsi id 1 */
|
||
/* #define SEADEBUG15 1 */ /* Display every byte sent/received */
|
||
|
||
#define NUM_CONCURRENT 1 /* number of concurrent ops per board */
|
||
|
||
/******************************* board definitions **************************/
|
||
/*
|
||
* CONTROL defines
|
||
*/
|
||
|
||
#define CMD_RST 0x01 /* scsi reset */
|
||
#define CMD_SEL 0x02 /* scsi select */
|
||
#define CMD_BSY 0x04 /* scsi busy */
|
||
#define CMD_ATTN 0x08 /* scsi attention */
|
||
#define CMD_START_ARB 0x10 /* start arbitration bit */
|
||
#define CMD_EN_PARITY 0x20 /* enable scsi parity generation */
|
||
#define CMD_INTR 0x40 /* enable scsi interrupts */
|
||
#define CMD_DRVR_ENABLE 0x80 /* scsi enable */
|
||
|
||
/*
|
||
* STATUS
|
||
*/
|
||
|
||
#define STAT_BSY 0x01 /* scsi busy */
|
||
#define STAT_MSG 0x02 /* scsi msg */
|
||
#define STAT_IO 0x04 /* scsi I/O */
|
||
#define STAT_CD 0x08 /* scsi C/D */
|
||
#define STAT_REQ 0x10 /* scsi req */
|
||
#define STAT_SEL 0x20 /* scsi select */
|
||
#define STAT_PARITY 0x40 /* parity error bit */
|
||
#define STAT_ARB_CMPL 0x80 /* arbitration complete bit */
|
||
|
||
/*
|
||
* REQUESTS
|
||
*/
|
||
|
||
#define REQ_MASK (STAT_CD | STAT_IO | STAT_MSG)
|
||
#define REQ_DATAOUT 0
|
||
#define REQ_DATAIN STAT_IO
|
||
#define REQ_CMDOUT STAT_CD
|
||
#define REQ_STATIN (STAT_CD | STAT_IO)
|
||
#define REQ_MSGOUT (STAT_MSG | STAT_CD)
|
||
#define REQ_MSGIN (STAT_MSG | STAT_CD | STAT_IO)
|
||
|
||
#define REQ_UNKNOWN 0xff
|
||
|
||
#define SEAGATERAMOFFSET 0x00001800
|
||
|
||
#ifdef PARITY
|
||
#define BASE_CMD (CMD_EN_PARITY | CMD_INTR)
|
||
#else
|
||
#define BASE_CMD (CMD_INTR)
|
||
#endif
|
||
|
||
#define SEAGATE 1
|
||
#define FD 2
|
||
|
||
/******************************************************************************
|
||
* This should be placed in a more generic file (presume in /sys/scsi)
|
||
* Message codes:
|
||
*/
|
||
#define MSG_ABORT 0x06
|
||
#define MSG_NOP 0x08
|
||
#define MSG_COMMAND_COMPLETE 0x00
|
||
#define MSG_DISCONNECT 0x04
|
||
#define MSG_IDENTIFY 0x80
|
||
#define MSG_BUS_DEV_RESET 0x0c
|
||
#define MSG_MESSAGE_REJECT 0x07
|
||
#define MSG_SAVE_POINTERS 0x02
|
||
#define MSG_RESTORE_POINTERS 0x03
|
||
/******************************************************************************/
|
||
|
||
#define IDENTIFY(can_disconnect,lun) (MSG_IDENTIFY | ((can_disconnect) ? \
|
||
0x40 : 0) | ((lun) & 0x07))
|
||
|
||
/* scsi control block used to keep info about a scsi command */
|
||
struct sea_scb
|
||
{
|
||
int flags; /* status of the instruction */
|
||
#define SCB_FREE 0
|
||
#define SCB_ACTIVE 1
|
||
#define SCB_ABORTED 2
|
||
#define SCB_TIMEOUT 4
|
||
#define SCB_ERROR 8
|
||
#define SCB_TIMECHK 16 /* We have set a timeout on this one */
|
||
struct sea_scb *next; /* in free list */
|
||
struct scsi_xfer *xfer; /* the scsi_xfer for this cmd */
|
||
u_char * data; /* position in data buffer so far */
|
||
int32 datalen; /* bytes remaining to transfer */;
|
||
};
|
||
|
||
/*
|
||
* data structure describing current status of the scsi bus. One for each
|
||
* controller card.
|
||
*/
|
||
struct sea_data
|
||
{
|
||
caddr_t basemaddr; /* Base address for card */
|
||
char ctrl_type; /* FD or SEAGATE */
|
||
caddr_t st0x_cr_sr; /* Address of control and status register */
|
||
caddr_t st0x_dr; /* Address of data register */
|
||
u_short vect; /* interrupt vector for this card */
|
||
int our_id; /* our scsi id */
|
||
int numscb; /* number of scsi control blocks */
|
||
struct scsi_link sc_link; /* struct connecting different data */
|
||
struct sea_scb *connected; /* currently connected command */
|
||
struct sea_scb *issue_queue; /* waiting to be issued */
|
||
struct sea_scb *disconnected_queue; /* waiting to reconnect */
|
||
struct sea_scb scbs[SCB_TABLE_SIZE];
|
||
struct sea_scb *free_scb; /* free scb list */
|
||
volatile unsigned char busy[8]; /* index=target, bit=lun, Keep track of
|
||
busy luns at device target */
|
||
} *seadata[NSEA];
|
||
|
||
/* flag showing if main routine is running. */
|
||
static volatile int main_running = 0;
|
||
|
||
#define STATUS (*(volatile unsigned char *) sea->st0x_cr_sr)
|
||
#define CONTROL STATUS
|
||
#define DATA (*(volatile unsigned char *) sea->st0x_dr)
|
||
|
||
/*
|
||
* These are "special" values for the tag parameter passed to sea_select
|
||
* Not implemented right now.
|
||
*/
|
||
|
||
#define TAG_NEXT -1 /* Use next free tag */
|
||
#define TAG_NONE -2 /*
|
||
* Establish I_T_L nexus instead of I_T_L_Q
|
||
* even on SCSI-II devices.
|
||
*/
|
||
|
||
typedef struct {
|
||
char *signature ;
|
||
unsigned offset;
|
||
unsigned length;
|
||
unsigned char type;
|
||
} BiosSignature;
|
||
|
||
/*
|
||
* Signatures for automatic recognition of board type
|
||
*/
|
||
|
||
static const BiosSignature signatures[] = {
|
||
{"ST01 v1.7 (C) Copyright 1987 Seagate", 15, 37, SEAGATE},
|
||
{"SCSI BIOS 2.00 (C) Copyright 1987 Seagate", 15, 40, SEAGATE},
|
||
|
||
/*
|
||
* The following two lines are NOT mistakes. One detects ROM revision
|
||
* 3.0.0, the other 3.2. Since seagate has only one type of SCSI adapter,
|
||
* and this is not going to change, the "SEAGATE" and "SCSI" together
|
||
* are probably "good enough"
|
||
*/
|
||
|
||
{"SEAGATE SCSI BIOS ", 16, 17, SEAGATE},
|
||
{"SEAGATE SCSI BIOS ", 17, 17, SEAGATE},
|
||
|
||
/*
|
||
* However, future domain makes several incompatible SCSI boards, so specific
|
||
* signatures must be used.
|
||
*/
|
||
|
||
{"FUTURE DOMAIN CORP. (C) 1986-1989 V5.0C2/14/89", 5, 45, FD},
|
||
{"FUTURE DOMAIN CORP. (C) 1986-1989 V6.0A7/28/89", 5, 46, FD},
|
||
{"FUTURE DOMAIN CORP. (C) 1986-1990 V6.0105/31/90",5, 47, FD},
|
||
{"FUTURE DOMAIN CORP. (C) 1986-1990 V6.0209/18/90",5, 47, FD},
|
||
{"FUTURE DOMAIN CORP. (C) 1986-1990 V7.009/18/90", 5, 46, FD},
|
||
{"FUTURE DOMAIN CORP. (C) 1992 V8.00.004/02/92", 5, 44, FD},
|
||
{"FUTURE DOMAIN TMC-950", 5, 21, FD},
|
||
};
|
||
|
||
#define NUM_SIGNATURES (sizeof(signatures) / sizeof(BiosSignature))
|
||
|
||
static const char * seagate_bases[] = {
|
||
(char *) 0xc8000, (char *) 0xca000, (char *) 0xcc000,
|
||
(char *) 0xce000, (char *) 0xdc000, (char *) 0xde000
|
||
};
|
||
|
||
#define NUM_BASES (sizeof(seagate_bases)/sizeof(char *))
|
||
|
||
int sea_probe(struct isa_device *dev);
|
||
int sea_attach(struct isa_device *dev);
|
||
int seaintr(int unit);
|
||
int32 sea_scsi_cmd(struct scsi_xfer *xs);
|
||
void sea_timeout(caddr_t, int);
|
||
void seaminphys(struct buf *bp);
|
||
void sea_done(int unit, struct sea_scb *scb);
|
||
u_int32 sea_adapter_info(int unit);
|
||
struct sea_scb *sea_get_scb(int unit, int flags);
|
||
void sea_free_scb(int unit, struct sea_scb *scb, int flags);
|
||
static void sea_main(void);
|
||
static void sea_information_transfer(struct sea_data *sea);
|
||
int sea_poll(int unit, struct scsi_xfer *xs, struct sea_scb *scb);
|
||
int sea_init(int unit);
|
||
int sea_send_scb(struct sea_data *sea, struct sea_scb *scb);
|
||
int sea_reselect(struct sea_data *sea);
|
||
int sea_select(struct sea_data *sea, struct sea_scb *scb);
|
||
int sea_transfer_pio(struct sea_data *sea, u_char *phase, int32 *count,
|
||
u_char **data);
|
||
int sea_abort(int unit, struct sea_scb *scb);
|
||
|
||
static sea_unit = 0;
|
||
static sea_slot = -1; /* last found board seagate_bases address index */
|
||
#define FAIL 1
|
||
#define SUCCESS 0
|
||
|
||
#ifdef KERNEL
|
||
struct scsi_adapter sea_switch =
|
||
{
|
||
sea_scsi_cmd,
|
||
seaminphys,
|
||
0,
|
||
0,
|
||
sea_adapter_info,
|
||
"sea",
|
||
0,0
|
||
};
|
||
|
||
/* the below structure is so we have a default dev struct for our link struct */
|
||
struct scsi_device sea_dev =
|
||
{
|
||
NULL, /* use default error handler */
|
||
NULL, /* have a queue, served by this */
|
||
NULL, /* have no async handler */
|
||
NULL, /* Use default 'done' routine */
|
||
"sea",
|
||
0,
|
||
0,0
|
||
};
|
||
|
||
struct isa_driver seadriver =
|
||
{
|
||
sea_probe,
|
||
sea_attach,
|
||
"sea"
|
||
};
|
||
|
||
#endif /* KERNEL */
|
||
|
||
#ifdef SEADEBUG6
|
||
void sea_queue_length()
|
||
{
|
||
struct sea_scb *tmp;
|
||
int length = 0;
|
||
|
||
if(seadata[0]->connected)
|
||
length = 1;
|
||
for(tmp = seadata[0]->issue_queue; tmp != NULL; tmp = tmp->next, length++);
|
||
for(tmp = seadata[0]->disconnected_queue ; tmp != NULL; tmp->next, length++);
|
||
printf("length:%d ",length);
|
||
}
|
||
#endif
|
||
|
||
/***********************************************************************\
|
||
* Check if the device can be found at the port given and if so, detect *
|
||
* the type of board. Set it up ready for further work. Takes the *
|
||
* isa_dev structure from autoconf as an argument. *
|
||
* Returns 1 if card recognized, 0 if errors *
|
||
\***********************************************************************/
|
||
int
|
||
sea_probe(dev)
|
||
struct isa_device *dev;
|
||
{
|
||
int j;
|
||
int unit = sea_unit;
|
||
struct sea_data *sea;
|
||
dev->id_unit = unit;
|
||
|
||
#ifdef SEADEBUG2
|
||
printf("sea_probe ");
|
||
#endif
|
||
|
||
/* find unit and check we have that many defined */
|
||
if(unit >= NSEA) {
|
||
printf("sea%d: unit number too high\n",unit);
|
||
return(0);
|
||
}
|
||
dev->id_unit = unit;
|
||
#ifdef SEADEBUG2
|
||
printf("unit: %d\n",unit);
|
||
printf("dev_addr: 0x%lx\n",dev->id_maddr);
|
||
#endif
|
||
/* allocate a storage area for us */
|
||
|
||
if (seadata[unit]) {
|
||
printf("sea%d: memory already allocated\n", unit);
|
||
return(0);
|
||
}
|
||
#ifdef SEADEBUG2
|
||
printf("Before malloc\n");
|
||
#endif
|
||
sea = malloc(sizeof(struct sea_data), M_TEMP, M_NOWAIT);
|
||
if (!sea) {
|
||
printf("sea%d: cannot malloc!\n", unit);
|
||
return(0);
|
||
}
|
||
|
||
#ifdef SEADEBUG2
|
||
printf("after malloc\n");
|
||
for(j=0;j<32767;j++);
|
||
#endif
|
||
bzero(sea,sizeof(struct sea_data));
|
||
seadata[unit] = sea;
|
||
|
||
/* check for address if no one specified */
|
||
sea->basemaddr = NULL;
|
||
|
||
/* Could try to find a board by looking through all possible addresses */
|
||
/* This is not done the right way now, because I have not found a way */
|
||
/* to get a boards virtual memory address given its physical. There is */
|
||
/* a function that returns the physical address for a given virtual */
|
||
/* address, but not the other way around */
|
||
|
||
if(dev->id_maddr == 0) {
|
||
/*
|
||
for(sea_slot++;sea_slot<NUM_BASES;sea_slot++)
|
||
for(j = 0; !sea->basemaddr && j < NUM_SIGNATURES; ++j)
|
||
if(!memcmp((void *)(seagate_bases[sea_slot]+signatures[j].offset),
|
||
(void *) signatures[j].signature, signatures[j].length)) {
|
||
sea->basemaddr = (void *)seagate_bases[sea_slot];
|
||
break;
|
||
}
|
||
*/
|
||
} else {
|
||
|
||
#ifdef SEADEBUG2
|
||
printf("id_maddr != 0\n");
|
||
for(j = 0; j < 32767 ; j++);
|
||
for(j = 0; j < 32767 ; j++);
|
||
#endif
|
||
/* find sea_slot position for overridden memory address */
|
||
for(j = 0; ((char *)vtophys(dev->id_maddr) != seagate_bases[j]) &&
|
||
j<NUM_BASES; ++j);
|
||
if(j == NUM_BASES) {
|
||
printf("sea%d: board not expected at address 0x%lx\n",
|
||
unit, dev->id_maddr);
|
||
seadata[unit]=NULL;
|
||
free(sea, M_TEMP);
|
||
return(0);
|
||
} else if(sea_slot > j) {
|
||
printf("sea%d: board address 0x%lx already probed!\n",
|
||
unit, dev->id_maddr);
|
||
seadata[unit]=NULL;
|
||
free(sea, M_TEMP);
|
||
return(0);
|
||
} else {
|
||
sea->basemaddr = dev->id_maddr;
|
||
}
|
||
|
||
}
|
||
#ifdef SEADEBUG2
|
||
printf("sea->basemaddr = %lx\n", sea->basemaddr);
|
||
#endif
|
||
|
||
/* check board type */ /* No way to define this through config */
|
||
for(j = 0; j < NUM_SIGNATURES; j++)
|
||
if(!memcmp((void *) (sea->basemaddr + signatures[j].offset),
|
||
(void *) signatures[j].signature, signatures[j].length)) {
|
||
sea->ctrl_type = signatures[j].type;
|
||
break;
|
||
}
|
||
if(j == NUM_SIGNATURES) {
|
||
#ifdef SEADEBUG
|
||
printf("sea: Board type unknown at address 0x%lx\n",
|
||
sea->basemaddr);
|
||
#endif
|
||
seadata[unit]=NULL;
|
||
free(sea, M_TEMP);
|
||
return(0);
|
||
}
|
||
|
||
/* Find controller and data memory addresses */
|
||
sea->st0x_cr_sr = (void *) (((unsigned char *) sea->basemaddr) +
|
||
((sea->ctrl_type == SEAGATE) ? 0x1a00 : 0x1c00));
|
||
sea->st0x_dr = (void *) (((unsigned char *) sea->basemaddr) +
|
||
((sea->ctrl_type == SEAGATE) ? 0x1c00 : 0x1e00));
|
||
|
||
/* Test controller RAM (works the same way on future domain cards?) */
|
||
*(sea->basemaddr + SEAGATERAMOFFSET) = 0xa5;
|
||
*(sea->basemaddr + SEAGATERAMOFFSET + 1) = 0x5a;
|
||
|
||
if((*(sea->basemaddr + SEAGATERAMOFFSET) != (char) 0xa5) ||
|
||
(*(sea->basemaddr + SEAGATERAMOFFSET + 1) != (char) 0x5a)) {
|
||
printf("sea%d: Board RAM failure\n",unit);
|
||
}
|
||
|
||
if(sea_init(unit) != 0) {
|
||
seadata[unit] = NULL;
|
||
free(sea,M_TEMP);
|
||
return(0);
|
||
}
|
||
|
||
/* if its there put in it's interrupt vector */
|
||
/* (Doesn't use dma, so no drq is set) */
|
||
sea->vect = dev->id_irq;
|
||
|
||
sea_unit++;
|
||
return(1);
|
||
}
|
||
|
||
static struct kern_devconf kdc_sea[NSEA] = { {
|
||
0, 0, 0, /* filled in by dev_attach */
|
||
"sea", 0, { MDDT_ISA, 0, "bio" },
|
||
isa_generic_externalize, 0, 0, ISA_EXTERNALLEN,
|
||
&kdc_isa0, /* parent */
|
||
0, /* parentdata */
|
||
DC_BUSY, /* host adaptors are always busy */
|
||
"Seagate ST01/02 SCSI controller"
|
||
} };
|
||
|
||
static inline void
|
||
sea_registerdev(struct isa_device *id)
|
||
{
|
||
if(id->id_unit)
|
||
kdc_sea[id->id_unit] = kdc_sea[0];
|
||
kdc_sea[id->id_unit].kdc_unit = id->id_unit;
|
||
kdc_sea[id->id_unit].kdc_isa = id;
|
||
dev_attach(&kdc_sea[id->id_unit]);
|
||
}
|
||
|
||
/***********************************************\
|
||
* Attach all sub-devices we can find *
|
||
\***********************************************/
|
||
int
|
||
sea_attach(dev)
|
||
struct isa_device *dev;
|
||
{
|
||
int unit = dev->id_unit;
|
||
struct sea_data *sea = seadata[unit];
|
||
|
||
#ifdef SEADEBUG2
|
||
printf("sea_attach called\n");
|
||
#endif
|
||
|
||
/* fill in the prototype scsi_link */
|
||
sea->sc_link.adapter_unit = unit;
|
||
sea->sc_link.adapter_targ = sea->our_id;
|
||
sea->sc_link.adapter = &sea_switch;
|
||
sea->sc_link.device = &sea_dev;
|
||
printf("\n");
|
||
/*****************************************************\
|
||
* ask the adapter what subunits are present *
|
||
\*****************************************************/
|
||
scsi_attachdevs(&(sea->sc_link));
|
||
sea_registerdev(dev);
|
||
return 1;
|
||
}
|
||
|
||
/***********************************************\
|
||
* Return some information to the caller about *
|
||
* the adapter and its capabilities *
|
||
\***********************************************/
|
||
u_int32
|
||
sea_adapter_info(unit)
|
||
int unit;
|
||
{
|
||
#ifdef SEADEBUG2
|
||
printf("sea_adapter_info called\n");
|
||
#endif
|
||
return 1;
|
||
}
|
||
|
||
/***********************************************\
|
||
* Catch an interrupt from the adaptor *
|
||
\***********************************************/
|
||
int
|
||
seaintr(unit)
|
||
int unit;
|
||
{
|
||
int done;
|
||
struct sea_data *sea = seadata[unit];
|
||
int oldpri;
|
||
|
||
#if SEADEBUG2
|
||
printf(";");
|
||
#endif
|
||
|
||
do {
|
||
done = 1;
|
||
/* dispatch to appropriate routine if found and done=0 */
|
||
/* should check to see that this card really caused the interrupt */
|
||
if ((STATUS & (STAT_SEL | STAT_IO)) == (STAT_SEL | STAT_IO)) {
|
||
/* Reselect interrupt */
|
||
#ifdef SEADEBUG2
|
||
printf(";2");
|
||
#endif
|
||
done = 0;
|
||
/* enable_intr(); */ /* ?? How should this be done ?? */
|
||
sea_reselect(sea);
|
||
} else if (STATUS & STAT_PARITY) {
|
||
/* Parity error interrupt */
|
||
#ifdef SEADEBUG2
|
||
printf(";3");
|
||
#endif
|
||
printf("sea%d: PARITY interrupt\n", unit);
|
||
} else {
|
||
#ifdef SEADEBUG2
|
||
/* printf("sea%d: unknown interrupt\n",unit); */
|
||
printf(";4%x", STATUS);
|
||
#endif
|
||
}
|
||
if (!done) {
|
||
oldpri = splbio(); /* disable_intr(); */
|
||
if (!main_running) {
|
||
#ifdef SEADEBUG2
|
||
printf(";5");
|
||
#endif
|
||
main_running = 1;
|
||
sea_main();
|
||
/* main_running is cleared in sea_main once it can't
|
||
* do more work, and sea_main exits with interrupts
|
||
* disabled
|
||
*/
|
||
splx(oldpri); /* enable_intr(); */
|
||
} else {
|
||
splx(oldpri); /* enable_intr(); */
|
||
}
|
||
}
|
||
} while (!done);
|
||
return 1;
|
||
}
|
||
|
||
/***********************************************\
|
||
* Setup data structures, and reset the board *
|
||
* and the scsi bus *
|
||
\***********************************************/
|
||
int
|
||
sea_init(unit)
|
||
int unit;
|
||
{
|
||
long l;
|
||
int i;
|
||
struct sea_data *sea = seadata[unit];
|
||
|
||
#ifdef SEADEBUG2
|
||
printf("sea_init called\n");
|
||
#endif
|
||
/* Reset the scsi bus (I don't know if this is needed */
|
||
CONTROL = BASE_CMD | CMD_DRVR_ENABLE | CMD_RST;
|
||
DELAY(25); /* hold reset for at least 25 microseconds */
|
||
CONTROL = BASE_CMD;
|
||
DELAY(10); /* wait a Bus Clear Delay (800 ns + bus free delay (800 ns) */
|
||
/* Set our id (don't know anything about this) */
|
||
if(sea->ctrl_type == SEAGATE)
|
||
sea->our_id = 7;
|
||
else
|
||
sea->our_id = 6;
|
||
/* init fields used by our routines */
|
||
sea->connected = NULL;
|
||
sea->issue_queue = NULL;
|
||
sea->disconnected_queue = NULL;
|
||
for (i=0; i<8 ; i++)
|
||
sea->busy[i] = 0;
|
||
|
||
/* link up the free list of scbs */
|
||
sea->numscb = SCB_TABLE_SIZE;
|
||
sea->free_scb = (struct sea_scb *) & (sea->scbs[0]);
|
||
for(i=1;i< SCB_TABLE_SIZE ; i++) {
|
||
sea->scbs[i-1].next = &(sea->scbs[i]);
|
||
}
|
||
sea->scbs[SCB_TABLE_SIZE - 1].next = NULL;
|
||
|
||
return(0);
|
||
}
|
||
|
||
/***********************************************\
|
||
* *
|
||
\***********************************************/
|
||
void seaminphys(bp)
|
||
struct buf *bp;
|
||
{
|
||
#ifdef SEADEBUG2
|
||
/* printf("seaminphys called\n"); */
|
||
printf(",");
|
||
#endif
|
||
}
|
||
|
||
/***********************************************\
|
||
* start a scsi operation given the command and *
|
||
* the data address. Also needs the unit, target *
|
||
* and lu *
|
||
* get a free scb and set it up *
|
||
* call send_scb *
|
||
* either start timer or wait until done *
|
||
\***********************************************/
|
||
int32 sea_scsi_cmd(xs)
|
||
struct scsi_xfer *xs;
|
||
{
|
||
struct scsi_sense_data *s1, *s2;
|
||
struct sea_scb *scb;
|
||
int i = 0;
|
||
int flags;
|
||
int unit = xs->sc_link->adapter_unit;
|
||
struct sea_data *sea = seadata[unit];
|
||
int s;
|
||
unsigned int stat;
|
||
int32 result;
|
||
|
||
#ifdef SEADEBUG2
|
||
/* printf("scsi_cmd\n"); */
|
||
printf("=");
|
||
#endif
|
||
|
||
#ifdef SEADEBUG11
|
||
if(xs->sc_link->target != 1) {
|
||
xs->flags |= ITSDONE;
|
||
xs->error = XS_TIMEOUT;
|
||
return(HAD_ERROR);
|
||
}
|
||
#endif
|
||
|
||
flags = xs->flags;
|
||
if(xs->bp) flags |= (SCSI_NOSLEEP);
|
||
if(flags & ITSDONE) {
|
||
printf("sea%d: Already done?", unit);
|
||
xs->flags &= ~ITSDONE;
|
||
}
|
||
if(!(flags & INUSE)) {
|
||
printf("sea%d: Not in use?", unit);
|
||
xs->flags |= INUSE;
|
||
}
|
||
if (!(scb = sea_get_scb(unit, flags))) {
|
||
#ifdef SEADEBUG2
|
||
printf("=2");
|
||
#endif
|
||
xs->error = XS_DRIVER_STUFFUP;
|
||
return(TRY_AGAIN_LATER);
|
||
}
|
||
|
||
/*
|
||
* Put all the arguments for the xfer in the scb
|
||
*/
|
||
scb->xfer = xs;
|
||
scb->datalen = xs->datalen;
|
||
scb->data = xs->data;
|
||
|
||
if(flags & SCSI_RESET) {
|
||
/* Try to send a reset command to the card. This is done by calling the
|
||
* Reset function. Should then return COMPLETE. Need to take care of the
|
||
* possible current connected command.
|
||
* Not implemented right now.
|
||
*/
|
||
printf("sea%d: Got a SCSI_RESET!\n",unit);
|
||
}
|
||
|
||
/* setup the scb to contain necessary values */
|
||
/* The interresting values can be read from the xs that is saved */
|
||
/* I therefore think that the structure can be kept very small */
|
||
/* the driver doesn't use DMA so the scatter/gather is not needed ? */
|
||
#ifdef SEADEBUG6
|
||
sea_queue_length();
|
||
#endif
|
||
if (sea_send_scb(sea, scb) == 0) {
|
||
#ifdef SEADEBUG2
|
||
printf("=3");
|
||
#endif
|
||
xs->error = XS_DRIVER_STUFFUP;
|
||
sea_free_scb(unit, scb, flags);
|
||
return (TRY_AGAIN_LATER);
|
||
}
|
||
|
||
/*
|
||
* Usually return SUCCESSFULLY QUEUED
|
||
*/
|
||
if (!(flags & SCSI_NOMASK)) {
|
||
if(xs->flags & ITSDONE) { /* timout timer not started, already finished */
|
||
/* Tried to return COMPLETE but the machine hanged with this */
|
||
#ifdef SEADEBUG2
|
||
printf("=6");
|
||
#endif
|
||
return(SUCCESSFULLY_QUEUED);
|
||
}
|
||
timeout((timeout_func_t)sea_timeout, (caddr_t)scb, (xs->timeout * hz)/1000);
|
||
scb->flags |= SCB_TIMECHK;
|
||
#ifdef SEADEBUG2
|
||
printf("=4");
|
||
#endif
|
||
return(SUCCESSFULLY_QUEUED);
|
||
}
|
||
|
||
/*
|
||
* If we can't use interrupts, poll on completion
|
||
*/
|
||
|
||
result = sea_poll(unit, xs, scb);
|
||
#ifdef SEADEBUG2
|
||
printf("=5 %lx", result);
|
||
#endif
|
||
return result;
|
||
}
|
||
|
||
/*
|
||
* Get a free scb. If there are none, see if we can allocate a new one. If so,
|
||
* put it in the hash table too, otherwise return an error or sleep.
|
||
*/
|
||
|
||
struct sea_scb *
|
||
sea_get_scb(unit, flags)
|
||
int unit;
|
||
int flags;
|
||
{
|
||
struct sea_data *sea = seadata[unit];
|
||
unsigned opri = 0;
|
||
struct sea_scb * scbp;
|
||
int hashnum;
|
||
|
||
#ifdef SEADEBUG2
|
||
/* printf("get_scb\n"); */
|
||
printf("(");
|
||
#endif
|
||
|
||
if (!(flags & SCSI_NOMASK))
|
||
opri = splbio();
|
||
|
||
#ifdef SEADEBUG3
|
||
printf("(2 %lx ", sea->free_scb);
|
||
#endif
|
||
|
||
/*
|
||
* If we can and have to, sleep waiting for one to come free
|
||
* but only if we can<61>t allocate a new one.
|
||
*/
|
||
while (!(scbp = sea->free_scb)) {
|
||
#ifdef SEADEBUG12
|
||
printf("(3");
|
||
#endif
|
||
if (sea->numscb < SEA_SCB_MAX) {
|
||
printf("malloced new scbs\n");
|
||
if (scbp = (struct sea_scb *) malloc(sizeof(struct sea_scb),
|
||
M_TEMP, M_NOWAIT)) {
|
||
bzero(scbp, sizeof(struct sea_scb));
|
||
sea->numscb++;
|
||
scbp->flags = SCB_ACTIVE;
|
||
scbp->next = NULL;
|
||
} else {
|
||
printf("sea%d: Can't malloc SCB\n",unit);
|
||
}
|
||
goto gottit;
|
||
} else {
|
||
#ifdef SEADEBUG12
|
||
printf("(4");
|
||
#endif
|
||
if(!(flags & SCSI_NOSLEEP)) {
|
||
#ifdef SEADEBUG2
|
||
printf("(5");
|
||
#endif
|
||
tsleep(&sea->free_scb, PRIBIO, "seascb", 0);
|
||
}
|
||
}
|
||
}
|
||
if (scbp) {
|
||
#ifdef SEADEBUG2
|
||
printf("(6");
|
||
#endif
|
||
/* Get SCB from free list */
|
||
sea->free_scb = scbp->next;
|
||
scbp->next = NULL;
|
||
scbp->flags = SCB_ACTIVE;
|
||
}
|
||
gottit:
|
||
if (!(flags & SCSI_NOMASK))
|
||
splx(opri);
|
||
|
||
return(scbp);
|
||
}
|
||
|
||
/*
|
||
* sea_send_scb
|
||
*
|
||
* Try to send this command to the board. Because this board does not use any
|
||
* mailboxes, this routine simply adds the command to the queue held by the
|
||
* sea_data structure.
|
||
* A check is done to see if the command contains a REQUEST_SENSE command, and
|
||
* if so the command is put first in the queue, otherwise the command is added
|
||
* to the end of the queue. ?? Not correct ??
|
||
*/
|
||
int
|
||
sea_send_scb(struct sea_data *sea, struct sea_scb *scb)
|
||
{
|
||
struct sea_scb *tmp;
|
||
int oldpri = 0;
|
||
|
||
#ifdef SEADEBUG2
|
||
printf("+");
|
||
#endif
|
||
|
||
if(!(scb->xfer->flags & SCSI_NOSLEEP)) {
|
||
oldpri = splbio();
|
||
}
|
||
|
||
/* add to head of queue if queue empty or command is REQUEST_SENSE */
|
||
|
||
if (!(sea->issue_queue)
|
||
#ifdef SEA_SENSEFIRST
|
||
|| (scb->xfer->cmd->opcode == (u_char) REQUEST_SENSE)
|
||
#endif
|
||
) {
|
||
#ifdef SEADEBUG2
|
||
printf("+2");
|
||
#endif
|
||
scb->next = sea->issue_queue;
|
||
sea->issue_queue = scb;
|
||
} else {
|
||
#ifdef SEADEBUG2
|
||
printf("+3");
|
||
#endif
|
||
for (tmp = sea->issue_queue; tmp->next; tmp = tmp->next);
|
||
tmp->next = scb;
|
||
scb->next = NULL; /* placed at the end of the queue */
|
||
}
|
||
/* Try to do some work on the card */
|
||
if (!main_running) {
|
||
main_running = 1;
|
||
sea_main();
|
||
/* main running is cleared in sea_main once it can't
|
||
* do more work, and sea_main exits with interrupts
|
||
* disabled
|
||
*/
|
||
}
|
||
if(!(scb->xfer->flags & SCSI_NOSLEEP)) {
|
||
splx(oldpri);
|
||
}
|
||
return (1); /* No possible errors right now */
|
||
}
|
||
|
||
/*
|
||
* sea_main(void)
|
||
*
|
||
* corroutine that runs as long as more work can be done on the seagate host
|
||
* adapter in a system. Both sea_scsi_cmd and sea_intr will try to start it in
|
||
* case it is not running.
|
||
*/
|
||
|
||
static void sea_main(void)
|
||
{
|
||
struct sea_data *sea; /* This time we look at all cards */
|
||
struct sea_scb *tmp, *prev;
|
||
int done;
|
||
int unit;
|
||
int oldpri;
|
||
|
||
#ifdef SEADEBUG2
|
||
printf(".");
|
||
#endif
|
||
|
||
/*
|
||
* This should not be run with interrupts disabled, but use the splx code
|
||
* instead
|
||
*/
|
||
do {
|
||
done = 1;
|
||
for (sea=seadata[unit=0]; (unit < NSEA) && seadata[unit] ;
|
||
sea=seadata[++unit]) {
|
||
oldpri = splbio();
|
||
if (!sea->connected) {
|
||
#ifdef SEADEBUG2
|
||
printf(".2");
|
||
#endif
|
||
/*
|
||
* Search through the issue_queue for a command destined for a
|
||
* target that's not busy.
|
||
*/
|
||
for (tmp = sea->issue_queue, prev = NULL; tmp ;
|
||
prev = tmp, tmp = tmp->next)
|
||
/* When we find one, remove it from the issue queue. */
|
||
if (!(sea->busy[tmp->xfer->sc_link->target] &
|
||
(1 << tmp->xfer->sc_link->lun))) {
|
||
if (prev)
|
||
prev->next = tmp->next;
|
||
else
|
||
sea->issue_queue = tmp->next;
|
||
tmp->next = NULL;
|
||
|
||
/* re-enable interrupts after finding one */
|
||
splx(oldpri);
|
||
|
||
/*
|
||
* Attempt to establish an I_T_L nexus here.
|
||
* On success, sea->connected is set.
|
||
* On failure, we must add the command back to
|
||
* the issue queue so we can keep trying.
|
||
*/
|
||
#ifdef SEADEBUG2
|
||
printf(".3");
|
||
#endif
|
||
|
||
/* REQUEST_SENSE commands are issued without tagged
|
||
* queueing, even on SCSI-II devices because the
|
||
* contingent alligence condition exists for the
|
||
* entire unit.
|
||
*/
|
||
|
||
/* First check that if any device has tried a reconnect while
|
||
* we have done other things with interrupts disabled
|
||
*/
|
||
|
||
if ((STATUS & (STAT_SEL | STAT_IO)) == (STAT_SEL | STAT_IO)) {
|
||
#ifdef SEADEBUG2
|
||
printf(".7");
|
||
#endif
|
||
sea_reselect(sea);
|
||
break;
|
||
}
|
||
if (!sea_select(sea, tmp)) {
|
||
#ifdef SEADEBUG2
|
||
/* printf("Select returned ok\n"); */
|
||
printf(".4");
|
||
#endif
|
||
break;
|
||
} else {
|
||
oldpri = splbio();
|
||
tmp->next = sea->issue_queue;
|
||
sea->issue_queue = tmp;
|
||
splx(oldpri);
|
||
printf("sea_main: select failed\n");
|
||
}
|
||
} /* if target/lun is not busy */
|
||
} /* if (!sea->connected) */
|
||
|
||
if (sea->connected) { /* we are connected. Do the task */
|
||
splx(oldpri);
|
||
#ifdef SEADEBUG2
|
||
/* printf("sea_main: starting information transfer!\n"); */
|
||
printf(".5");
|
||
#endif
|
||
sea_information_transfer(sea);
|
||
#ifdef SEADEBUG2
|
||
/* printf("sea_main: sea->connected:%lx\n", sea->connected); */
|
||
printf(".6%lx ", sea->connected);
|
||
#endif
|
||
done = 0;
|
||
} else
|
||
break;
|
||
} /* for instance */
|
||
} while (!done);
|
||
main_running = 0;
|
||
}
|
||
|
||
void
|
||
sea_free_scb(unit, scb, flags)
|
||
int unit;
|
||
struct sea_scb *scb;
|
||
int flags;
|
||
{
|
||
struct sea_data *sea = seadata[unit];
|
||
unsigned int opri = 0;
|
||
|
||
#ifdef SEADEBUG2
|
||
/* printf("free_scb\n"); */
|
||
printf(")");
|
||
#endif
|
||
|
||
if(!(flags & SCSI_NOMASK))
|
||
opri = splbio();
|
||
|
||
scb->next = sea->free_scb;
|
||
sea->free_scb = scb;
|
||
scb->flags = SCB_FREE;
|
||
/*
|
||
* If there were none, wake anybody waiting for one to come free,
|
||
* starting with queued entries.
|
||
*/
|
||
if(!scb->next) {
|
||
#ifdef SEADEBUG2
|
||
/* printf("free_scb waking up sleep\n"); */
|
||
printf(")2");
|
||
#endif
|
||
wakeup((caddr_t)&sea->free_scb);
|
||
}
|
||
|
||
if (!(flags & SCSI_NOMASK))
|
||
splx(opri);
|
||
}
|
||
|
||
void
|
||
sea_timeout(caddr_t arg1, int arg2)
|
||
{
|
||
struct sea_scb *scb = (struct sea_scb *)arg1;
|
||
int unit;
|
||
struct sea_data *sea;
|
||
int s=splbio();
|
||
|
||
#ifdef SEADEBUG2
|
||
/* printf("sea_timeout called\n"); */
|
||
printf(":");
|
||
#endif
|
||
|
||
unit = scb->xfer->sc_link->adapter_unit;
|
||
sea = seadata[unit];
|
||
#ifndef SEADEBUG /* print message only if not waiting unless debug */
|
||
if(!(scb->xfer->flags & SCSI_NOMASK))
|
||
#endif
|
||
printf("sea%d:%d:%d (%s%d) timed out ", unit,
|
||
scb->xfer->sc_link->target,
|
||
scb->xfer->sc_link->lun,
|
||
scb->xfer->sc_link->device->name,
|
||
scb->xfer->sc_link->dev_unit);
|
||
|
||
/*
|
||
* If it has been through before, then
|
||
* a previous abort has failed, don't
|
||
* try abort again
|
||
*/
|
||
if (/* (sea_abort(unit, scb) != 1) ||*/ (scb->flags & SCB_ABORTED)) {
|
||
/*
|
||
* abort timed out
|
||
*/
|
||
#ifdef SEADEBUG2
|
||
/* printf("sea%d: Abort Operation has timed out\n", unit); */
|
||
printf(":2");
|
||
#endif
|
||
scb->xfer->retries = 0;
|
||
scb->flags |= SCB_ABORTED;
|
||
sea_done(unit, scb);
|
||
} else {
|
||
#ifdef SEADEBUG2
|
||
/* printf("sea%d: Try to abort\n", unit); */
|
||
printf(":3");
|
||
#endif
|
||
sea_abort(unit, scb);
|
||
/* sea_send_scb(sea, ~SCSI_NOMASK, SEA_SCB_ABORT, scb); */
|
||
/* 2 seconds for the abort */
|
||
timeout((timeout_func_t)sea_timeout, (caddr_t)scb, 2*hz);
|
||
scb->flags |= (SCB_ABORTED | SCB_TIMECHK);
|
||
}
|
||
splx(s);
|
||
}
|
||
|
||
int
|
||
sea_reselect(sea)
|
||
struct sea_data *sea;
|
||
{
|
||
unsigned char target_mask;
|
||
long l;
|
||
unsigned char lun, phase;
|
||
unsigned char msg[3];
|
||
int32 len;
|
||
u_char *data;
|
||
struct sea_scb *tmp = 0, *prev = 0;
|
||
int abort = 0;
|
||
|
||
#if SEADEBUG2
|
||
/* printf("sea_reselect called\n"); */
|
||
printf("}");
|
||
#endif
|
||
|
||
if (!((target_mask = STATUS) & STAT_SEL)) {
|
||
printf("sea: wrong state 0x%x\n", target_mask);
|
||
return(0);
|
||
}
|
||
/* wait for a device to win the reselection phase */
|
||
/* signals this by asserting the I/O signal */
|
||
for(l=10; l && (STATUS & (STAT_SEL | STAT_IO | STAT_BSY))
|
||
!= (STAT_SEL | STAT_IO | 0);l--);
|
||
/* !! Check for timeout here */
|
||
/* the data bus contains original initiator id ORed with target id */
|
||
target_mask = DATA;
|
||
/* see that we really are the initiator */
|
||
if (!(target_mask & ((sea->ctrl_type == SEAGATE) ? 0x80 : 0x40))) {
|
||
printf("sea: polled reselection was not for me: %x\n",target_mask);
|
||
return(0);
|
||
}
|
||
/* find target who won */
|
||
target_mask &= ((sea->ctrl_type == SEAGATE) ? ~0x80 : ~0x40);
|
||
/* host responds by asserting the BSY signal */
|
||
CONTROL = (BASE_CMD | CMD_DRVR_ENABLE | CMD_BSY);
|
||
/* target should respond by deasserting the SEL signal */
|
||
for(l=50000;l && (STATUS & STAT_SEL);l++);
|
||
/* remove the busy status */
|
||
CONTROL = (BASE_CMD | CMD_DRVR_ENABLE);
|
||
/* we are connected. Now we wait for the MSGIN condition */
|
||
for(l=50000; l && !(STATUS & STAT_REQ);l--);
|
||
/* !! Add timeout check here */
|
||
/* hope we get an IDENTIFY message */
|
||
len = 3;
|
||
data = msg;
|
||
phase = REQ_MSGIN;
|
||
sea_transfer_pio(sea, &phase, &len, &data);
|
||
|
||
if (!(msg[0] & 0x80)) {
|
||
printf("sea: Expecting IDENTIFY message, got 0x%x\n", msg[0]);
|
||
abort = 1;
|
||
} else {
|
||
lun = (msg[0] & 0x07);
|
||
|
||
/*
|
||
* Find the command corresponding to the I_T_L or I_T_L_Q nexus we
|
||
* just restablished, and remove it from the disconnected queue.
|
||
*/
|
||
|
||
for(tmp = sea->disconnected_queue, prev = NULL;
|
||
tmp; prev=tmp, tmp = tmp->next)
|
||
if((target_mask == (1 << tmp->xfer->sc_link->target)) &&
|
||
(lun == tmp->xfer->sc_link->lun)) {
|
||
if(prev) {
|
||
#ifdef SEADEBUG2
|
||
printf("}2");
|
||
#endif
|
||
prev->next = tmp->next;
|
||
} else {
|
||
#ifdef SEADEBUG2
|
||
printf("}3");
|
||
#endif
|
||
sea->disconnected_queue = tmp->next;
|
||
}
|
||
tmp->next = NULL;
|
||
break;
|
||
}
|
||
if (!tmp) {
|
||
printf("sea: warning : target %02x lun %d not in disconnect_queue\n",
|
||
target_mask, lun);
|
||
/*
|
||
* Since we have an established nexus that we can't do anything with,
|
||
* we must abort it.
|
||
*/
|
||
abort = 1;
|
||
}
|
||
}
|
||
|
||
if(abort) {
|
||
#ifdef SEADEBUG2
|
||
printf("}4");
|
||
#endif
|
||
msg[0] = MSG_ABORT;
|
||
len = 1;
|
||
data = msg;
|
||
phase = REQ_MSGOUT;
|
||
CONTROL = (BASE_CMD | CMD_ATTN);
|
||
sea_transfer_pio(sea, &phase, &len, &data);
|
||
} else {
|
||
#ifdef SEADEBUG2
|
||
printf("}5");
|
||
#endif
|
||
sea->connected = tmp;
|
||
}
|
||
/* return value not used yet */
|
||
return 0;
|
||
}
|
||
|
||
/* Transfer data in given phase using polled I/O
|
||
*/
|
||
|
||
int sea_transfer_pio(struct sea_data *sea, u_char *phase, int32 *count,
|
||
u_char **data)
|
||
{
|
||
register unsigned char p = *phase, tmp;
|
||
register int c = *count;
|
||
register unsigned char *d = *data;
|
||
unsigned long int timeout;
|
||
|
||
#if SEADEBUG2
|
||
/* printf("sea_transfer_pio called: len:%x\n",c); */
|
||
printf("-1 %x %x", c, p);
|
||
#endif
|
||
|
||
do {
|
||
/* wait for assertion of REQ, after which the phase bits will be valid */
|
||
for(timeout = 0; timeout < 5000000L ; timeout++)
|
||
if ((tmp = STATUS) & STAT_REQ)
|
||
break;
|
||
if (!(tmp & STAT_REQ)) {
|
||
printf("sea_transfer_pio: timeout waiting for STAT_REQ\n");
|
||
break;
|
||
}
|
||
|
||
/* check for phase mismatch */
|
||
/* Reached if the target decides that it has finished the transfer */
|
||
if ((tmp & REQ_MASK) != p) {
|
||
#ifdef SEADEBUG1
|
||
/* printf("-2 %x", tmp); */
|
||
printf("sea:pio phase mismatch:%x, want:%x, len:%x\n",tmp,p,c);
|
||
#endif
|
||
break;
|
||
}
|
||
|
||
/* Do actual transfer from SCSI bus to/from memory */
|
||
if (!(p & STAT_IO))
|
||
DATA = *d;
|
||
else
|
||
*d = DATA;
|
||
#ifdef SEADEBUG15
|
||
printf("-7%x", *d);
|
||
#endif
|
||
++d;
|
||
|
||
/* The SCSI standard suggests that in MSGOUT phase, the initiator
|
||
* should drop ATN on the last byte of the message phase
|
||
* after REQ has been asserted for the handshake but before
|
||
* the initiator raises ACK.
|
||
* Don't know how to accomplish this on the ST01/02
|
||
*/
|
||
/* We don't mind right now. */
|
||
|
||
/* The st01 code doesn't wait for STAT_REQ to be deasserted. Is this ok? */
|
||
/* for(timeout=0;timeout<200000L;timeout++)
|
||
if(!(STATUS & STAT_REQ))
|
||
break;
|
||
if(STATUS & STAT_REQ)
|
||
printf("timeout on wait for !STAT_REQ"); */
|
||
/* printf("*"); */
|
||
} while (--c);
|
||
|
||
*count = c;
|
||
*data = d;
|
||
tmp = STATUS;
|
||
if (tmp & STAT_REQ) {
|
||
#if SEADEBUG2
|
||
printf("-3%x", tmp);
|
||
#endif
|
||
*phase = tmp & REQ_MASK;
|
||
} else {
|
||
#if SEADEBUG2
|
||
printf("-4%x", tmp);
|
||
#endif
|
||
*phase = REQ_UNKNOWN;
|
||
}
|
||
if (!c || (*phase == p)) {
|
||
#if SEADEBUG2
|
||
printf("-5%x %x", c, *phase);
|
||
#endif
|
||
return 0;
|
||
} else {
|
||
#if SEADEBUG2
|
||
printf("-6");
|
||
#endif
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
/* sea_select
|
||
* establish I_T_L or I_T_L_Q nexus for new or existing command
|
||
* including ARBITRATION, SELECTION, and initial message out for IDENTIFY and
|
||
* queue messages.
|
||
* return -1 if selection could not execute for some reason, 0 if selection
|
||
* succeded or failed because the taget did not respond
|
||
*/
|
||
int sea_select(struct sea_data *sea, struct sea_scb *scb)
|
||
{
|
||
unsigned char tmp[3], phase;
|
||
u_char *data;
|
||
int32 len;
|
||
unsigned long timeout;
|
||
|
||
#ifdef SEADEBUG2
|
||
/* printf("sea_select called\n"); */
|
||
printf("{");
|
||
#endif
|
||
|
||
CONTROL = BASE_CMD;
|
||
DATA = ((sea->ctrl_type == SEAGATE) ? 0x80 : 0x40);
|
||
CONTROL = (BASE_CMD & ~CMD_INTR) | CMD_START_ARB;
|
||
/* wait for arbitration to complete */
|
||
for (timeout = 0; timeout < 3000000L ; timeout++) {
|
||
if (STATUS & STAT_ARB_CMPL)
|
||
break;
|
||
}
|
||
if (!(STATUS & STAT_ARB_CMPL)) {
|
||
if (STATUS & STAT_SEL) {
|
||
printf("sea: arbitration lost\n");
|
||
scb->flags |= SCB_ERROR;
|
||
} else {
|
||
printf("sea: arbitration timeout.\n");
|
||
scb->flags |=SCB_TIMEOUT;
|
||
}
|
||
CONTROL = BASE_CMD;
|
||
return(-1);
|
||
}
|
||
DELAY(2);
|
||
|
||
#if SEADEBUG2
|
||
/* printf("after arbitration: STATUS=%x\n", STATUS); */
|
||
printf("{2 %x", STATUS);
|
||
#endif
|
||
|
||
DATA = (unsigned char)((1 << scb->xfer->sc_link->target) |
|
||
((sea->ctrl_type == SEAGATE) ? 0x80 : 0x40));
|
||
#ifdef SEANOMSGS
|
||
CONTROL = (BASE_CMD & (~CMD_INTR))| CMD_DRVR_ENABLE | CMD_SEL;
|
||
#else
|
||
CONTROL = (BASE_CMD & (~CMD_INTR)) | CMD_DRVR_ENABLE | CMD_SEL | CMD_ATTN;
|
||
#endif
|
||
DELAY(1);
|
||
/* wait for a bsy from target */
|
||
for (timeout = 0; timeout < 2000000L; timeout++) {
|
||
if (STATUS & STAT_BSY)
|
||
break;
|
||
}
|
||
|
||
#if SEADEBUG2
|
||
/* printf("after wait for BSY: STATUS=%x,count=%lx\n", STATUS, timeout); */
|
||
printf("{3 %x %x", STATUS, timeout);
|
||
#endif
|
||
|
||
if (!(STATUS & STAT_BSY)) {
|
||
/* should return some error to the higher level driver */
|
||
CONTROL = BASE_CMD;
|
||
#if SEADEBUG2
|
||
/* printf("sea: target did not respond\n"); */
|
||
printf("{4");
|
||
#endif
|
||
scb->flags |= SCB_TIMEOUT;
|
||
return 0;
|
||
}
|
||
|
||
/* Try to make the target to take a message from us */
|
||
#ifdef SEANOMSGS
|
||
CONTROL = (BASE_CMD & (~CMD_INTR)) | CMD_DRVR_ENABLE;
|
||
#else
|
||
CONTROL = (BASE_CMD & (~CMD_INTR)) | CMD_DRVR_ENABLE | CMD_ATTN;
|
||
#endif
|
||
|
||
DELAY(1);
|
||
|
||
/* should start a msg_out phase */
|
||
for (timeout = 0; timeout < 2000000L ; timeout++) {
|
||
if (STATUS & STAT_REQ)
|
||
break;
|
||
}
|
||
|
||
CONTROL = BASE_CMD | CMD_DRVR_ENABLE;
|
||
|
||
#if SEADEBUG2 || SEADEBUG9
|
||
/* printf("after wait for STAT_REQ: STATUS=%x,count=%lx\n", STATUS, timeout);
|
||
printf("2:nd try after wait for STAT_REQ: STATUS=%x\n", STATUS); */
|
||
printf("{5%x", timeout);
|
||
#endif
|
||
|
||
if (!(STATUS & STAT_REQ)) {
|
||
/* This should not be taken as an error, but more like an unsupported
|
||
* feature!
|
||
* Should set a flag indicating that the target don't support messages, and
|
||
* continue without failure. (THIS IS NOT AN ERROR!)
|
||
*/
|
||
#if SEADEBUG
|
||
/* printf("{6"); */
|
||
printf("sea: WARNING: target %x don't support messages?\n",
|
||
scb->xfer->sc_link->target);
|
||
#endif
|
||
} else {
|
||
tmp[0] = IDENTIFY(1, scb->xfer->sc_link->lun); /* allow disconnects */
|
||
len = 1;
|
||
data = tmp;
|
||
phase = REQ_MSGOUT;
|
||
/* Should do test on result of sea_transfer_pio */
|
||
#if SEADEBUG2
|
||
/* printf("Trying a msg out phase\n"); */
|
||
printf("{7");
|
||
#endif
|
||
sea_transfer_pio(sea, &phase, &len, &data);
|
||
}
|
||
if (!(STATUS & STAT_BSY)) {
|
||
printf("sea: after successful arbitrate: No STAT_BSY!\n");
|
||
}
|
||
|
||
#if SEADEBUG2
|
||
printf("{8");
|
||
#endif
|
||
sea->connected = scb;
|
||
sea->busy[scb->xfer->sc_link->target] |= (1 << scb->xfer->sc_link->lun);
|
||
/* this assignment should depend on possibility to send a message to target */
|
||
CONTROL = BASE_CMD | CMD_DRVR_ENABLE;
|
||
/* reset pointer in command ??? */
|
||
return 0;
|
||
}
|
||
|
||
/* sea_abort
|
||
send an abort to the target
|
||
return 1 success, 0 on failure
|
||
*/
|
||
int sea_abort(int unit, struct sea_scb *scb)
|
||
{
|
||
struct sea_data *sea = seadata[unit];
|
||
struct sea_scb *tmp, **prev;
|
||
unsigned char msg, phase, *msgptr;
|
||
int32 len;
|
||
int oldpri;
|
||
|
||
#ifdef SEADEBUG2
|
||
/* printf("sea_abort called\n"); */
|
||
printf("\\");
|
||
#endif
|
||
|
||
oldpri = splbio();
|
||
|
||
/* If the command hasn't been issued yet, we simply remove it from the
|
||
* issue queue
|
||
*/
|
||
for (prev = (struct sea_scb **) &(sea->issue_queue),
|
||
tmp = sea->issue_queue; tmp;
|
||
prev = (struct sea_scb **) &(tmp->next), tmp = tmp->next)
|
||
if (scb == tmp) {
|
||
(*prev) = tmp->next;
|
||
tmp->next = NULL;
|
||
/* set some type of error result for this operation */
|
||
splx(oldpri);
|
||
#ifdef SEADEBUG2
|
||
printf("\\2");
|
||
#endif
|
||
return 1;
|
||
}
|
||
|
||
/* If any commands are connected, we're going to fail the abort
|
||
* and let the high level SCSI driver retry at a later time or issue a
|
||
* reset
|
||
*/
|
||
|
||
if(sea->connected) {
|
||
splx(oldpri);
|
||
#ifdef SEADEBUG2
|
||
/* printf("sea:abort error connected\n"); */
|
||
printf("\\3");
|
||
#endif
|
||
return 0;
|
||
}
|
||
|
||
/* If the command is currently disconnected from the bus, and there are
|
||
* no connected commands, we reconnect the I_T_L or I_T_L_Q nexus
|
||
* associated with it, go into message out, and send an abort message.
|
||
*/
|
||
|
||
for (tmp = sea->disconnected_queue; tmp; tmp = tmp->next)
|
||
if (scb == tmp) {
|
||
splx(oldpri);
|
||
#ifdef SEADEBUG2
|
||
printf("\\4");
|
||
#endif
|
||
if (sea_select(sea,scb)) {
|
||
#ifdef SEADEBUG2
|
||
printf("\\5");
|
||
#endif
|
||
return 0;
|
||
}
|
||
msg = MSG_ABORT;
|
||
msgptr = &msg;
|
||
len = 1;
|
||
phase = REQ_MSGOUT;
|
||
CONTROL = BASE_CMD | CMD_ATTN;
|
||
sea_transfer_pio(sea, &phase, &len, &msgptr);
|
||
|
||
oldpri = splbio();
|
||
for (prev = (struct sea_scb **) &(sea->disconnected_queue),
|
||
tmp = sea->disconnected_queue; tmp ;
|
||
prev = (struct sea_scb **) &(tmp->next), tmp = tmp->next)
|
||
if (scb == tmp) {
|
||
*prev = tmp->next;
|
||
tmp->next = NULL;
|
||
/* set some type of error result for the operation */
|
||
#ifdef SEADEBUG2
|
||
printf("\\6");
|
||
#endif
|
||
splx(oldpri);
|
||
return 1;
|
||
}
|
||
}
|
||
|
||
/* command not found in any queue, race condition in the code ? */
|
||
|
||
splx(oldpri);
|
||
#ifdef SEADEBUG2
|
||
/* printf("sea: WARNING: SCSI command probably completed successfully\n"
|
||
" before abortion\n"); */
|
||
printf("\\7");
|
||
#endif
|
||
return 1;
|
||
|
||
}
|
||
|
||
void sea_done(int unit, struct sea_scb *scb)
|
||
{
|
||
struct sea_data *sea = seadata[unit];
|
||
struct scsi_xfer *xs = scb->xfer;
|
||
|
||
|
||
#ifdef SEADEBUG2
|
||
/* printf("sea_done called\n"); */
|
||
printf("&");
|
||
#endif
|
||
|
||
if (scb->flags & SCB_TIMECHK) {
|
||
#ifdef SEADEBUG2
|
||
printf("&2");
|
||
#endif
|
||
untimeout((timeout_func_t)sea_timeout, (caddr_t)scb);
|
||
}
|
||
|
||
xs->resid = scb->datalen; /* How much of the buffer was not touched */
|
||
|
||
if ((scb->flags == SCB_ACTIVE) || (xs->flags & SCSI_ERR_OK)) {
|
||
#ifdef SEADEBUG2
|
||
/* printf("sea_done:Report no err in xs\n"); */
|
||
printf("&3");
|
||
#endif
|
||
/* xs->resid = 0; */
|
||
/* xs->error = 0; */
|
||
} else {
|
||
|
||
if (!(scb->flags == SCB_ACTIVE)) {
|
||
if ((scb->flags & SCB_TIMEOUT) || (scb->flags & SCB_ABORTED)) {
|
||
#ifdef SEADEBUG2
|
||
printf("&6");
|
||
#endif
|
||
xs->error = XS_TIMEOUT;
|
||
}
|
||
if (scb->flags & SCB_ERROR) {
|
||
#ifdef SEADEBUG2
|
||
printf("&7");
|
||
#endif
|
||
xs->error = XS_DRIVER_STUFFUP;
|
||
}
|
||
} else {
|
||
|
||
/* !!! Add code to check for target status */
|
||
/* say all error now */
|
||
xs->error = XS_DRIVER_STUFFUP;
|
||
#ifdef SEADEBUG2
|
||
printf("&4");
|
||
#endif
|
||
}
|
||
}
|
||
xs->flags |= ITSDONE;
|
||
sea_free_scb(unit, scb, xs->flags);
|
||
scsi_done(xs);
|
||
#ifdef SEADEBUG2
|
||
/* printf("Leaving sea_done\n"); */
|
||
printf("&5");
|
||
#endif
|
||
}
|
||
|
||
/* wait for completion of command in polled mode */
|
||
|
||
int sea_poll(int unit, struct scsi_xfer *xs, struct sea_scb *scb)
|
||
{
|
||
int count = 500; /* xs->timeout; */
|
||
int oldpri;
|
||
|
||
#ifdef SEADEBUG2
|
||
/* printf("sea_poll called\n"); */
|
||
printf("?");
|
||
#endif
|
||
|
||
while (count) {
|
||
/* try to do something */
|
||
oldpri = splbio();
|
||
if (!main_running) {
|
||
main_running = 1;
|
||
sea_main();
|
||
/* main_running is cleared in sea_main once it can't
|
||
* do more work, and sea_main exits with interrupts
|
||
* disabled
|
||
*/
|
||
splx(oldpri);
|
||
} else {
|
||
splx(oldpri);
|
||
}
|
||
if (xs->flags & ITSDONE) {
|
||
break;
|
||
}
|
||
DELAY(10);
|
||
count--;
|
||
}
|
||
#ifdef SEADEBUG2
|
||
printf("?2 %x ", count);
|
||
/* printf("sea_poll: count:%x\n",count); */
|
||
#endif
|
||
if (count == 0) {
|
||
/* we timed out, so call the timeout handler manually,
|
||
* accounting for the fact that the clock is not running yet
|
||
* by taking out the clock queue entry it makes.
|
||
*/
|
||
#ifdef SEADEBUG2
|
||
printf("?3");
|
||
#endif
|
||
sea_timeout((caddr_t)scb, 0);
|
||
|
||
/* because we are polling, take out the timeout entry
|
||
* sea_timeout made
|
||
*/
|
||
#ifdef SEADEBUG2
|
||
printf("?4");
|
||
#endif
|
||
untimeout((timeout_func_t)sea_timeout, (caddr_t) scb);
|
||
count = 50;
|
||
while (count) {
|
||
/* once again, wait for the int bit */
|
||
oldpri = splbio();
|
||
if (!main_running) {
|
||
main_running = 1;
|
||
sea_main();
|
||
/* main_running is cleared by sea_main once it can't
|
||
* do more work, and sea_main exits with interrupts
|
||
* disabled
|
||
*/
|
||
splx(oldpri);
|
||
} else {
|
||
splx(oldpri);
|
||
}
|
||
if (xs->flags & ITSDONE) {
|
||
break;
|
||
}
|
||
DELAY(10);
|
||
count--;
|
||
}
|
||
if (count == 0) {
|
||
/* we timed out again... This is bad. Notice that
|
||
* this time there is no clock queue entry to remove
|
||
*/
|
||
#ifdef SEADEBUG2
|
||
printf("?5");
|
||
#endif
|
||
sea_timeout((caddr_t)scb, 0);
|
||
}
|
||
}
|
||
#ifdef SEADEBUG2
|
||
/* printf("sea_poll: xs->error:%x\n",xs->error); */
|
||
printf("?6%x",xs->error);
|
||
#endif
|
||
if (xs->error) {
|
||
#ifdef SEADEBUG2
|
||
/* printf("done return error\n"); */
|
||
printf("?7");
|
||
#endif
|
||
return (HAD_ERROR);
|
||
}
|
||
#ifdef SEADEBUG2
|
||
/* printf("done return complete\n"); */
|
||
printf("?8");
|
||
#endif
|
||
return (COMPLETE);
|
||
}
|
||
|
||
/*
|
||
* sea_information_transfer
|
||
* Do the transfer. We know we are connected. Update the flags,
|
||
* call sea_done when task accomplished. Dialog controlled by the
|
||
* target
|
||
*/
|
||
static void sea_information_transfer (struct sea_data *sea)
|
||
{
|
||
long int timeout;
|
||
int unit = sea->sc_link.adapter_unit;
|
||
unsigned char msgout = MSG_NOP;
|
||
int32 len;
|
||
int oldpri;
|
||
u_char *data;
|
||
unsigned char phase, tmp, old_phase=REQ_UNKNOWN;
|
||
struct sea_scb *scb = sea->connected;
|
||
int loop;
|
||
|
||
#if SEADEBUG2
|
||
/* printf("sea_information_transfer called\n"); */
|
||
printf("!");
|
||
#endif
|
||
|
||
for(timeout = 0; timeout < 10000000L ; timeout++) {
|
||
tmp = STATUS;
|
||
if (!(tmp & STAT_BSY)) {
|
||
/* for(loop=0;loop < 20 ; loop++) {
|
||
if((tmp=STATUS) & STAT_BSY)
|
||
break;
|
||
} */
|
||
#ifndef SEADEBUG8
|
||
if(!(tmp & STAT_BSY)) {
|
||
printf("sea: !STAT_BSY unit in data transfer!\n");
|
||
oldpri = splbio();
|
||
sea->connected = NULL;
|
||
scb->flags = SCB_ERROR;
|
||
splx(oldpri);
|
||
sea_done(unit, scb);
|
||
return;
|
||
}
|
||
#endif
|
||
}
|
||
|
||
/* we only have a valid SCSI phase when REQ is asserted */
|
||
if (tmp & STAT_REQ) {
|
||
phase = (tmp & REQ_MASK);
|
||
if (phase != old_phase) {
|
||
old_phase = phase;
|
||
}
|
||
|
||
#ifdef SEADEBUG7
|
||
printf("!2%x", phase);
|
||
for(loop=0;loop < 20; loop++) {
|
||
phase = STATUS;
|
||
printf("!6%x",phase);
|
||
phase = phase & REQ_MASK;
|
||
}
|
||
#endif
|
||
|
||
switch (phase) {
|
||
case REQ_DATAOUT:
|
||
#ifdef SEA_NODATAOUT
|
||
printf("sea: SEA_NODATAOUT set, attempted DATAOUT aborted\n");
|
||
msgout = MSG_ABORT;
|
||
CONTROL = BASE_CMD | CMD_ATTN;
|
||
break;
|
||
#endif
|
||
case REQ_DATAIN:
|
||
/* data = scb->xfer->data;
|
||
len = scb->xfer->datalen;
|
||
*/ if(!(scb->data)) {
|
||
printf("no data address!\n");
|
||
}
|
||
#ifdef SEA_BLINDTRANSFER
|
||
if (scb->datalen && !(scb->datalen % BLOCK_SIZE)) {
|
||
while (scb->datalen) {
|
||
for(timeout = 0; timeout < 5000000L ; timeout++)
|
||
if((tmp = STATUS) & STAT_REQ)
|
||
break;
|
||
if(!(tmp & STAT_REQ)) {
|
||
printf("sea_transfer_pio: timeout waiting for STAT_REQ\n");
|
||
/* getchar(); */
|
||
}
|
||
if((tmp & REQ_MASK) != phase) {
|
||
#ifdef SEADEBUG1
|
||
printf("sea:infotransfer phase mismatch:%x, want:%x, len:%x\n",
|
||
tmp,phase,scb->datalen);
|
||
/* getchar(); */
|
||
#endif
|
||
break;
|
||
}
|
||
if(!(phase & STAT_IO)) {
|
||
#ifdef SEA_ASSEMBLER
|
||
asm("
|
||
shr $2, %%ecx;
|
||
cld;
|
||
rep;
|
||
movsl; " : :
|
||
"D" (sea->st0x_dr), "S" (scb->data), "c" (BLOCK_SIZE) :
|
||
"cx", "si", "di" );
|
||
scb->data += BLOCK_SIZE;
|
||
#else
|
||
for(count=0; count < BLOCK_SIZE; count++) {
|
||
DATA = *(scb->data);
|
||
scb->data++;
|
||
}
|
||
#endif
|
||
} else {
|
||
#ifdef SEA_ASSEMBLER
|
||
asm("
|
||
shr $2, %%ecx;
|
||
cld;
|
||
rep;
|
||
movsl; " : :
|
||
"S" (sea->st0x_dr), "D" (scb->data), "c" (BLOCK_SIZE) :
|
||
"cx", "si", "di" );
|
||
scb->data += BLOCK_SIZE;
|
||
#else
|
||
for(count=0; count < BLOCK_SIZE; count++) {
|
||
*scb->data = DATA;
|
||
scb->data++;
|
||
}
|
||
#endif
|
||
}
|
||
scb->datalen -= BLOCK_SIZE;
|
||
}
|
||
}
|
||
|
||
/* save current position into the command structure */
|
||
/* scb->xfer->data = data;
|
||
scb->xfer->datalen = len; */
|
||
#endif
|
||
|
||
sea_transfer_pio(sea, &phase, &(scb->datalen), &(scb->data));
|
||
/* scb->xfer->data = data;
|
||
scb->xfer->datalen = len;
|
||
*/ break;
|
||
|
||
case REQ_MSGIN:
|
||
/* don't handle multi-byte messages here, because they
|
||
* should not be present here
|
||
*/
|
||
len = 1;
|
||
data = &tmp;
|
||
sea_transfer_pio(sea, &phase, &len, &data);
|
||
/* scb->MessageIn = tmp; */
|
||
|
||
switch (tmp) {
|
||
|
||
case MSG_ABORT:
|
||
scb->flags = SCB_ABORTED;
|
||
printf("sea:Command aborted by target\n");
|
||
CONTROL = BASE_CMD;
|
||
sea_done(unit, scb);
|
||
return;
|
||
|
||
case MSG_COMMAND_COMPLETE:
|
||
oldpri = splbio();
|
||
sea->connected = NULL;
|
||
splx(oldpri);
|
||
#ifdef SEADEBUG2
|
||
printf("!3");
|
||
#endif
|
||
sea->busy[scb->xfer->sc_link->target] &=
|
||
~(1 << scb->xfer->sc_link->lun);
|
||
|
||
CONTROL = BASE_CMD;
|
||
sea_done(unit, scb);
|
||
return;
|
||
case MSG_MESSAGE_REJECT:
|
||
/* printf("sea: message_reject recieved\n"); */
|
||
printf("!4");
|
||
break;
|
||
case MSG_DISCONNECT:
|
||
oldpri = splbio();
|
||
scb->next = sea->disconnected_queue;
|
||
sea->disconnected_queue = scb;
|
||
sea->connected = NULL;
|
||
CONTROL = BASE_CMD;
|
||
splx(oldpri);
|
||
#ifdef SEADEBUG2
|
||
/* printf("msg_disconnect\n"); */
|
||
printf("!5");
|
||
#endif
|
||
return;
|
||
/* save/restore of pointers are ignored */
|
||
case MSG_SAVE_POINTERS:
|
||
case MSG_RESTORE_POINTERS:
|
||
#if SEADEBUG2
|
||
printf("sea: rec save/restore ptrs\n");
|
||
#endif
|
||
break;
|
||
default:
|
||
/* this should be handled in the pio data transfer phase, as the
|
||
* ATN should be raised before ACK goes false when rejecting a message
|
||
*/
|
||
#ifdef SEADEBUG
|
||
printf("sea: Unknown message in:%x\n", tmp);
|
||
#endif
|
||
break;
|
||
} /* switch (tmp) */
|
||
break;
|
||
case REQ_MSGOUT:
|
||
len = 1;
|
||
data = &msgout;
|
||
/* sea->last_message = msgout; */
|
||
sea_transfer_pio(sea, &phase, &len, &data);
|
||
if (msgout == MSG_ABORT) {
|
||
printf("sea: sent message abort to target\n");
|
||
oldpri = splbio();
|
||
sea->busy[scb->xfer->sc_link->target] &=
|
||
~(1 << scb->xfer->sc_link->lun);
|
||
sea->connected = NULL;
|
||
scb->flags = SCB_ABORTED;
|
||
splx(oldpri);
|
||
/* enable interrupt from scsi */
|
||
sea_done(unit, scb);
|
||
return;
|
||
}
|
||
msgout = MSG_NOP;
|
||
break;
|
||
case REQ_CMDOUT:
|
||
len = scb->xfer->cmdlen;
|
||
data = (char *) scb->xfer->cmd;
|
||
sea_transfer_pio(sea, &phase, &len, &data);
|
||
break;
|
||
case REQ_STATIN:
|
||
len = 1;
|
||
data = &tmp;
|
||
sea_transfer_pio(sea, &phase, &len, &data);
|
||
scb->xfer->status = tmp;
|
||
break;
|
||
default:
|
||
printf("sea: unknown phase\n");
|
||
} /* switch (phase) */
|
||
} /* if (tmp & STAT_REQ) */
|
||
} /* for (...) */
|
||
/* if we get here we have got a timeout! */
|
||
printf("sea: Timeout in data transfer\n");
|
||
scb->flags = SCB_TIMEOUT;
|
||
/* should I clear scsi-bus state? */
|
||
sea_done(unit, scb);
|
||
}
|
||
|
||
|