* (Free/Net/386)BSD ST01/02, Future Domain TMC-885, TMC-950 SCSI driver for
* 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)
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 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
* 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
* 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
#ifdef KERNEL /* don't laugh.. look for main() */
#include <i386/isa/isa_device.h>
#include <scsi/scsi_all.h>
#include <scsi/scsiconf.h>
#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
* 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
/* #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 */
#define SEA_FREEBSD11 1 /* intermediate def. for FreeBSD 1.1 BETA */
/* timeout function has changed */
/* 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 **************************/
#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 */
#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 */
#define REQ_MASK (STAT_CD | STAT_IO | STAT_MSG)
#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 SEAGATERAMOFFSET 0x00001800
#define BASE_CMD (CMD_EN_PARITY | CMD_INTR)
#define BASE_CMD (CMD_INTR)
/******************************************************************************
* This should be placed in a more generic file (presume in /sys/scsi)
#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 */
int flags
; /* status of the instruction */
#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
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 */
/* flag showing if main routine is running. */
static volatile int main_running
= 0;
#define STATUS (*(volatile unsigned char *) sea->st0x_cr_sr)
#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 */
* Establish I_T_L nexus instead of I_T_L_Q
* even on SCSI-II devices.
* 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
);
int32
sea_scsi_cmd(struct scsi_xfer
*xs
);
void sea_timeout(caddr_t
, int);
void sea_timeout(struct sea_scb
*scb
);
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_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
,
int sea_abort(int unit
, struct sea_scb
*scb
);
static sea_slot
= -1; /* last found board seagate_bases address index */
struct scsi_adapter sea_switch
=
/* 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 */
struct isa_driver seadriver
=
if(seadata
[0]->connected
)
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
);
/***********************************************************************\
* 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 *
\***********************************************************************/
/* find unit and check we have that many defined */
printf("sea%d: unit number too high\n",unit
);
printf("unit: %d\n",unit
);
printf("dev_addr: 0x%lx\n",dev
->id_maddr
);
/* allocate a storage area for us */
printf("sea%d: memory already allocated\n", unit
);
printf("Before malloc\n");
sea
= malloc(sizeof(struct sea_data
), M_TEMP
, M_NOWAIT
);
printf("sea%d: cannot malloc!\n", unit
);
printf("after malloc\n");
bzero(sea
,sizeof(struct sea_data
));
/* check for address if no one specified */
/* 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 */
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];
printf("id_maddr != 0\n");
for(j
= 0; j
< 32767 ; j
++);
for(j
= 0; j
< 32767 ; j
++);
/* find sea_slot position for overridden memory address */
for(j
= 0; ((char *)vtophys(dev
->id_maddr
) != seagate_bases
[j
]) &&
printf("sea: board not expected at address 0x%lx\n",dev
->id_maddr
);
} else if(sea_slot
> j
) {
printf("sea: board address 0x%lx already probed!\n", dev
->id_maddr
);
sea
->basemaddr
= dev
->id_maddr
;
printf("sea->basemaddr = %lx\n", sea
->basemaddr
);
/* 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
;
if(j
== NUM_SIGNATURES
) {
printf("sea: Board type unknown at address 0x%lx\n",
/* 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) {
/* if its there put in it's interrupt vector */
/* (Doesn't use dma, so no drq is set) */
/***********************************************\
* Attach all sub-devices we can find *
\***********************************************/
struct sea_data
*sea
= seadata
[unit
];
printf("sea_attach called\n");
/* 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
;
/*****************************************************\
* ask the adapter what subunits are present *
\*****************************************************/
scsi_attachdevs(&(sea
->sc_link
));
/***********************************************\
* Return some information to the caller about *
* the adapter and its capabilities *
\***********************************************/
printf("sea_adapter_info called\n");
/***********************************************\
* Catch an interrupt from the adaptor *
\***********************************************/
struct sea_data
*sea
= seadata
[unit
];
/* 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
)) {
/* enable_intr(); */ /* ?? How should this be done ?? */
} else if (STATUS
& STAT_PARITY
) {
/* Parity error interrupt */
printf("sea%d: PARITY interrupt\n", unit
);
/* printf("sea%d: unknown interrupt\n",unit); */
oldpri
= splbio(); /* disable_intr(); */
/* main_running is cleared in sea_main once it can't
* do more work, and sea_main exits with interrupts
splx(oldpri
); /* enable_intr(); */
splx(oldpri
); /* enable_intr(); */
/***********************************************\
* Setup data structures, and reset the board *
\***********************************************/
struct sea_data
*sea
= seadata
[unit
];
printf("sea_init called\n");
/* 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 */
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
)
/* init fields used by our routines */
sea
->disconnected_queue
= NULL
;
/* 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
;
/***********************************************\
\***********************************************/
/* printf("seaminphys called\n"); */
/***********************************************\
* start a scsi operation given the command and *
* the data address. Also needs the unit, target *
* get a free scb and set it up *
* either start timer or wait until done *
\***********************************************/
struct scsi_sense_data
*s1
, *s2
;
int unit
= xs
->sc_link
->adapter_unit
;
struct sea_data
*sea
= seadata
[unit
];
/* printf("scsi_cmd\n"); */
if(xs
->sc_link
->target
!= 1) {
if(xs
->bp
) flags
|= (SCSI_NOSLEEP
);
printf("sea%d: Already done?", unit
);
printf("sea%d: Not in use?", unit
);
if (!(scb
= sea_get_scb(unit
, flags
))) {
xs
->error
= XS_DRIVER_STUFFUP
;
* Put all the arguments for the xfer in the scb
scb
->datalen
= xs
->datalen
;
/* 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 ? */
if (sea_send_scb(sea
, scb
) == 0) {
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 */
return(SUCCESSFULLY_QUEUED
);
timeout(sea_timeout
, (caddr_t
)scb
, (xs
->timeout
* hz
) / 1000);
timeout(sea_timeout
, scb
, (xs
->timeout
* hz
) / 1000);
scb
->flags
|= SCB_TIMECHK
;
return(SUCCESSFULLY_QUEUED
);
* If we can't use interrupts, poll on completion
result
= sea_poll(unit
, xs
, scb
);
printf("=5 %lx", 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_data
*sea
= seadata
[unit
];
/* printf("get_scb\n"); */
if (!(flags
& SCSI_NOMASK
))
printf("(2 %lx ", sea
->free_scb
);
* If we can and have to, sleep waiting for one to come free
* but only if we can´t allocate a new one.
while (!(scbp
= sea
->free_scb
)) {
if (sea
->numscb
< SEA_SCB_MAX
) {
printf("malloced new scbs\n");
if (scbp
= (struct sea_scb
*) malloc(sizeof(struct sea_scb
),
bzero(scbp
, sizeof(struct sea_scb
));
scbp
->flags
= SCB_ACTIVE
;
printf("sea%d: Can't malloc SCB\n",unit
);
if(!(flags
& SCSI_NOSLEEP
)) {
sleep(&sea
->free_scb
, PRIBIO
);
/* Get SCB from free list */
sea
->free_scb
= scbp
->next
;
scbp
->flags
= SCB_ACTIVE
;
if (!(flags
& SCSI_NOMASK
))
* 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
* 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 ??
sea_send_scb(struct sea_data
*sea
, struct sea_scb
*scb
)
if(!(scb
->xfer
->flags
& SCSI_NOSLEEP
)) {
/* add to head of queue if queue empty or command is REQUEST_SENSE */
|| (scb
->xfer
->cmd
->opcode
== (u_char
) REQUEST_SENSE
)
scb
->next
= sea
->issue_queue
;
for (tmp
= sea
->issue_queue
; tmp
->next
; tmp
= tmp
->next
);
scb
->next
= NULL
; /* placed at the end of the queue */
/* Try to do some work on the card */
/* main running is cleared in sea_main once it can't
* do more work, and sea_main exits with interrupts
if(!(scb
->xfer
->flags
& SCSI_NOSLEEP
)) {
return (1); /* No possible errors right now */
* 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
;
* This should not be run with interrupts disabled, but use the splx code
for (sea
=seadata
[unit
=0]; (unit
< NSEA
) && seadata
[unit
] ;
* 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
))) {
sea
->issue_queue
= tmp
->next
;
/* re-enable interrupts after finding one */
* 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.
/* REQUEST_SENSE commands are issued without tagged
* queueing, even on SCSI-II devices because the
* contingent alligence condition exists for the
/* 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
)) {
if (!sea_select(sea
, tmp
)) {
/* printf("Select returned ok\n"); */
tmp
->next
= sea
->issue_queue
;
printf("sea_main: select failed\n");
} /* if target/lun is not busy */
} /* if (!sea->connected) */
if (sea
->connected
) { /* we are connected. Do the task */
/* printf("sea_main: starting information transfer!\n"); */
sea_information_transfer(sea
);
/* printf("sea_main: sea->connected:%lx\n", sea->connected); */
printf(".6%lx ", sea
->connected
);
sea_free_scb(unit
, scb
, flags
)
struct sea_data
*sea
= seadata
[unit
];
/* printf("free_scb\n"); */
if(!(flags
& SCSI_NOMASK
))
scb
->next
= sea
->free_scb
;
* If there were none, wake anybody waiting for one to come free,
* starting with queued entries.
/* printf("free_scb waking up sleep\n"); */
wakeup((caddr_t
)&sea
->free_scb
);
if (!(flags
& SCSI_NOMASK
))
sea_timeout(caddr_t arg1
, int arg2
)
sea_timeout(struct sea_scb
*scb
)
struct sea_scb
*scb
= (struct sea_scb
*)arg1
;
/* printf("sea_timeout called\n"); */
unit
= scb
->xfer
->sc_link
->adapter_unit
;
#ifndef SEADEBUG /* print message only if not waiting unless debug */
if(!(scb
->xfer
->flags
& SCSI_NOMASK
))
printf("sea%d:%d:%d (%s%d) timed out ", unit
,
scb
->xfer
->sc_link
->target
,
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
if (/* (sea_abort(unit, scb) != 1) ||*/ (scb
->flags
& SCB_ABORTED
)) {
/* printf("sea%d: Abort Operation has timed out\n", unit); */
scb
->flags
|= SCB_ABORTED
;
/* printf("sea%d: Try to abort\n", unit); */
/* sea_send_scb(sea, ~SCSI_NOMASK, SEA_SCB_ABORT, scb); */
/* 2 seconds for the abort */
timeout(sea_timeout
, (caddr_t
)scb
, 2*hz
);
timeout(sea_timeout
, scb
, 2*hz
);
scb
->flags
|= (SCB_ABORTED
| SCB_TIMECHK
);
unsigned char target_mask
;
unsigned char lun
, phase
;
struct sea_scb
*tmp
= 0, *prev
= 0;
/* printf("sea_reselect called\n"); */
if (!((target_mask
= STATUS
) & STAT_SEL
)) {
printf("sea: wrong state 0x%x\n", target_mask
);
/* 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 */
/* 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
);
/* 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 */
sea_transfer_pio(sea
, &phase
, &len
, &data
);
printf("sea: Expecting IDENTIFY message, got 0x%x\n", msg
[0]);
* 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
)) {
sea
->disconnected_queue
= tmp
->next
;
printf("sea: warning : target %02x lun %d not in disconnect_queue\n",
* Since we have an established nexus that we can't do anything with,
CONTROL
= (BASE_CMD
| CMD_ATTN
);
sea_transfer_pio(sea
, &phase
, &len
, &data
);
/* return value not used yet */
/* Transfer data in given phase using polled I/O
int sea_transfer_pio(struct sea_data
*sea
, u_char
*phase
, int32
*count
,
register unsigned char p
= *phase
, tmp
;
register unsigned char *d
= *data
;
unsigned long int timeout
;
/* printf("sea_transfer_pio called: len:%x\n",c); */
printf("-1 %x %x", c
, p
);
/* wait for assertion of REQ, after which the phase bits will be valid */
for(timeout
= 0; timeout
< 5000000L ; timeout
++)
if ((tmp
= STATUS
) & STAT_REQ
)
printf("sea_transfer_pio: timeout waiting for STAT_REQ\n");
/* check for phase mismatch */
/* Reached if the target decides that it has finished the transfer */
if ((tmp
& REQ_MASK
) != p
) {
/* printf("-2 %x", tmp); */
printf("sea:pio phase mismatch:%x, want:%x, len:%x\n",tmp
,p
,c
);
/* Do actual transfer from SCSI bus to/from memory */
/* 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++)
printf("timeout on wait for !STAT_REQ"); */
if (!c
|| (*phase
== p
)) {
printf("-5%x %x", c
, *phase
);
* 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
* 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
;
/* printf("sea_select called\n"); */
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
)
if (!(STATUS
& STAT_ARB_CMPL
)) {
printf("sea: arbitration lost\n");
printf("sea: arbitration timeout.\n");
scb
->flags
|=SCB_TIMEOUT
;
/* printf("after arbitration: STATUS=%x\n", STATUS); */
DATA
= (unsigned char)((1 << scb
->xfer
->sc_link
->target
) |
((sea
->ctrl_type
== SEAGATE
) ? 0x80 : 0x40));
CONTROL
= (BASE_CMD
& (~CMD_INTR
))| CMD_DRVR_ENABLE
| CMD_SEL
;
CONTROL
= (BASE_CMD
& (~CMD_INTR
)) | CMD_DRVR_ENABLE
| CMD_SEL
| CMD_ATTN
;
/* wait for a bsy from target */
for (timeout
= 0; timeout
< 2000000L; timeout
++) {
/* printf("after wait for BSY: STATUS=%x,count=%lx\n", STATUS, timeout); */
printf("{3 %x %x", STATUS
, timeout
);
if (!(STATUS
& STAT_BSY
)) {
/* should return some error to the higher level driver */
/* printf("sea: target did not respond\n"); */
scb
->flags
|= SCB_TIMEOUT
;
/* Try to make the target to take a message from us */
CONTROL
= (BASE_CMD
& (~CMD_INTR
)) | CMD_DRVR_ENABLE
;
CONTROL
= (BASE_CMD
& (~CMD_INTR
)) | CMD_DRVR_ENABLE
| CMD_ATTN
;
/* should start a msg_out phase */
for (timeout
= 0; timeout
< 2000000L ; timeout
++) {
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); */
if (!(STATUS
& STAT_REQ
)) {
/* This should not be taken as an error, but more like an unsupported
* Should set a flag indicating that the target don't support messages, and
* continue without failure. (THIS IS NOT AN ERROR!)
printf("sea: WARNING: target %x don't support messages?\n",
scb
->xfer
->sc_link
->target
);
tmp
[0] = IDENTIFY(1, scb
->xfer
->sc_link
->lun
); /* allow disconnects */
/* Should do test on result of sea_transfer_pio */
/* printf("Trying a msg out phase\n"); */
sea_transfer_pio(sea
, &phase
, &len
, &data
);
if (!(STATUS
& STAT_BSY
)) {
printf("sea: after successful arbitrate: No STAT_BSY!\n");
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 ??? */
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
;
/* printf("sea_abort called\n"); */
/* If the command hasn't been issued yet, we simply remove it from the
for (prev
= (struct sea_scb
**) &(sea
->issue_queue
),
tmp
= sea
->issue_queue
; tmp
;
prev
= (struct sea_scb
**) &(tmp
->next
), tmp
= tmp
->next
)
/* set some type of error result for this operation */
/* 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
/* printf("sea:abort error connected\n"); */
/* 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 (sea_select(sea
,scb
)) {
CONTROL
= BASE_CMD
| CMD_ATTN
;
sea_transfer_pio(sea
, &phase
, &len
, &msgptr
);
for (prev
= (struct sea_scb
**) &(sea
->disconnected_queue
),
tmp
= sea
->disconnected_queue
; tmp
;
prev
= (struct sea_scb
**) &(tmp
->next
), tmp
= tmp
->next
)
/* set some type of error result for the operation */
/* command not found in any queue, race condition in the code ? */
/* printf("sea: WARNING: SCSI command probably completed successfully\n"
" before abortion\n"); */
void sea_done(int unit
, struct sea_scb
*scb
)
struct sea_data
*sea
= seadata
[unit
];
struct scsi_xfer
*xs
= scb
->xfer
;
/* printf("sea_done called\n"); */
if (scb
->flags
& SCB_TIMECHK
) {
untimeout(sea_timeout
, (caddr_t
)scb
);
untimeout(sea_timeout
, scb
);
xs
->resid
= scb
->datalen
; /* How much of the buffer was not touched */
if ((scb
->flags
== SCB_ACTIVE
) || (xs
->flags
& SCSI_ERR_OK
)) {
/* printf("sea_done:Report no err in xs\n"); */
if (!(scb
->flags
== SCB_ACTIVE
)) {
if ((scb
->flags
& SCB_TIMEOUT
) || (scb
->flags
& SCB_ABORTED
)) {
if (scb
->flags
& SCB_ERROR
) {
xs
->error
= XS_DRIVER_STUFFUP
;
/* !!! Add code to check for target status */
xs
->error
= XS_DRIVER_STUFFUP
;
sea_free_scb(unit
, scb
, xs
->flags
);
/* printf("Leaving sea_done\n"); */
/* 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; */
/* printf("sea_poll called\n"); */
/* try to do something */
/* main_running is cleared in sea_main once it can't
* do more work, and sea_main exits with interrupts
if (xs
->flags
& ITSDONE
) {
/* printf("sea_poll: count:%x\n",count); */
/* 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.
sea_timeout((caddr_t
)scb
, 0);
/* because we are polling, take out the timeout entry
untimeout(sea_timeout
, (caddr_t
) scb
);
untimeout(sea_timeout
, scb
);
/* once again, wait for the int bit */
/* main_running is cleared by sea_main once it can't
* do more work, and sea_main exits with interrupts
if (xs
->flags
& ITSDONE
) {
/* we timed out again... This is bad. Notice that
* this time there is no clock queue entry to remove
sea_timeout((caddr_t
)scb
, 0);
/* printf("sea_poll: xs->error:%x\n",xs->error); */
printf("?6%x",xs
->error
);
/* printf("done return error\n"); */
/* printf("done return complete\n"); */
* sea_information_transfer
* Do the transfer. We know we are connected. Update the flags,
* call sea_done when task accomplished. Dialog controlled by the
static void sea_information_transfer (struct sea_data
*sea
)
int unit
= sea
->sc_link
.adapter_unit
;
unsigned char msgout
= MSG_NOP
;
unsigned char phase
, tmp
, old_phase
=REQ_UNKNOWN
;
struct sea_scb
*scb
= sea
->connected
;
/* printf("sea_information_transfer called\n"); */
for(timeout
= 0; timeout
< 10000000L ; timeout
++) {
/* for(loop=0;loop < 20 ; loop++) {
if((tmp=STATUS) & STAT_BSY)
printf("sea: !STAT_BSY unit in data transfer!\n");
/* we only have a valid SCSI phase when REQ is asserted */
phase
= (tmp
& REQ_MASK
);
if (phase
!= old_phase
) {
for(loop
=0;loop
< 20; loop
++) {
phase
= phase
& REQ_MASK
;
printf("sea: SEA_NODATAOUT set, attempted DATAOUT aborted\n");
CONTROL
= BASE_CMD
| CMD_ATTN
;
/* data = scb->xfer->data;
len = scb->xfer->datalen;
printf("no data address!\n");
if (scb
->datalen
&& !(scb
->datalen
% BLOCK_SIZE
)) {
for(timeout
= 0; timeout
< 5000000L ; timeout
++)
if((tmp
= STATUS
) & STAT_REQ
)
printf("sea_transfer_pio: timeout waiting for STAT_REQ\n");
if((tmp
& REQ_MASK
) != phase
) {
printf("sea:infotransfer phase mismatch:%x, want:%x, len:%x\n",
"D" (sea
->st0x_dr
), "S" (scb
->data
), "c" (BLOCK_SIZE
) :
for(count
=0; count
< BLOCK_SIZE
; count
++) {
"S" (sea
->st0x_dr
), "D" (scb
->data
), "c" (BLOCK_SIZE
) :
for(count
=0; count
< BLOCK_SIZE
; count
++) {
scb
->datalen
-= BLOCK_SIZE
;
/* save current position into the command structure */
/* scb->xfer->data = data;
scb->xfer->datalen = len; */
sea_transfer_pio(sea
, &phase
, &(scb
->datalen
), &(scb
->data
));
/* scb->xfer->data = data;
scb->xfer->datalen = len;
/* don't handle multi-byte messages here, because they
* should not be present here
sea_transfer_pio(sea
, &phase
, &len
, &data
);
/* scb->MessageIn = tmp; */
scb
->flags
= SCB_ABORTED
;
printf("sea:Command aborted by target\n");
case MSG_COMMAND_COMPLETE
:
sea
->busy
[scb
->xfer
->sc_link
->target
] &=
~(1 << scb
->xfer
->sc_link
->lun
);
/* printf("sea: message_reject recieved\n"); */
scb
->next
= sea
->disconnected_queue
;
sea
->disconnected_queue
= scb
;
/* printf("msg_disconnect\n"); */
/* save/restore of pointers are ignored */
case MSG_RESTORE_POINTERS
:
printf("sea: rec save/restore ptrs\n");
/* this should be handled in the pio data transfer phase, as the
* ATN should be raised before ACK goes false when rejecting a message
printf("sea: Unknown message in:%x\n", tmp
);
/* sea->last_message = msgout; */
sea_transfer_pio(sea
, &phase
, &len
, &data
);
if (msgout
== MSG_ABORT
) {
printf("sea: sent message abort to target\n");
sea
->busy
[scb
->xfer
->sc_link
->target
] &=
~(1 << scb
->xfer
->sc_link
->lun
);
scb
->flags
= SCB_ABORTED
;
/* enable interrupt from scsi */
data
= (char *) scb
->xfer
->cmd
;
sea_transfer_pio(sea
, &phase
, &len
, &data
);
sea_transfer_pio(sea
, &phase
, &len
, &data
);
printf("sea: unknown phase\n");
} /* if (tmp & STAT_REQ) */
/* 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? */