Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Linux/drivers/scsi/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 79 kB image not shown  

Quelle  initio.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-or-later
/**************************************************************************
 * Initio 9100 device driver for Linux.
 *
 * Copyright (c) 1994-1998 Initio Corporation
 * Copyright (c) 1998 Bas Vermeulen <bvermeul@blackstar.xs4all.nl>
 * Copyright (c) 2004 Christoph Hellwig <hch@lst.de>
 * Copyright (c) 2007 Red Hat
 *
 *************************************************************************
 *
 * DESCRIPTION:
 *
 * This is the Linux low-level SCSI driver for Initio INI-9X00U/UW SCSI host
 * adapters
 *
 * 08/06/97 hc - v1.01h
 * - Support inic-940 and inic-935
 * 09/26/97 hc - v1.01i
 * - Make correction from J.W. Schultz suggestion
 * 10/13/97 hc - Support reset function
 * 10/21/97 hc - v1.01j
 * - Support 32 LUN (SCSI 3)
 * 01/14/98 hc - v1.01k
 * - Fix memory allocation problem
 * 03/04/98 hc - v1.01l
 * - Fix tape rewind which will hang the system problem
 * - Set can_queue to initio_num_scb
 * 06/25/98 hc - v1.01m
 * - Get it work for kernel version >= 2.1.75
 * - Dynamic assign SCSI bus reset holding time in initio_init()
 * 07/02/98 hc - v1.01n
 * - Support 0002134A
 * 08/07/98 hc  - v1.01o
 * - Change the initio_abort_srb routine to use scsi_done. <01>
 * 09/07/98 hl  - v1.02
 *              - Change the INI9100U define and proc_dir_entry to
 *                reflect the newer Kernel 2.1.118, but the v1.o1o
 *                should work with Kernel 2.1.118.
 * 09/20/98 wh  - v1.02a
 *              - Support Abort command.
 *              - Handle reset routine.
 * 09/21/98 hl  - v1.03
 *              - remove comments.
 * 12/09/98 bv - v1.03a
 * - Removed unused code
 * 12/13/98 bv - v1.03b
 * - Remove cli() locking for kernels >= 2.1.95. This uses
 *   spinlocks to serialize access to the pSRB_head and
 *   pSRB_tail members of the HCS structure.
 * 09/01/99 bv - v1.03d
 * - Fixed a deadlock problem in SMP.
 * 21/01/99 bv - v1.03e
 * - Add support for the Domex 3192U PCI SCSI
 *   This is a slightly modified patch by
 *   Brian Macy <bmacy@sunshinecomputing.com>
 * 22/02/99 bv - v1.03f
 * - Didn't detect the INIC-950 in 2.0.x correctly.
 *   Now fixed.
 * 05/07/99 bv - v1.03g
 * - Changed the assumption that HZ = 100
 * 10/17/03 mc - v1.04
 * - added new DMA API support
 * 06/01/04 jmd - v1.04a
 * - Re-add reset_bus support
 **************************************************************************/


#include <linux/module.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/blkdev.h>
#include <linux/spinlock.h>
#include <linux/stat.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/string.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/dma-mapping.h>
#include <asm/io.h>

#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_tcq.h>

#include "initio.h"

#define SENSE_SIZE  14

#define i91u_MAXQUEUE  2
#define i91u_REVID "Initio INI-9X00U/UW SCSI device driver; Revision: 1.04a"

#ifdef DEBUG_i91u
static unsigned int i91u_debug = DEBUG_DEFAULT;
#endif

static int initio_tag_enable = 1;

#ifdef DEBUG_i91u
static int setup_debug = 0;
#endif

static void i91uSCBPost(u8 * pHcb, u8 * pScb);

#define DEBUG_INTERRUPT 0
#define DEBUG_QUEUE     0
#define DEBUG_STATE     0
#define INT_DISC 0

/*--- forward references ---*/
static struct scsi_ctrl_blk *initio_find_busy_scb(struct initio_host * host, u16 tarlun);
static struct scsi_ctrl_blk *initio_find_done_scb(struct initio_host * host);

static int tulip_main(struct initio_host * host);

static int initio_next_state(struct initio_host * host);
static int initio_state_1(struct initio_host * host);
static int initio_state_2(struct initio_host * host);
static int initio_state_3(struct initio_host * host);
static int initio_state_4(struct initio_host * host);
static int initio_state_5(struct initio_host * host);
static int initio_state_6(struct initio_host * host);
static int initio_state_7(struct initio_host * host);
static int initio_xfer_data_in(struct initio_host * host);
static int initio_xfer_data_out(struct initio_host * host);
static int initio_xpad_in(struct initio_host * host);
static int initio_xpad_out(struct initio_host * host);
static int initio_status_msg(struct initio_host * host);

static int initio_msgin(struct initio_host * host);
static int initio_msgin_sync(struct initio_host * host);
static int initio_msgin_accept(struct initio_host * host);
static int initio_msgout_reject(struct initio_host * host);
static int initio_msgin_extend(struct initio_host * host);

static int initio_msgout_ide(struct initio_host * host);
static int initio_msgout_abort_targ(struct initio_host * host);
static int initio_msgout_abort_tag(struct initio_host * host);

static int initio_bus_device_reset(struct initio_host * host);
static void initio_select_atn(struct initio_host * host, struct scsi_ctrl_blk * scb);
static void initio_select_atn3(struct initio_host * host, struct scsi_ctrl_blk * scb);
static void initio_select_atn_stop(struct initio_host * host, struct scsi_ctrl_blk * scb);
static int int_initio_busfree(struct initio_host * host);
static int int_initio_scsi_rst(struct initio_host * host);
static int int_initio_bad_seq(struct initio_host * host);
static int int_initio_resel(struct initio_host * host);
static int initio_sync_done(struct initio_host * host);
static int wdtr_done(struct initio_host * host);
static int wait_tulip(struct initio_host * host);
static int initio_wait_done_disc(struct initio_host * host);
static int initio_wait_disc(struct initio_host * host);
static void tulip_scsi(struct initio_host * host);
static int initio_post_scsi_rst(struct initio_host * host);

static void initio_se2_ew_en(unsigned long base);
static void initio_se2_ew_ds(unsigned long base);
static int initio_se2_rd_all(unsigned long base);
static void initio_se2_update_all(unsigned long base); /* setup default pattern */
static void initio_read_eeprom(unsigned long base);

/* ---- INTERNAL VARIABLES ---- */

static NVRAM i91unvram;
static NVRAM *i91unvramp;

static u8 i91udftNvRam[64] =
{
 /*----------- header -----------*/
 0x25, 0xc9,  /* Signature    */
 0x40,   /* Size         */
 0x01,   /* Revision     */
 /* -- Host Adapter Structure -- */
 0x95,   /* ModelByte0   */
 0x00,   /* ModelByte1   */
 0x00,   /* ModelInfo    */
 0x01,   /* NumOfCh      */
 NBC1_DEFAULT,  /* BIOSConfig1  */
 0,   /* BIOSConfig2  */
 0,   /* HAConfig1    */
 0,   /* HAConfig2    */
 /* SCSI channel 0 and target Structure  */
 7,   /* SCSIid       */
 NCC1_DEFAULT,  /* SCSIconfig1  */
 0,   /* SCSIconfig2  */
 0x10,   /* NumSCSItarget */

 NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
 NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
 NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
 NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,

 /* SCSI channel 1 and target Structure  */
 7,   /* SCSIid       */
 NCC1_DEFAULT,  /* SCSIconfig1  */
 0,   /* SCSIconfig2  */
 0x10,   /* NumSCSItarget */

 NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
 NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
 NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
 NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 0, 0};   /*      - CheckSum -            */


static u8 initio_rate_tbl[8] = /* fast 20      */
{
    /* nanosecond divide by 4 */
 12,   /* 50ns,  20M   */
 18,   /* 75ns,  13.3M */
 25,   /* 100ns, 10M   */
 31,   /* 125ns, 8M    */
 37,   /* 150ns, 6.6M  */
 43,   /* 175ns, 5.7M  */
 50,   /* 200ns, 5M    */
 62   /* 250ns, 4M    */
};

static void initio_do_pause(unsigned amount)
{
 /* Pause for amount jiffies */
 unsigned long the_time = jiffies + amount;

 while (time_before_eq(jiffies, the_time))
  cpu_relax();
}

/*-- forward reference --*/

/******************************************************************
 Input: instruction for  Serial E2PROM

 EX: se2_rd(0 call se2_instr() to send address and read command

 StartBit  OP_Code   Address                Data
 --------- --------  ------------------     -------
 1         1 , 0     A5,A4,A3,A2,A1,A0      D15-D0

 +-----------------------------------------------------
 |
 CS -----+
+--+  +--+  +--+  +--+  +--+
^  |  ^  |  ^  |  ^  |  ^  |
|  |  |  |  |  |  |  |  |  |
 CLK -------+  +--+  +--+  +--+  +--+  +--
 (leading edge trigger)

 +--1-----1--+
 | SB    OP  |  OP    A5    A4
 DI  ----+           +--0------------------
 (address and cmd sent to nvram)

 -------------------------------------------+
|
 DO                                             +---
 (data sent from nvram)


******************************************************************/


/**
 * initio_se2_instr - bitbang an instruction
 * @base: Base of InitIO controller
 * @instr: Instruction for serial E2PROM
 *
 * Bitbang an instruction out to the serial E2Prom
 */


static void initio_se2_instr(unsigned long base, u8 instr)
{
 int i;
 u8 b;

 outb(SE2CS | SE2DO, base + TUL_NVRAM);  /* cs+start bit */
 udelay(30);
 outb(SE2CS | SE2CLK | SE2DO, base + TUL_NVRAM); /* +CLK */
 udelay(30);

 for (i = 0; i < 8; i++) {
  if (instr & 0x80)
   b = SE2CS | SE2DO;  /* -CLK+dataBit */
  else
   b = SE2CS;   /* -CLK */
  outb(b, base + TUL_NVRAM);
  udelay(30);
  outb(b | SE2CLK, base + TUL_NVRAM); /* +CLK */
  udelay(30);
  instr <<= 1;
 }
 outb(SE2CS, base + TUL_NVRAM);   /* -CLK */
 udelay(30);
}


/**
 * initio_se2_ew_en - Enable erase/write
 * @base: Base address of InitIO controller
 *
 * Enable erase/write state of serial EEPROM
 */

void initio_se2_ew_en(unsigned long base)
{
 initio_se2_instr(base, 0x30); /* EWEN */
 outb(0, base + TUL_NVRAM); /* -CS  */
 udelay(30);
}


/**
 * initio_se2_ew_ds - Disable erase/write
 * @base: Base address of InitIO controller
 *
 * Disable erase/write state of serial EEPROM
 */

void initio_se2_ew_ds(unsigned long base)
{
 initio_se2_instr(base, 0); /* EWDS */
 outb(0, base + TUL_NVRAM); /* -CS  */
 udelay(30);
}


/**
 * initio_se2_rd - read E2PROM word
 * @base: Base of InitIO controller
 * @addr: Address of word in E2PROM
 *
 * Read a word from the NV E2PROM device
 */

static u16 initio_se2_rd(unsigned long base, u8 addr)
{
 u8 instr, rb;
 u16 val = 0;
 int i;

 instr = (u8) (addr | 0x80);
 initio_se2_instr(base, instr); /* READ INSTR */

 for (i = 15; i >= 0; i--) {
  outb(SE2CS | SE2CLK, base + TUL_NVRAM); /* +CLK */
  udelay(30);
  outb(SE2CS, base + TUL_NVRAM);  /* -CLK */

  /* sample data after the following edge of clock  */
  rb = inb(base + TUL_NVRAM);
  rb &= SE2DI;
  val += (rb << i);
  udelay(30); /* 6/20/95 */
 }

 outb(0, base + TUL_NVRAM);  /* no chip select */
 udelay(30);
 return val;
}

/**
 * initio_se2_wr - read E2PROM word
 * @base: Base of InitIO controller
 * @addr: Address of word in E2PROM
 * @val: Value to write
 *
 * Write a word to the NV E2PROM device. Used when recovering from
 * a problem with the NV.
 */

static void initio_se2_wr(unsigned long base, u8 addr, u16 val)
{
 u8 instr;
 int i;

 instr = (u8) (addr | 0x40);
 initio_se2_instr(base, instr); /* WRITE INSTR */
 for (i = 15; i >= 0; i--) {
  if (val & 0x8000)
   outb(SE2CS | SE2DO, base + TUL_NVRAM); /* -CLK+dataBit 1 */
  else
   outb(SE2CS, base + TUL_NVRAM);  /* -CLK+dataBit 0 */
  udelay(30);
  outb(SE2CS | SE2CLK, base + TUL_NVRAM);  /* +CLK */
  udelay(30);
  val <<= 1;
 }
 outb(SE2CS, base + TUL_NVRAM);    /* -CLK */
 udelay(30);
 outb(0, base + TUL_NVRAM);    /* -CS  */
 udelay(30);

 outb(SE2CS, base + TUL_NVRAM);    /* +CS  */
 udelay(30);

 for (;;) {
  outb(SE2CS | SE2CLK, base + TUL_NVRAM);  /* +CLK */
  udelay(30);
  outb(SE2CS, base + TUL_NVRAM);   /* -CLK */
  udelay(30);
  if (inb(base + TUL_NVRAM) & SE2DI)
   break/* write complete */
 }
 outb(0, base + TUL_NVRAM);    /* -CS */
}

/**
 * initio_se2_rd_all - read hostadapter NV configuration
 * @base: Base address of InitIO controller
 *
 * Reads the E2PROM data into main memory. Ensures that the checksum
 * and header marker are valid. Returns 1 on success -1 on error.
 */


static int initio_se2_rd_all(unsigned long base)
{
 int i;
 u16 chksum = 0;
 u16 *np;

 i91unvramp = &i91unvram;
 np = (u16 *) i91unvramp;
 for (i = 0; i < 32; i++)
  *np++ = initio_se2_rd(base, i);

 /* Is signature "ini" ok ? */
 if (i91unvramp->NVM_Signature != INI_SIGNATURE)
  return -1;
 /* Is ckecksum ok ? */
 np = (u16 *) i91unvramp;
 for (i = 0; i < 31; i++)
  chksum += *np++;
 if (i91unvramp->NVM_CheckSum != chksum)
  return -1;
 return 1;
}

/**
 * initio_se2_update_all - Update E2PROM
 * @base: Base of InitIO controller
 *
 * Update the E2PROM by wrting any changes into the E2PROM
 * chip, rewriting the checksum.
 */

static void initio_se2_update_all(unsigned long base)
{    /* setup default pattern */
 int i;
 u16 chksum = 0;
 u16 *np, *np1;

 i91unvramp = &i91unvram;
 /* Calculate checksum first */
 np = (u16 *) i91udftNvRam;
 for (i = 0; i < 31; i++)
  chksum += *np++;
 *np = chksum;
 initio_se2_ew_en(base); /* Enable write  */

 np = (u16 *) i91udftNvRam;
 np1 = (u16 *) i91unvramp;
 for (i = 0; i < 32; i++, np++, np1++) {
  if (*np != *np1)
   initio_se2_wr(base, i, *np);
 }
 initio_se2_ew_ds(base); /* Disable write   */
}

/**
 * initio_read_eeprom - Retrieve configuration
 * @base: Base of InitIO Host Adapter
 *
 * Retrieve the host adapter configuration data from E2Prom. If the
 * data is invalid then the defaults are used and are also restored
 * into the E2PROM. This forms the access point for the SCSI driver
 * into the E2PROM layer, the other functions for the E2PROM are all
 * internal use.
 *
 * Must be called single threaded, uses a shared global area.
 */


static void initio_read_eeprom(unsigned long base)
{
 u8 gctrl;

 i91unvramp = &i91unvram;
 /* Enable EEProm programming */
 gctrl = inb(base + TUL_GCTRL);
 outb(gctrl | TUL_GCTRL_EEPROM_BIT, base + TUL_GCTRL);
 if (initio_se2_rd_all(base) != 1) {
  initio_se2_update_all(base); /* setup default pattern */
  initio_se2_rd_all(base); /* load again  */
 }
 /* Disable EEProm programming */
 gctrl = inb(base + TUL_GCTRL);
 outb(gctrl & ~TUL_GCTRL_EEPROM_BIT, base + TUL_GCTRL);
}

/**
 * initio_stop_bm - stop bus master
 * @host: InitIO we are stopping
 *
 * Stop any pending DMA operation, aborting the DMA if necessary
 */


static void initio_stop_bm(struct initio_host * host)
{

 if (inb(host->addr + TUL_XStatus) & XPEND) { /* if DMA xfer is pending, abort DMA xfer */
  outb(TAX_X_ABT | TAX_X_CLR_FIFO, host->addr + TUL_XCmd);
  /* wait Abort DMA xfer done */
  while ((inb(host->addr + TUL_Int) & XABT) == 0)
   cpu_relax();
 }
 outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0);
}

/**
 * initio_reset_scsi - Reset SCSI host controller
 * @host: InitIO host to reset
 * @seconds: Recovery time
 *
 * Perform a full reset of the SCSI subsystem.
 */


static int initio_reset_scsi(struct initio_host * host, int seconds)
{
 outb(TSC_RST_BUS, host->addr + TUL_SCtrl0);

 while (!((host->jsint = inb(host->addr + TUL_SInt)) & TSS_SCSIRST_INT))
  cpu_relax();

 /* reset tulip chip */
 outb(0, host->addr + TUL_SSignal);

 /* Stall for a while, wait for target's firmware ready,make it 2 sec ! */
 /* SONY 5200 tape drive won't work if only stall for 1 sec */
 /* FIXME: this is a very long busy wait right now */
 initio_do_pause(seconds * HZ);

 inb(host->addr + TUL_SInt);
 return SCSI_RESET_SUCCESS;
}

/**
 * initio_init - set up an InitIO host adapter
 * @host: InitIO host adapter
 * @bios_addr: BIOS address
 *
 * Set up the host adapter and devices according to the configuration
 * retrieved from the E2PROM.
 *
 * Locking: Calls E2PROM layer code which is not re-enterable so must
 * run single threaded for now.
 */


static void initio_init(struct initio_host * host, u8 *bios_addr)
{
 int i;
 u8 *flags;
 u8 *heads;

 /* Get E2Prom configuration */
 initio_read_eeprom(host->addr);
 if (i91unvramp->NVM_SCSIInfo[0].NVM_NumOfTarg == 8)
  host->max_tar = 8;
 else
  host->max_tar = 16;

 host->config = i91unvramp->NVM_SCSIInfo[0].NVM_ChConfig1;

 host->scsi_id = i91unvramp->NVM_SCSIInfo[0].NVM_ChSCSIID;
 host->idmask = ~(1 << host->scsi_id);

#ifdef CHK_PARITY
 /* Enable parity error response */
 outb(inb(host->addr + TUL_PCMD) | 0x40, host->addr + TUL_PCMD);
#endif

 /* Mask all the interrupt       */
 outb(0x1F, host->addr + TUL_Mask);

 initio_stop_bm(host);
 /* --- Initialize the tulip --- */
 outb(TSC_RST_CHIP, host->addr + TUL_SCtrl0);

 /* program HBA's SCSI ID        */
 outb(host->scsi_id << 4, host->addr + TUL_SScsiId);

 /* Enable Initiator Mode ,phase latch,alternate sync period mode,
   disable SCSI reset */

 if (host->config & HCC_EN_PAR)
  host->sconf1 = (TSC_INITDEFAULT | TSC_EN_SCSI_PAR);
 else
  host->sconf1 = (TSC_INITDEFAULT);
 outb(host->sconf1, host->addr + TUL_SConfig);

 /* Enable HW reselect */
 outb(TSC_HW_RESELECT, host->addr + TUL_SCtrl1);

 outb(0, host->addr + TUL_SPeriod);

 /* selection time out = 250 ms */
 outb(153, host->addr + TUL_STimeOut);

 /* Enable SCSI terminator */
 outb((host->config & (HCC_ACT_TERM1 | HCC_ACT_TERM2)),
  host->addr + TUL_XCtrl);
 outb(((host->config & HCC_AUTO_TERM) >> 4) |
  (inb(host->addr + TUL_GCTRL1) & 0xFE),
  host->addr + TUL_GCTRL1);

 for (i = 0,
      flags = & (i91unvramp->NVM_SCSIInfo[0].NVM_Targ0Config),
      heads = bios_addr + 0x180;
      i < host->max_tar;
      i++, flags++) {
  host->targets[i].flags = *flags & ~(TCF_SYNC_DONE | TCF_WDTR_DONE);
  if (host->targets[i].flags & TCF_EN_255)
   host->targets[i].drv_flags = TCF_DRV_255_63;
  else
   host->targets[i].drv_flags = 0;
  host->targets[i].js_period = 0;
  host->targets[i].sconfig0 = host->sconf1;
  host->targets[i].heads = *heads++;
  if (host->targets[i].heads == 255)
   host->targets[i].drv_flags = TCF_DRV_255_63;
  else
   host->targets[i].drv_flags = 0;
  host->targets[i].sectors = *heads++;
  host->targets[i].flags &= ~TCF_BUSY;
  host->act_tags[i] = 0;
  host->max_tags[i] = 0xFF;
 }   /* for                          */
 printk("i91u: PCI Base=0x%04X, IRQ=%d, BIOS=0x%04X0, SCSI ID=%d\n",
        host->addr, host->pci_dev->irq,
        host->bios_addr, host->scsi_id);
 /* Reset SCSI Bus */
 if (host->config & HCC_SCSI_RESET) {
  printk(KERN_INFO "i91u: Reset SCSI Bus ... \n");
  initio_reset_scsi(host, 10);
 }
 outb(0x17, host->addr + TUL_SCFG1);
 outb(0xE9, host->addr + TUL_SIntEnable);
}

/**
 * initio_alloc_scb - Allocate an SCB
 * @host: InitIO host we are allocating for
 *
 * Walk the SCB list for the controller and allocate a free SCB if
 * one exists.
 */

static struct scsi_ctrl_blk *initio_alloc_scb(struct initio_host *host)
{
 struct scsi_ctrl_blk *scb;
 unsigned long flags;

 spin_lock_irqsave(&host->avail_lock, flags);
 if ((scb = host->first_avail) != NULL) {
#if DEBUG_QUEUE
  printk("find scb at %p\n", scb);
#endif
  if ((host->first_avail = scb->next) == NULL)
   host->last_avail = NULL;
  scb->next = NULL;
  scb->status = SCB_RENT;
 }
 spin_unlock_irqrestore(&host->avail_lock, flags);
 return scb;
}

/**
 * initio_release_scb - Release an SCB
 * @host: InitIO host that owns the SCB
 * @cmnd: SCB command block being returned
 *
 * Return an allocated SCB to the host free list
 */


static void initio_release_scb(struct initio_host * host, struct scsi_ctrl_blk * cmnd)
{
 unsigned long flags;

#if DEBUG_QUEUE
 printk("Release SCB %p; ", cmnd);
#endif
 spin_lock_irqsave(&(host->avail_lock), flags);
 cmnd->srb = NULL;
 cmnd->status = 0;
 cmnd->next = NULL;
 if (host->last_avail != NULL) {
  host->last_avail->next = cmnd;
  host->last_avail = cmnd;
 } else {
  host->first_avail = cmnd;
  host->last_avail = cmnd;
 }
 spin_unlock_irqrestore(&(host->avail_lock), flags);
}

/***************************************************************************/
static void initio_append_pend_scb(struct initio_host * host, struct scsi_ctrl_blk * scbp)
{

#if DEBUG_QUEUE
 printk("Append pend SCB %p; ", scbp);
#endif
 scbp->status = SCB_PEND;
 scbp->next = NULL;
 if (host->last_pending != NULL) {
  host->last_pending->next = scbp;
  host->last_pending = scbp;
 } else {
  host->first_pending = scbp;
  host->last_pending = scbp;
 }
}

/***************************************************************************/
static void initio_push_pend_scb(struct initio_host * host, struct scsi_ctrl_blk * scbp)
{

#if DEBUG_QUEUE
 printk("Push pend SCB %p; ", scbp);
#endif
 scbp->status = SCB_PEND;
 if ((scbp->next = host->first_pending) != NULL) {
  host->first_pending = scbp;
 } else {
  host->first_pending = scbp;
  host->last_pending = scbp;
 }
}

static struct scsi_ctrl_blk *initio_find_first_pend_scb(struct initio_host * host)
{
 struct scsi_ctrl_blk *first;


 first = host->first_pending;
 while (first != NULL) {
  if (first->opcode != ExecSCSI)
   return first;
  if (first->tagmsg == 0) {
   if ((host->act_tags[first->target] == 0) &&
       !(host->targets[first->target].flags & TCF_BUSY))
    return first;
  } else {
   if ((host->act_tags[first->target] >=
     host->max_tags[first->target]) |
       (host->targets[first->target].flags & TCF_BUSY)) {
    first = first->next;
    continue;
   }
   return first;
  }
  first = first->next;
 }
 return first;
}

static void initio_unlink_pend_scb(struct initio_host * host, struct scsi_ctrl_blk * scb)
{
 struct scsi_ctrl_blk *tmp, *prev;

#if DEBUG_QUEUE
 printk("unlink pend SCB %p; ", scb);
#endif

 prev = tmp = host->first_pending;
 while (tmp != NULL) {
  if (scb == tmp) { /* Unlink this SCB              */
   if (tmp == host->first_pending) {
    if ((host->first_pending = tmp->next) == NULL)
     host->last_pending = NULL;
   } else {
    prev->next = tmp->next;
    if (tmp == host->last_pending)
     host->last_pending = prev;
   }
   tmp->next = NULL;
   break;
  }
  prev = tmp;
  tmp = tmp->next;
 }
}

static void initio_append_busy_scb(struct initio_host * host, struct scsi_ctrl_blk * scbp)
{

#if DEBUG_QUEUE
 printk("append busy SCB %p; ", scbp);
#endif
 if (scbp->tagmsg)
  host->act_tags[scbp->target]++;
 else
  host->targets[scbp->target].flags |= TCF_BUSY;
 scbp->status = SCB_BUSY;
 scbp->next = NULL;
 if (host->last_busy != NULL) {
  host->last_busy->next = scbp;
  host->last_busy = scbp;
 } else {
  host->first_busy = scbp;
  host->last_busy = scbp;
 }
}

/***************************************************************************/
static struct scsi_ctrl_blk *initio_pop_busy_scb(struct initio_host * host)
{
 struct scsi_ctrl_blk *tmp;


 if ((tmp = host->first_busy) != NULL) {
  if ((host->first_busy = tmp->next) == NULL)
   host->last_busy = NULL;
  tmp->next = NULL;
  if (tmp->tagmsg)
   host->act_tags[tmp->target]--;
  else
   host->targets[tmp->target].flags &= ~TCF_BUSY;
 }
#if DEBUG_QUEUE
 printk("Pop busy SCB %p; ", tmp);
#endif
 return tmp;
}

/***************************************************************************/
static void initio_unlink_busy_scb(struct initio_host * host, struct scsi_ctrl_blk * scb)
{
 struct scsi_ctrl_blk *tmp, *prev;

#if DEBUG_QUEUE
 printk("unlink busy SCB %p; ", scb);
#endif

 prev = tmp = host->first_busy;
 while (tmp != NULL) {
  if (scb == tmp) { /* Unlink this SCB              */
   if (tmp == host->first_busy) {
    if ((host->first_busy = tmp->next) == NULL)
     host->last_busy = NULL;
   } else {
    prev->next = tmp->next;
    if (tmp == host->last_busy)
     host->last_busy = prev;
   }
   tmp->next = NULL;
   if (tmp->tagmsg)
    host->act_tags[tmp->target]--;
   else
    host->targets[tmp->target].flags &= ~TCF_BUSY;
   break;
  }
  prev = tmp;
  tmp = tmp->next;
 }
 return;
}

struct scsi_ctrl_blk *initio_find_busy_scb(struct initio_host * host, u16 tarlun)
{
 struct scsi_ctrl_blk *tmp;
 u16 scbp_tarlun;


 tmp = host->first_busy;
 while (tmp != NULL) {
  scbp_tarlun = (tmp->lun << 8) | (tmp->target);
  if (scbp_tarlun == tarlun) { /* Unlink this SCB              */
   break;
  }
  tmp = tmp->next;
 }
#if DEBUG_QUEUE
 printk("find busy SCB %p; ", tmp);
#endif
 return tmp;
}

static void initio_append_done_scb(struct initio_host * host, struct scsi_ctrl_blk * scbp)
{
#if DEBUG_QUEUE
 printk("append done SCB %p; ", scbp);
#endif

 scbp->status = SCB_DONE;
 scbp->next = NULL;
 if (host->last_done != NULL) {
  host->last_done->next = scbp;
  host->last_done = scbp;
 } else {
  host->first_done = scbp;
  host->last_done = scbp;
 }
}

struct scsi_ctrl_blk *initio_find_done_scb(struct initio_host * host)
{
 struct scsi_ctrl_blk *tmp;

 if ((tmp = host->first_done) != NULL) {
  if ((host->first_done = tmp->next) == NULL)
   host->last_done = NULL;
  tmp->next = NULL;
 }
#if DEBUG_QUEUE
 printk("find done SCB %p; ",tmp);
#endif
 return tmp;
}

static int initio_abort_srb(struct initio_host * host, struct scsi_cmnd *srbp)
{
 unsigned long flags;
 struct scsi_ctrl_blk *tmp, *prev;

 spin_lock_irqsave(&host->semaph_lock, flags);

 if ((host->semaph == 0) && (host->active == NULL)) {
  /* disable Jasmin SCSI Int        */
  outb(0x1F, host->addr + TUL_Mask);
  spin_unlock_irqrestore(&host->semaph_lock, flags);
  /* FIXME: synchronize_irq needed ? */
  tulip_main(host);
  spin_lock_irqsave(&host->semaph_lock, flags);
  host->semaph = 1;
  outb(0x0F, host->addr + TUL_Mask);
  spin_unlock_irqrestore(&host->semaph_lock, flags);
  return SCSI_ABORT_SNOOZE;
 }
 prev = tmp = host->first_pending; /* Check Pend queue */
 while (tmp != NULL) {
  /* 07/27/98 */
  if (tmp->srb == srbp) {
   if (tmp == host->active) {
    spin_unlock_irqrestore(&host->semaph_lock, flags);
    return SCSI_ABORT_BUSY;
   } else if (tmp == host->first_pending) {
    if ((host->first_pending = tmp->next) == NULL)
     host->last_pending = NULL;
   } else {
    prev->next = tmp->next;
    if (tmp == host->last_pending)
     host->last_pending = prev;
   }
   tmp->hastat = HOST_ABORTED;
   tmp->flags |= SCF_DONE;
   if (tmp->flags & SCF_POST)
    (*tmp->post) ((u8 *) host, (u8 *) tmp);
   spin_unlock_irqrestore(&host->semaph_lock, flags);
   return SCSI_ABORT_SUCCESS;
  }
  prev = tmp;
  tmp = tmp->next;
 }

 prev = tmp = host->first_busy; /* Check Busy queue */
 while (tmp != NULL) {
  if (tmp->srb == srbp) {
   if (tmp == host->active) {
    spin_unlock_irqrestore(&host->semaph_lock, flags);
    return SCSI_ABORT_BUSY;
   } else if (tmp->tagmsg == 0) {
    spin_unlock_irqrestore(&host->semaph_lock, flags);
    return SCSI_ABORT_BUSY;
   } else {
    host->act_tags[tmp->target]--;
    if (tmp == host->first_busy) {
     if ((host->first_busy = tmp->next) == NULL)
      host->last_busy = NULL;
    } else {
     prev->next = tmp->next;
     if (tmp == host->last_busy)
      host->last_busy = prev;
    }
    tmp->next = NULL;


    tmp->hastat = HOST_ABORTED;
    tmp->flags |= SCF_DONE;
    if (tmp->flags & SCF_POST)
     (*tmp->post) ((u8 *) host, (u8 *) tmp);
    spin_unlock_irqrestore(&host->semaph_lock, flags);
    return SCSI_ABORT_SUCCESS;
   }
  }
  prev = tmp;
  tmp = tmp->next;
 }
 spin_unlock_irqrestore(&host->semaph_lock, flags);
 return SCSI_ABORT_NOT_RUNNING;
}

/***************************************************************************/
static int initio_bad_seq(struct initio_host * host)
{
 struct scsi_ctrl_blk *scb;

 printk("initio_bad_seg c=%d\n", host->index);

 if ((scb = host->active) != NULL) {
  initio_unlink_busy_scb(host, scb);
  scb->hastat = HOST_BAD_PHAS;
  scb->tastat = 0;
  initio_append_done_scb(host, scb);
 }
 initio_stop_bm(host);
 initio_reset_scsi(host, 8); /* 7/29/98 */
 return initio_post_scsi_rst(host);
}


/************************************************************************/
static void initio_exec_scb(struct initio_host * host, struct scsi_ctrl_blk * scb)
{
 unsigned long flags;

 scb->mode = 0;

 scb->sgidx = 0;
 scb->sgmax = scb->sglen;

 spin_lock_irqsave(&host->semaph_lock, flags);

 initio_append_pend_scb(host, scb); /* Append this SCB to Pending queue */

/* VVVVV 07/21/98 */
 if (host->semaph == 1) {
  /* Disable Jasmin SCSI Int */
  outb(0x1F, host->addr + TUL_Mask);
  host->semaph = 0;
  spin_unlock_irqrestore(&host->semaph_lock, flags);

  tulip_main(host);

  spin_lock_irqsave(&host->semaph_lock, flags);
  host->semaph = 1;
  outb(0x0F, host->addr + TUL_Mask);
 }
 spin_unlock_irqrestore(&host->semaph_lock, flags);
 return;
}

/***************************************************************************/
static int initio_isr(struct initio_host * host)
{
 if (inb(host->addr + TUL_Int) & TSS_INT_PENDING) {
  if (host->semaph == 1) {
   outb(0x1F, host->addr + TUL_Mask);
   /* Disable Tulip SCSI Int */
   host->semaph = 0;

   tulip_main(host);

   host->semaph = 1;
   outb(0x0F, host->addr + TUL_Mask);
   return 1;
  }
 }
 return 0;
}

static int tulip_main(struct initio_host * host)
{
 struct scsi_ctrl_blk *scb;

 for (;;) {
  tulip_scsi(host); /* Call tulip_scsi              */

  /* Walk the list of completed SCBs */
  while ((scb = initio_find_done_scb(host)) != NULL) { /* find done entry */
   if (scb->tastat == INI_QUEUE_FULL) {
    host->max_tags[scb->target] =
        host->act_tags[scb->target] - 1;
    scb->tastat = 0;
    initio_append_pend_scb(host, scb);
    continue;
   }
   if (!(scb->mode & SCM_RSENS)) {  /* not in auto req. sense mode */
    if (scb->tastat == 2) {

     /* clr sync. nego flag */

     if (scb->flags & SCF_SENSE) {
      u8 len;
      len = scb->senselen;
      if (len == 0)
       len = 1;
      scb->buflen = scb->senselen;
      scb->bufptr = scb->senseptr;
      scb->flags &= ~(SCF_SG | SCF_DIR); /* for xfer_data_in */
      /* so, we won't report wrong direction in xfer_data_in,
   and won't report HOST_DO_DU in state_6 */

      scb->mode = SCM_RSENS;
      scb->ident &= 0xBF; /* Disable Disconnect */
      scb->tagmsg = 0;
      scb->tastat = 0;
      scb->cdblen = 6;
      scb->cdb[0] = SCSICMD_RequestSense;
      scb->cdb[1] = 0;
      scb->cdb[2] = 0;
      scb->cdb[3] = 0;
      scb->cdb[4] = len;
      scb->cdb[5] = 0;
      initio_push_pend_scb(host, scb);
      break;
     }
    }
   } else { /* in request sense mode */

    if (scb->tastat == 2) {  /* check contition status again after sending
   requset sense cmd 0x3 */

     scb->hastat = HOST_BAD_PHAS;
    }
    scb->tastat = 2;
   }
   scb->flags |= SCF_DONE;
   if (scb->flags & SCF_POST) {
    /* FIXME: only one post method and lose casts */
    (*scb->post) ((u8 *) host, (u8 *) scb);
   }
  }  /* while */
  /* find_active: */
  if (inb(host->addr + TUL_SStatus0) & TSS_INT_PENDING)
   continue;
  if (host->active) /* return to OS and wait for xfer_done_ISR/Selected_ISR */
   return 1; /* return to OS, enable interrupt */
  /* Check pending SCB            */
  if (initio_find_first_pend_scb(host) == NULL)
   return 1; /* return to OS, enable interrupt */
 }   /* End of for loop */
 /* statement won't reach here */
}

static void tulip_scsi(struct initio_host * host)
{
 struct scsi_ctrl_blk *scb;
 struct target_control *active_tc;

 /* make sure to service interrupt asap */
 if ((host->jsstatus0 = inb(host->addr + TUL_SStatus0)) & TSS_INT_PENDING) {
  host->phase = host->jsstatus0 & TSS_PH_MASK;
  host->jsstatus1 = inb(host->addr + TUL_SStatus1);
  host->jsint = inb(host->addr + TUL_SInt);
  if (host->jsint & TSS_SCSIRST_INT) { /* SCSI bus reset detected      */
   int_initio_scsi_rst(host);
   return;
  }
  if (host->jsint & TSS_RESEL_INT) { /* if selected/reselected interrupt */
   if (int_initio_resel(host) == 0)
    initio_next_state(host);
   return;
  }
  if (host->jsint & TSS_SEL_TIMEOUT) {
   int_initio_busfree(host);
   return;
  }
  if (host->jsint & TSS_DISC_INT) { /* BUS disconnection            */
   int_initio_busfree(host); /* unexpected bus free or sel timeout */
   return;
  }
  if (host->jsint & (TSS_FUNC_COMP | TSS_BUS_SERV)) { /* func complete or Bus service */
   if (host->active)
    initio_next_state(host);
   return;
  }
 }
 if (host->active != NULL)
  return;

 if ((scb = initio_find_first_pend_scb(host)) == NULL)
  return;

 /* program HBA's SCSI ID & target SCSI ID */
 outb((host->scsi_id << 4) | (scb->target & 0x0F),
  host->addr + TUL_SScsiId);
 if (scb->opcode == ExecSCSI) {
  active_tc = &host->targets[scb->target];

  if (scb->tagmsg)
   active_tc->drv_flags |= TCF_DRV_EN_TAG;
  else
   active_tc->drv_flags &= ~TCF_DRV_EN_TAG;

  outb(active_tc->js_period, host->addr + TUL_SPeriod);
  if ((active_tc->flags & (TCF_WDTR_DONE | TCF_NO_WDTR)) == 0) { /* do wdtr negotiation          */
   initio_select_atn_stop(host, scb);
  } else {
   if ((active_tc->flags & (TCF_SYNC_DONE | TCF_NO_SYNC_NEGO)) == 0) { /* do sync negotiation          */
    initio_select_atn_stop(host, scb);
   } else {
    if (scb->tagmsg)
     initio_select_atn3(host, scb);
    else
     initio_select_atn(host, scb);
   }
  }
  if (scb->flags & SCF_POLL) {
   while (wait_tulip(host) != -1) {
    if (initio_next_state(host) == -1)
     break;
   }
  }
 } else if (scb->opcode == BusDevRst) {
  initio_select_atn_stop(host, scb);
  scb->next_state = 8;
  if (scb->flags & SCF_POLL) {
   while (wait_tulip(host) != -1) {
    if (initio_next_state(host) == -1)
     break;
   }
  }
 } else if (scb->opcode == AbortCmd) {
  if (initio_abort_srb(host, scb->srb) != 0) {
   initio_unlink_pend_scb(host, scb);
   initio_release_scb(host, scb);
  } else {
   scb->opcode = BusDevRst;
   initio_select_atn_stop(host, scb);
   scb->next_state = 8;
  }
 } else {
  initio_unlink_pend_scb(host, scb);
  scb->hastat = 0x16; /* bad command */
  initio_append_done_scb(host, scb);
 }
 return;
}

/**
 * initio_next_state - Next SCSI state
 * @host: InitIO host we are processing
 *
 * Progress the active command block along the state machine
 * until we hit a state which we must wait for activity to occur.
 *
 * Returns zero or a negative code.
 */


static int initio_next_state(struct initio_host * host)
{
 int next;

 next = host->active->next_state;
 for (;;) {
  switch (next) {
  case 1:
   next = initio_state_1(host);
   break;
  case 2:
   next = initio_state_2(host);
   break;
  case 3:
   next = initio_state_3(host);
   break;
  case 4:
   next = initio_state_4(host);
   break;
  case 5:
   next = initio_state_5(host);
   break;
  case 6:
   next = initio_state_6(host);
   break;
  case 7:
   next = initio_state_7(host);
   break;
  case 8:
   return initio_bus_device_reset(host);
  default:
   return initio_bad_seq(host);
  }
  if (next <= 0)
   return next;
 }
}


/**
 * initio_state_1 - SCSI state machine
 * @host: InitIO host we are controlling
 *
 * Perform SCSI state processing for Select/Attention/Stop
 */


static int initio_state_1(struct initio_host * host)
{
 struct scsi_ctrl_blk *scb = host->active;
 struct target_control *active_tc = host->active_tc;
#if DEBUG_STATE
 printk("-s1-");
#endif

 /* Move the SCB from pending to busy */
 initio_unlink_pend_scb(host, scb);
 initio_append_busy_scb(host, scb);

 outb(active_tc->sconfig0, host->addr + TUL_SConfig );
 /* ATN on */
 if (host->phase == MSG_OUT) {
  outb(TSC_EN_BUS_IN | TSC_HW_RESELECT, host->addr + TUL_SCtrl1);
  outb(scb->ident, host->addr + TUL_SFifo);

  if (scb->tagmsg) {
   outb(scb->tagmsg, host->addr + TUL_SFifo);
   outb(scb->tagid, host->addr + TUL_SFifo);
  }
  if ((active_tc->flags & (TCF_WDTR_DONE | TCF_NO_WDTR)) == 0) {
   active_tc->flags |= TCF_WDTR_DONE;
   outb(EXTENDED_MESSAGE, host->addr + TUL_SFifo);
   outb(2, host->addr + TUL_SFifo); /* Extended msg length */
   outb(EXTENDED_SDTR, host->addr + TUL_SFifo); /* Sync request */
   outb(1, host->addr + TUL_SFifo); /* Start from 16 bits */
  } else if ((active_tc->flags & (TCF_SYNC_DONE | TCF_NO_SYNC_NEGO)) == 0) {
   active_tc->flags |= TCF_SYNC_DONE;
   outb(EXTENDED_MESSAGE, host->addr + TUL_SFifo);
   outb(3, host->addr + TUL_SFifo); /* extended msg length */
   outb(EXTENDED_SDTR, host->addr + TUL_SFifo); /* sync request */
   outb(initio_rate_tbl[active_tc->flags & TCF_SCSI_RATE], host->addr + TUL_SFifo);
   outb(MAX_OFFSET, host->addr + TUL_SFifo); /* REQ/ACK offset */
  }
  outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd);
  if (wait_tulip(host) == -1)
   return -1;
 }
 outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0);
 outb((inb(host->addr + TUL_SSignal) & (TSC_SET_ACK | 7)), host->addr + TUL_SSignal);
 /* Into before CDB xfer */
 return 3;
}


/**
 * initio_state_2 - SCSI state machine
 * @host: InitIO host we are controlling
 *
 * state after selection with attention
 * state after selection with attention3
 */


static int initio_state_2(struct initio_host * host)
{
 struct scsi_ctrl_blk *scb = host->active;
 struct target_control *active_tc = host->active_tc;
#if DEBUG_STATE
 printk("-s2-");
#endif

 initio_unlink_pend_scb(host, scb);
 initio_append_busy_scb(host, scb);

 outb(active_tc->sconfig0, host->addr + TUL_SConfig);

 if (host->jsstatus1 & TSS_CMD_PH_CMP)
  return 4;

 outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0);
 outb((inb(host->addr + TUL_SSignal) & (TSC_SET_ACK | 7)), host->addr + TUL_SSignal);
 /* Into before CDB xfer */
 return 3;
}

/**
 * initio_state_3 - SCSI state machine
 * @host: InitIO host we are controlling
 *
 * state before CDB xfer is done
 */


static int initio_state_3(struct initio_host * host)
{
 struct scsi_ctrl_blk *scb = host->active;
 struct target_control *active_tc = host->active_tc;
 int i;

#if DEBUG_STATE
 printk("-s3-");
#endif
 for (;;) {
  switch (host->phase) {
  case CMD_OUT: /* Command out phase            */
   for (i = 0; i < (int) scb->cdblen; i++)
    outb(scb->cdb[i], host->addr + TUL_SFifo);
   outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd);
   if (wait_tulip(host) == -1)
    return -1;
   if (host->phase == CMD_OUT)
    return initio_bad_seq(host);
   return 4;

  case MSG_IN: /* Message in phase             */
   scb->next_state = 3;
   if (initio_msgin(host) == -1)
    return -1;
   break;

  case STATUS_IN: /* Status phase                 */
   if (initio_status_msg(host) == -1)
    return -1;
   break;

  case MSG_OUT: /* Message out phase            */
   if (active_tc->flags & (TCF_SYNC_DONE | TCF_NO_SYNC_NEGO)) {
    outb(NOP, host->addr + TUL_SFifo);  /* msg nop */
    outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd);
    if (wait_tulip(host) == -1)
     return -1;
   } else {
    active_tc->flags |= TCF_SYNC_DONE;

    outb(EXTENDED_MESSAGE, host->addr + TUL_SFifo);
    outb(3, host->addr + TUL_SFifo); /* ext. msg len */
    outb(EXTENDED_SDTR, host->addr + TUL_SFifo); /* sync request */
    outb(initio_rate_tbl[active_tc->flags & TCF_SCSI_RATE], host->addr + TUL_SFifo);
    outb(MAX_OFFSET, host->addr + TUL_SFifo); /* REQ/ACK offset */
    outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd);
    if (wait_tulip(host) == -1)
     return -1;
    outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0);
    outb(inb(host->addr + TUL_SSignal) & (TSC_SET_ACK | 7), host->addr + TUL_SSignal);

   }
   break;
  default:
   return initio_bad_seq(host);
  }
 }
}

/**
 * initio_state_4 - SCSI state machine
 * @host: InitIO host we are controlling
 *
 * SCSI state machine. State 4
 */


static int initio_state_4(struct initio_host * host)
{
 struct scsi_ctrl_blk *scb = host->active;

#if DEBUG_STATE
 printk("-s4-");
#endif
 if ((scb->flags & SCF_DIR) == SCF_NO_XF) {
  return 6; /* Go to state 6 (After data) */
 }
 for (;;) {
  if (scb->buflen == 0)
   return 6;

  switch (host->phase) {

  case STATUS_IN: /* Status phase                 */
   if ((scb->flags & SCF_DIR) != 0) /* if direction bit set then report data underrun */
    scb->hastat = HOST_DO_DU;
   if ((initio_status_msg(host)) == -1)
    return -1;
   break;

  case MSG_IN: /* Message in phase             */
   scb->next_state = 0x4;
   if (initio_msgin(host) == -1)
    return -1;
   break;

  case MSG_OUT: /* Message out phase            */
   if (host->jsstatus0 & TSS_PAR_ERROR) {
    scb->buflen = 0;
    scb->hastat = HOST_DO_DU;
    if (initio_msgout_ide(host) == -1)
     return -1;
    return 6;
   } else {
    outb(NOP, host->addr + TUL_SFifo);  /* msg nop */
    outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd);
    if (wait_tulip(host) == -1)
     return -1;
   }
   break;

  case DATA_IN: /* Data in phase                */
   return initio_xfer_data_in(host);

  case DATA_OUT: /* Data out phase               */
   return initio_xfer_data_out(host);

  default:
   return initio_bad_seq(host);
  }
 }
}


/**
 * initio_state_5 - SCSI state machine
 * @host: InitIO host we are controlling
 *
 * State after dma xfer done or phase change before xfer done
 */


static int initio_state_5(struct initio_host * host)
{
 struct scsi_ctrl_blk *scb = host->active;
 long cnt, xcnt;  /* cannot use unsigned !! code: if (xcnt < 0) */

#if DEBUG_STATE
 printk("-s5-");
#endif
 /*------ get remaining count -------*/
 cnt = inl(host->addr + TUL_SCnt0) & 0x0FFFFFF;

 if (inb(host->addr + TUL_XCmd) & 0x20) {
  /* ----------------------- DATA_IN ----------------------------- */
  /* check scsi parity error */
  if (host->jsstatus0 & TSS_PAR_ERROR)
   scb->hastat = HOST_DO_DU;
  if (inb(host->addr + TUL_XStatus) & XPEND) { /* DMA xfer pending, Send STOP  */
   /* tell Hardware  scsi xfer has been terminated */
   outb(inb(host->addr + TUL_XCtrl) | 0x80, host->addr + TUL_XCtrl);
   /* wait until DMA xfer not pending */
   while (inb(host->addr + TUL_XStatus) & XPEND)
    cpu_relax();
  }
 } else {
  /*-------- DATA OUT -----------*/
  if ((inb(host->addr + TUL_SStatus1) & TSS_XFER_CMP) == 0) {
   if (host->active_tc->js_period & TSC_WIDE_SCSI)
    cnt += (inb(host->addr + TUL_SFifoCnt) & 0x1F) << 1;
   else
    cnt += (inb(host->addr + TUL_SFifoCnt) & 0x1F);
  }
  if (inb(host->addr + TUL_XStatus) & XPEND) { /* if DMA xfer is pending, abort DMA xfer */
   outb(TAX_X_ABT, host->addr + TUL_XCmd);
   /* wait Abort DMA xfer done */
   while ((inb(host->addr + TUL_Int) & XABT) == 0)
    cpu_relax();
  }
  if ((cnt == 1) && (host->phase == DATA_OUT)) {
   outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd);
   if (wait_tulip(host) == -1)
    return -1;
   cnt = 0;
  } else {
   if ((inb(host->addr + TUL_SStatus1) & TSS_XFER_CMP) == 0)
    outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0);
  }
 }
 if (cnt == 0) {
  scb->buflen = 0;
  return 6; /* After Data */
 }
 /* Update active data pointer */
 xcnt = (long) scb->buflen - cnt; /* xcnt== bytes already xferred */
 scb->buflen = (u32) cnt;  /* cnt == bytes left to be xferred */
 if (scb->flags & SCF_SG) {
  struct sg_entry *sgp;
  unsigned long i;

  sgp = &scb->sglist[scb->sgidx];
  for (i = scb->sgidx; i < scb->sgmax; sgp++, i++) {
   xcnt -= (long) sgp->len;
   if (xcnt < 0) {  /* this sgp xfer half done */
    xcnt += (long) sgp->len; /* xcnt == bytes xferred in this sgp */
    sgp->data += (u32) xcnt; /* new ptr to be xfer */
    sgp->len -= (u32) xcnt; /* new len to be xfer */
    scb->bufptr += ((u32) (i - scb->sgidx) << 3);
    /* new SG table ptr */
    scb->sglen = (u8) (scb->sgmax - i);
    /* new SG table len */
    scb->sgidx = (u16) i;
    /* for next disc and come in this loop */
    return 4; /* Go to state 4                */
   }
   /* else (xcnt >= 0 , i.e. this sgp already xferred */
  }  /* for */
  return 6; /* Go to state 6                */
 } else {
  scb->bufptr += (u32) xcnt;
 }
 return 4;  /* Go to state 4                */
}

/**
 * initio_state_6 - SCSI state machine
 * @host: InitIO host we are controlling
 *
 * State after Data phase
 */


static int initio_state_6(struct initio_host * host)
{
 struct scsi_ctrl_blk *scb = host->active;

#if DEBUG_STATE
 printk("-s6-");
#endif
 for (;;) {
  switch (host->phase) {
  case STATUS_IN: /* Status phase                 */
   if ((initio_status_msg(host)) == -1)
    return -1;
   break;

  case MSG_IN: /* Message in phase             */
   scb->next_state = 6;
   if ((initio_msgin(host)) == -1)
    return -1;
   break;

  case MSG_OUT: /* Message out phase            */
   outb(NOP, host->addr + TUL_SFifo);  /* msg nop */
   outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd);
   if (wait_tulip(host) == -1)
    return -1;
   break;

  case DATA_IN: /* Data in phase                */
   return initio_xpad_in(host);

  case DATA_OUT: /* Data out phase               */
   return initio_xpad_out(host);

  default:
   return initio_bad_seq(host);
  }
 }
}

/**
 * initio_state_7 - SCSI state machine
 * @host: InitIO host we are controlling
 *
 */


static int initio_state_7(struct initio_host * host)
{
 int cnt, i;

#if DEBUG_STATE
 printk("-s7-");
#endif
 /* flush SCSI FIFO */
 cnt = inb(host->addr + TUL_SFifoCnt) & 0x1F;
 if (cnt) {
  for (i = 0; i < cnt; i++)
   inb(host->addr + TUL_SFifo);
 }
 switch (host->phase) {
 case DATA_IN:  /* Data in phase                */
 case DATA_OUT:  /* Data out phase               */
  return initio_bad_seq(host);
 default:
  return 6; /* Go to state 6                */
 }
}

/**
 * initio_xfer_data_in - Commence data input
 * @host: InitIO host in use
 *
 * Commence a block of data transfer. The transfer itself will
 * be managed by the controller and we will get a completion (or
 * failure) interrupt.
 */

static int initio_xfer_data_in(struct initio_host * host)
{
 struct scsi_ctrl_blk *scb = host->active;

 if ((scb->flags & SCF_DIR) == SCF_DOUT)
  return 6; /* wrong direction */

 outl(scb->buflen, host->addr + TUL_SCnt0);
 outb(TSC_XF_DMA_IN, host->addr + TUL_SCmd); /* 7/25/95 */

 if (scb->flags & SCF_SG) { /* S/G xfer */
  outl(((u32) scb->sglen) << 3, host->addr + TUL_XCntH);
  outl(scb->bufptr, host->addr + TUL_XAddH);
  outb(TAX_SG_IN, host->addr + TUL_XCmd);
 } else {
  outl(scb->buflen, host->addr + TUL_XCntH);
  outl(scb->bufptr, host->addr + TUL_XAddH);
  outb(TAX_X_IN, host->addr + TUL_XCmd);
 }
 scb->next_state = 0x5;
 return 0;  /* return to OS, wait xfer done , let jas_isr come in */
}

/**
 * initio_xfer_data_out - Commence data output
 * @host: InitIO host in use
 *
 * Commence a block of data transfer. The transfer itself will
 * be managed by the controller and we will get a completion (or
 * failure) interrupt.
 */


static int initio_xfer_data_out(struct initio_host * host)
{
 struct scsi_ctrl_blk *scb = host->active;

 if ((scb->flags & SCF_DIR) == SCF_DIN)
  return 6; /* wrong direction */

 outl(scb->buflen, host->addr + TUL_SCnt0);
 outb(TSC_XF_DMA_OUT, host->addr + TUL_SCmd);

 if (scb->flags & SCF_SG) { /* S/G xfer */
  outl(((u32) scb->sglen) << 3, host->addr + TUL_XCntH);
  outl(scb->bufptr, host->addr + TUL_XAddH);
  outb(TAX_SG_OUT, host->addr + TUL_XCmd);
 } else {
  outl(scb->buflen, host->addr + TUL_XCntH);
  outl(scb->bufptr, host->addr + TUL_XAddH);
  outb(TAX_X_OUT, host->addr + TUL_XCmd);
 }

 scb->next_state = 0x5;
 return 0;  /* return to OS, wait xfer done , let jas_isr come in */
}

int initio_xpad_in(struct initio_host * host)
{
 struct scsi_ctrl_blk *scb = host->active;
 struct target_control *active_tc = host->active_tc;

 if ((scb->flags & SCF_DIR) != SCF_NO_DCHK)
  scb->hastat = HOST_DO_DU; /* over run             */
 for (;;) {
  if (active_tc->js_period & TSC_WIDE_SCSI)
   outl(2, host->addr + TUL_SCnt0);
  else
   outl(1, host->addr + TUL_SCnt0);

  outb(TSC_XF_FIFO_IN, host->addr + TUL_SCmd);
  if (wait_tulip(host) == -1)
   return -1;
  if (host->phase != DATA_IN) {
   outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0);
   return 6;
  }
  inb(host->addr + TUL_SFifo);
 }
}

int initio_xpad_out(struct initio_host * host)
{
 struct scsi_ctrl_blk *scb = host->active;
 struct target_control *active_tc = host->active_tc;

 if ((scb->flags & SCF_DIR) != SCF_NO_DCHK)
  scb->hastat = HOST_DO_DU; /* over run             */
 for (;;) {
  if (active_tc->js_period & TSC_WIDE_SCSI)
   outl(2, host->addr + TUL_SCnt0);
  else
   outl(1, host->addr + TUL_SCnt0);

  outb(0, host->addr + TUL_SFifo);
  outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd);
  if ((wait_tulip(host)) == -1)
   return -1;
  if (host->phase != DATA_OUT) { /* Disable wide CPU to allow read 16 bits */
   outb(TSC_HW_RESELECT, host->addr + TUL_SCtrl1);
   outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0);
   return 6;
  }
 }
}

int initio_status_msg(struct initio_host * host)
{    /* status & MSG_IN */
 struct scsi_ctrl_blk *scb = host->active;
 u8 msg;

 outb(TSC_CMD_COMP, host->addr + TUL_SCmd);
 if (wait_tulip(host) == -1)
  return -1;

 /* get status */
 scb->tastat = inb(host->addr + TUL_SFifo);

 if (host->phase == MSG_OUT) {
  if (host->jsstatus0 & TSS_PAR_ERROR)
   outb(MSG_PARITY_ERROR, host->addr + TUL_SFifo);
  else
   outb(NOP, host->addr + TUL_SFifo);
  outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd);
  return wait_tulip(host);
 }
 if (host->phase == MSG_IN) {
  msg = inb(host->addr + TUL_SFifo);
  if (host->jsstatus0 & TSS_PAR_ERROR) { /* Parity error                 */
   if ((initio_msgin_accept(host)) == -1)
    return -1;
   if (host->phase != MSG_OUT)
    return initio_bad_seq(host);
   outb(MSG_PARITY_ERROR, host->addr + TUL_SFifo);
   outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd);
   return wait_tulip(host);
  }
  if (msg == 0) { /* Command complete             */

   if ((scb->tastat & 0x18) == 0x10) /* No link support              */
    return initio_bad_seq(host);
   outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0);
   outb(TSC_MSG_ACCEPT, host->addr + TUL_SCmd);
   return initio_wait_done_disc(host);

  }
  if (msg == LINKED_CMD_COMPLETE ||
      msg == LINKED_FLG_CMD_COMPLETE) {
   if ((scb->tastat & 0x18) == 0x10)
    return initio_msgin_accept(host);
  }
 }
 return initio_bad_seq(host);
}


/* scsi bus free */
int int_initio_busfree(struct initio_host * host)
{
 struct scsi_ctrl_blk *scb = host->active;

 if (scb != NULL) {
  if (scb->status & SCB_SELECT) {  /* selection timeout */
   initio_unlink_pend_scb(host, scb);
   scb->hastat = HOST_SEL_TOUT;
   initio_append_done_scb(host, scb);
  } else { /* Unexpected bus free          */
   initio_unlink_busy_scb(host, scb);
   scb->hastat = HOST_BUS_FREE;
   initio_append_done_scb(host, scb);
  }
  host->active = NULL;
  host->active_tc = NULL;
 }
 outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0);  /* Flush SCSI FIFO  */
 outb(TSC_INITDEFAULT, host->addr + TUL_SConfig);
 outb(TSC_HW_RESELECT, host->addr + TUL_SCtrl1); /* Enable HW reselect       */
 return -1;
}


/**
 * int_initio_scsi_rst - SCSI reset occurred
 * @host: Host seeing the reset
 *
 * A SCSI bus reset has occurred. Clean up any pending transfer
 * the hardware is doing by DMA and then abort all active and
 * disconnected commands. The mid layer should sort the rest out
 * for us
 */


static int int_initio_scsi_rst(struct initio_host * host)
{
 struct scsi_ctrl_blk *scb;
 int i;

 /* if DMA xfer is pending, abort DMA xfer */
 if (inb(host->addr + TUL_XStatus) & 0x01) {
  outb(TAX_X_ABT | TAX_X_CLR_FIFO, host->addr + TUL_XCmd);
  /* wait Abort DMA xfer done */
  while ((inb(host->addr + TUL_Int) & 0x04) == 0)
   cpu_relax();
  outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0);
 }
 /* Abort all active & disconnected scb */
 while ((scb = initio_pop_busy_scb(host)) != NULL) {
  scb->hastat = HOST_BAD_PHAS;
  initio_append_done_scb(host, scb);
 }
 host->active = NULL;
 host->active_tc = NULL;

 /* clr sync nego. done flag */
 for (i = 0; i < host->max_tar; i++)
  host->targets[i].flags &= ~(TCF_SYNC_DONE | TCF_WDTR_DONE);
 return -1;
}

/**
 * int_initio_resel - Reselection occurred
 * @host: InitIO host adapter
 *
 * A SCSI reselection event has been signalled and the interrupt
 * is now being processed. Work out which command block needs attention
 * and continue processing that command.
 */


int int_initio_resel(struct initio_host * host)
{
 struct scsi_ctrl_blk *scb;
 struct target_control *active_tc;
 u8 tag, msg = 0;
 u8 tar, lun;

 if ((scb = host->active) != NULL) {
  /* FIXME: Why check and not just clear ? */
  if (scb->status & SCB_SELECT)  /* if waiting for selection complete */
   scb->status &= ~SCB_SELECT;
  host->active = NULL;
 }
 /* --------- get target id---------------------- */
 tar = inb(host->addr + TUL_SBusId);
 /* ------ get LUN from Identify message----------- */
 lun = inb(host->addr + TUL_SIdent) & 0x0F;
 /* 07/22/98 from 0x1F -> 0x0F */
 active_tc = &host->targets[tar];
 host->active_tc = active_tc;
 outb(active_tc->sconfig0, host->addr + TUL_SConfig);
 outb(active_tc->js_period, host->addr + TUL_SPeriod);

 /* ------------- tag queueing ? ------------------- */
 if (active_tc->drv_flags & TCF_DRV_EN_TAG) {
  if ((initio_msgin_accept(host)) == -1)
   return -1;
  if (host->phase != MSG_IN)
   goto no_tag;
  outl(1, host->addr + TUL_SCnt0);
  outb(TSC_XF_FIFO_IN, host->addr + TUL_SCmd);
  if (wait_tulip(host) == -1)
   return -1;
  msg = inb(host->addr + TUL_SFifo); /* Read Tag Message    */

  if (msg < SIMPLE_QUEUE_TAG || msg > ORDERED_QUEUE_TAG)
   /* Is simple Tag      */
   goto no_tag;

  if (initio_msgin_accept(host) == -1)
   return -1;

  if (host->phase != MSG_IN)
   goto no_tag;

  outl(1, host->addr + TUL_SCnt0);
  outb(TSC_XF_FIFO_IN, host->addr + TUL_SCmd);
  if (wait_tulip(host) == -1)
   return -1;
  tag = inb(host->addr + TUL_SFifo); /* Read Tag ID       */
  scb = host->scb + tag;
  if (scb->target != tar || scb->lun != lun) {
   return initio_msgout_abort_tag(host);
  }
  if (scb->status != SCB_BUSY) { /* 03/24/95             */
   return initio_msgout_abort_tag(host);
  }
  host->active = scb;
  if ((initio_msgin_accept(host)) == -1)
   return -1;
 } else {  /* No tag               */
       no_tag:
  if ((scb = initio_find_busy_scb(host, tar | (lun << 8))) == NULL) {
   return initio_msgout_abort_targ(host);
  }
  host->active = scb;
  if (!(active_tc->drv_flags & TCF_DRV_EN_TAG)) {
   if ((initio_msgin_accept(host)) == -1)
    return -1;
  }
 }
 return 0;
}

/**
 * int_initio_bad_seq - out of phase
 * @host: InitIO host flagging event
 *
 * We have ended up out of phase somehow. Reset the host controller
 * and throw all our toys out of the pram. Let the midlayer clean up
 */


static int int_initio_bad_seq(struct initio_host * host)
{    /* target wrong phase           */
 struct scsi_ctrl_blk *scb;
 int i;

 initio_reset_scsi(host, 10);

 while ((scb = initio_pop_busy_scb(host)) != NULL) {
  scb->hastat = HOST_BAD_PHAS;
  initio_append_done_scb(host, scb);
 }
 for (i = 0; i < host->max_tar; i++)
  host->targets[i].flags &= ~(TCF_SYNC_DONE | TCF_WDTR_DONE);
 return -1;
}


/**
 * initio_msgout_abort_targ - abort a tag
 * @host: InitIO host
 *
 * Abort when the target/lun does not match or when our SCB is not
 * busy. Used by untagged commands.
 */


static int initio_msgout_abort_targ(struct initio_host * host)
{

 outb(((inb(host->addr + TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN), host->addr + TUL_SSignal);
 if (initio_msgin_accept(host) == -1)
  return -1;
 if (host->phase != MSG_OUT)
  return initio_bad_seq(host);

 outb(ABORT_TASK_SET, host->addr + TUL_SFifo);
 outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd);

 return initio_wait_disc(host);
}

/**
 * initio_msgout_abort_tag - abort a tag
 * @host: InitIO host
 *
 * Abort when the target/lun does not match or when our SCB is not
 * busy. Used for tagged commands.
 */


static int initio_msgout_abort_tag(struct initio_host * host)
{

 outb(((inb(host->addr + TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN), host->addr + TUL_SSignal);
 if (initio_msgin_accept(host) == -1)
  return -1;
 if (host->phase != MSG_OUT)
  return initio_bad_seq(host);

 outb(ABORT_TASK, host->addr + TUL_SFifo);
 outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd);

 return initio_wait_disc(host);

}

/**
 * initio_msgin - Message in
 * @host: InitIO Host
 *
 * Process incoming message
 */

static int initio_msgin(struct initio_host * host)
{
 struct target_control *active_tc;

 for (;;) {
  outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0);

  outl(1, host->addr + TUL_SCnt0);
  outb(TSC_XF_FIFO_IN, host->addr + TUL_SCmd);
  if (wait_tulip(host) == -1)
   return -1;

  switch (inb(host->addr + TUL_SFifo)) {
  case DISCONNECT: /* Disconnect msg */
   outb(TSC_MSG_ACCEPT, host->addr + TUL_SCmd);
   return initio_wait_disc(host);
  case SAVE_POINTERS:
  case RESTORE_POINTERS:
  case NOP:
   initio_msgin_accept(host);
   break;
  case MESSAGE_REJECT: /* Clear ATN first              */
   outb((inb(host->addr + TUL_SSignal) & (TSC_SET_ACK | 7)),
    host->addr + TUL_SSignal);
   active_tc = host->active_tc;
   if ((active_tc->flags & (TCF_SYNC_DONE | TCF_NO_SYNC_NEGO)) == 0) /* do sync nego */
    outb(((inb(host->addr + TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN),
     host->addr + TUL_SSignal);
   initio_msgin_accept(host);
   break;
  case EXTENDED_MESSAGE: /* extended msg */
   initio_msgin_extend(host);
   break;
  case IGNORE_WIDE_RESIDUE:
   initio_msgin_accept(host);
   break;
  case COMMAND_COMPLETE:
   outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0);
   outb(TSC_MSG_ACCEPT, host->addr + TUL_SCmd);
   return initio_wait_done_disc(host);
  default:
   initio_msgout_reject(host);
   break;
  }
  if (host->phase != MSG_IN)
   return host->phase;
 }
 /* statement won't reach here */
}

static int initio_msgout_reject(struct initio_host * host)
{
 outb(((inb(host->addr + TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN), host->addr + TUL_SSignal);

 if (initio_msgin_accept(host) == -1)
  return -1;

 if (host->phase == MSG_OUT) {
  outb(MESSAGE_REJECT, host->addr + TUL_SFifo);  /* Msg reject           */
  outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd);
  return wait_tulip(host);
 }
 return host->phase;
}

static int initio_msgout_ide(struct initio_host * host)
{
 outb(INITIATOR_ERROR, host->addr + TUL_SFifo);  /* Initiator Detected Error */
 outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd);
 return wait_tulip(host);
}

static int initio_msgin_extend(struct initio_host * host)
{
 u8 len, idx;

 if (initio_msgin_accept(host) != MSG_IN)
  return host->phase;

 /* Get extended msg length      */
 outl(1, host->addr + TUL_SCnt0);
 outb(TSC_XF_FIFO_IN, host->addr + TUL_SCmd);
 if (wait_tulip(host) == -1)
  return -1;

 len = inb(host->addr + TUL_SFifo);
 host->msg[0] = len;
 for (idx = 1; len != 0; len--) {

  if ((initio_msgin_accept(host)) != MSG_IN)
   return host->phase;
  outl(1, host->addr + TUL_SCnt0);
  outb(TSC_XF_FIFO_IN, host->addr + TUL_SCmd);
  if (wait_tulip(host) == -1)
   return -1;
  host->msg[idx++] = inb(host->addr + TUL_SFifo);
 }
 if (host->msg[1] == 1) {  /* if it's synchronous data transfer request */
  u8 r;
  if (host->msg[0] != 3) /* if length is not right */
   return initio_msgout_reject(host);
  if (host->active_tc->flags & TCF_NO_SYNC_NEGO) { /* Set OFFSET=0 to do async, nego back */
   host->msg[3] = 0;
  } else {
   if (initio_msgin_sync(host) == 0 &&
       (host->active_tc->flags & TCF_SYNC_DONE)) {
    initio_sync_done(host);
    return initio_msgin_accept(host);
   }
  }

  r = inb(host->addr + TUL_SSignal);
  outb((r & (TSC_SET_ACK | 7)) | TSC_SET_ATN,
   host->addr + TUL_SSignal);
  if (initio_msgin_accept(host) != MSG_OUT)
   return host->phase;
  /* sync msg out */
  outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0);

  initio_sync_done(host);

  outb(EXTENDED_MESSAGE, host->addr + TUL_SFifo);
  outb(3, host->addr + TUL_SFifo);
  outb(EXTENDED_SDTR, host->addr + TUL_SFifo);
  outb(host->msg[2], host->addr + TUL_SFifo);
  outb(host->msg[3], host->addr + TUL_SFifo);
  outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd);
  return wait_tulip(host);
 }
 if (host->msg[0] != 2 || host->msg[1] != 3)
  return initio_msgout_reject(host);
 /* if it's WIDE DATA XFER REQ   */
 if (host->active_tc->flags & TCF_NO_WDTR) {
  host->msg[2] = 0;
 } else {
  if (host->msg[2] > 2) /* > 32 bits            */
   return initio_msgout_reject(host);
  if (host->msg[2] == 2) {  /* == 32                */
   host->msg[2] = 1;
  } else {
   if ((host->active_tc->flags & TCF_NO_WDTR) == 0) {
    wdtr_done(host);
    if ((host->active_tc->flags & (TCF_SYNC_DONE | TCF_NO_SYNC_NEGO)) == 0)
     outb(((inb(host->addr + TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN), host->addr + TUL_SSignal);
    return initio_msgin_accept(host);
   }
  }
 }
 outb(((inb(host->addr + TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN), host->addr + TUL_SSignal);

 if (initio_msgin_accept(host) != MSG_OUT)
  return host->phase;
 /* WDTR msg out                 */
 outb(EXTENDED_MESSAGE, host->addr + TUL_SFifo);
 outb(2, host->addr + TUL_SFifo);
 outb(EXTENDED_WDTR, host->addr + TUL_SFifo);
 outb(host->msg[2], host->addr + TUL_SFifo);
 outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd);
 return wait_tulip(host);
}

static int initio_msgin_sync(struct initio_host * host)
{
 char default_period;

 default_period = initio_rate_tbl[host->active_tc->flags & TCF_SCSI_RATE];
 if (host->msg[3] > MAX_OFFSET) {
  host->msg[3] = MAX_OFFSET;
  if (host->msg[2] < default_period) {
   host->msg[2] = default_period;
   return 1;
  }
  if (host->msg[2] >= 59) /* Change to async              */
   host->msg[3] = 0;
  return 1;
 }
 /* offset requests asynchronous transfers ? */
 if (host->msg[3] == 0) {
  return 0;
 }
 if (host->msg[2] < default_period) {
  host->msg[2] = default_period;
  return 1;
 }
 if (host->msg[2] >= 59) {
  host->msg[3] = 0;
  return 1;
 }
 return 0;
}

static int wdtr_done(struct initio_host * host)
{
 host->active_tc->flags &= ~TCF_SYNC_DONE;
 host->active_tc->flags |= TCF_WDTR_DONE;

 host->active_tc->js_period = 0;
 if (host->msg[2]) /* if 16 bit */
  host->active_tc->js_period |= TSC_WIDE_SCSI;
 host->active_tc->sconfig0 &= ~TSC_ALT_PERIOD;
 outb(host->active_tc->sconfig0, host->addr + TUL_SConfig);
 outb(host->active_tc->js_period, host->addr + TUL_SPeriod);

 return 1;
}

static int initio_sync_done(struct initio_host * host)
{
 int i;

 host->active_tc->flags |= TCF_SYNC_DONE;

 if (host->msg[3]) {
  host->active_tc->js_period |= host->msg[3];
  for (i = 0; i < 8; i++) {
   if (initio_rate_tbl[i] >= host->msg[2]) /* pick the big one */
    break;
  }
  host->active_tc->js_period |= (i << 4);
  host->active_tc->sconfig0 |= TSC_ALT_PERIOD;
 }
 outb(host->active_tc->sconfig0, host->addr + TUL_SConfig);
 outb(host->active_tc->js_period, host->addr + TUL_SPeriod);

 return -1;
}


static int initio_post_scsi_rst(struct initio_host * host)
{
 struct scsi_ctrl_blk *scb;
 struct target_control *active_tc;
 int i;

 host->active = NULL;
 host->active_tc = NULL;
 host->flags = 0;

 while ((scb = initio_pop_busy_scb(host)) != NULL) {
  scb->hastat = HOST_BAD_PHAS;
  initio_append_done_scb(host, scb);
 }
 /* clear sync done flag         */
 active_tc = &host->targets[0];
 for (i = 0; i < host->max_tar; active_tc++, i++) {
  active_tc->flags &= ~(TCF_SYNC_DONE | TCF_WDTR_DONE);
  /* Initialize the sync. xfer register values to an asyn xfer */
  active_tc->js_period = 0;
  active_tc->sconfig0 = host->sconf1;
  host->act_tags[0] = 0; /* 07/22/98 */
  host->targets[i].flags &= ~TCF_BUSY; /* 07/22/98 */
 }   /* for */

 return -1;
}

static void initio_select_atn_stop(struct initio_host * host, struct scsi_ctrl_blk * scb)
{
 scb->status |= SCB_SELECT;
 scb->next_state = 0x1;
 host->active = scb;
 host->active_tc = &host->targets[scb->target];
 outb(TSC_SELATNSTOP, host->addr + TUL_SCmd);
}


static void initio_select_atn(struct initio_host * host, struct scsi_ctrl_blk * scb)
{
 int i;

 scb->status |= SCB_SELECT;
 scb->next_state = 0x2;

 outb(scb->ident, host->addr + TUL_SFifo);
 for (i = 0; i < (int) scb->cdblen; i++)
  outb(scb->cdb[i], host->addr + TUL_SFifo);
 host->active_tc = &host->targets[scb->target];
 host->active = scb;
 outb(TSC_SEL_ATN, host->addr + TUL_SCmd);
}

static void initio_select_atn3(struct initio_host * host, struct scsi_ctrl_blk * scb)
{
 int i;

 scb->status |= SCB_SELECT;
 scb->next_state = 0x2;

 outb(scb->ident, host->addr + TUL_SFifo);
 outb(scb->tagmsg, host->addr + TUL_SFifo);
 outb(scb->tagid, host->addr + TUL_SFifo);
 for (i = 0; i < scb->cdblen; i++)
  outb(scb->cdb[i], host->addr + TUL_SFifo);
 host->active_tc = &host->targets[scb->target];
 host->active = scb;
 outb(TSC_SEL_ATN3, host->addr + TUL_SCmd);
}

/**
 * initio_bus_device_reset -  SCSI Bus Device Reset
 * @host: InitIO host to reset
 *
 * Perform a device reset and abort all pending SCBs for the
 * victim device
 */

int initio_bus_device_reset(struct initio_host * host)
{
 struct scsi_ctrl_blk *scb = host->active;
 struct target_control *active_tc = host->active_tc;
 struct scsi_ctrl_blk *tmp, *prev;
 u8 tar;

 if (host->phase != MSG_OUT)
  return int_initio_bad_seq(host); /* Unexpected phase */

 initio_unlink_pend_scb(host, scb);
 initio_release_scb(host, scb);


 tar = scb->target; /* target                       */
 active_tc->flags &= ~(TCF_SYNC_DONE | TCF_WDTR_DONE | TCF_BUSY);
 /* clr sync. nego & WDTR flags  07/22/98 */

 /* abort all SCB with same target */
 prev = tmp = host->first_busy; /* Check Busy queue */
 while (tmp != NULL) {
  if (tmp->target == tar) {
   /* unlink it */
   if (tmp == host->first_busy) {
    if ((host->first_busy = tmp->next) == NULL)
     host->last_busy = NULL;
   } else {
    prev->next = tmp->next;
    if (tmp == host->last_busy)
     host->last_busy = prev;
   }
   tmp->hastat = HOST_ABORTED;
   initio_append_done_scb(host, tmp);
  }
  /* Previous haven't change      */
  else {
   prev = tmp;
  }
  tmp = tmp->next;
 }
 outb(TARGET_RESET, host->addr + TUL_SFifo);
 outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd);
 return initio_wait_disc(host);

}

static int initio_msgin_accept(struct initio_host * host)
{
 outb(TSC_MSG_ACCEPT, host->addr + TUL_SCmd);
 return wait_tulip(host);
}

static int wait_tulip(struct initio_host * host)
{

 while (!((host->jsstatus0 = inb(host->addr + TUL_SStatus0))
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=95 H=91 G=92

¤ Dauer der Verarbeitung: 0.22 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.