patch-2.1.128 linux/drivers/scsi/fd_mcs.c
Next file: linux/drivers/scsi/fd_mcs.h
Previous file: linux/drivers/scsi/fastlane.c
Back to the patch index
Back to the overall index
- Lines: 1482
- Date:
Sun Nov 8 13:50:47 1998
- Orig file:
v2.1.127/linux/drivers/scsi/fd_mcs.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.1.127/linux/drivers/scsi/fd_mcs.c linux/drivers/scsi/fd_mcs.c
@@ -0,0 +1,1481 @@
+/* fd_mcs.c -- Future Domain MCS 600/700 (or IBM OEM) driver
+ *
+ * FutureDomain MCS-600/700 v0.2 03/11/1998 by ZP Gu (zpg@castle.net)
+ *
+ * This driver is cloned from fdomain.* to specifically support
+ * the Future Domain MCS 600/700 MCA SCSI adapters. Some PS/2s
+ * also equipped with IBM Fast SCSI Adapter/A which is an OEM
+ * of MCS 700.
+ *
+ * This driver also supports Reply SB16/SCSI card (the SCSI part).
+ *
+ * What makes this driver different is that this driver is MCA only
+ * and it supports multiple adapters in the same system, IRQ
+ * sharing, some driver statistics, and maps highest SCSI id to sda.
+ * All cards are auto-detected.
+ *
+ * Assumptions: TMC-1800/18C50/18C30, BIOS >= 3.4
+ *
+ * LILO command-line options:
+ * fd_mcs=<FIFO_COUNT>[,<FIFO_SIZE>]
+ *
+ * ********************************************************
+ * Please see Copyrights/Comments in fdomain.* for credits.
+ * Following is from fdomain.c for acknowledgement:
+ *
+ * Created: Sun May 3 18:53:19 1992 by faith@cs.unc.edu
+ * Revised: Wed Oct 2 11:10:55 1996 by r.faith@ieee.org
+ * Author: Rickard E. Faith, faith@cs.unc.edu
+ * Copyright 1992, 1993, 1994, 1995, 1996 Rickard E. Faith
+ *
+ * $Id: fdomain.c,v 5.45 1996/10/02 15:13:06 root Exp $
+
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ **************************************************************************
+
+ NOTES ON USER DEFINABLE OPTIONS:
+
+ DEBUG: This turns on the printing of various debug information.
+
+ ENABLE_PARITY: This turns on SCSI parity checking. With the current
+ driver, all attached devices must support SCSI parity. If none of your
+ devices support parity, then you can probably get the driver to work by
+ turning this option off. I have no way of testing this, however, and it
+ would appear that no one ever uses this option.
+
+ FIFO_COUNT: The host adapter has an 8K cache (host adapters based on the
+ 18C30 chip have a 2k cache). When this many 512 byte blocks are filled by
+ the SCSI device, an interrupt will be raised. Therefore, this could be as
+ low as 0, or as high as 16. Note, however, that values which are too high
+ or too low seem to prevent any interrupts from occurring, and thereby lock
+ up the machine. I have found that 2 is a good number, but throughput may
+ be increased by changing this value to values which are close to 2.
+ Please let me know if you try any different values.
+ [*****Now a runtime option*****]
+
+ RESELECTION: This is no longer an option, since I gave up trying to
+ implement it in version 4.x of this driver. It did not improve
+ performance at all and made the driver unstable (because I never found one
+ of the two race conditions which were introduced by the multiple
+ outstanding command code). The instability seems a very high price to pay
+ just so that you don't have to wait for the tape to rewind. If you want
+ this feature implemented, send me patches. I'll be happy to send a copy
+ of my (broken) driver to anyone who would like to see a copy.
+
+ **************************************************************************/
+
+#ifdef MODULE
+#include <linux/module.h>
+#endif
+
+#include <linux/sched.h>
+#include <linux/blk.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/proc_fs.h>
+#include <linux/delay.h>
+#include <linux/config.h>
+#include <linux/mca.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/spinlock.h>
+
+#include "scsi.h"
+#include "hosts.h"
+#include "fd_mcs.h"
+
+#define DRIVER_VERSION "v0.2 by ZP Gu<zpg@castle.net>"
+
+struct proc_dir_entry proc_scsi_fd_mcs = {
+ PROC_SCSI_FD_MCS, 6, "fd_mcs",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+/* START OF USER DEFINABLE OPTIONS */
+
+#define DEBUG 0 /* Enable debugging output */
+#define ENABLE_PARITY 1 /* Enable SCSI Parity */
+#define DO_DETECT 0 /* Do device detection here (see scsi.c) */
+
+/* END OF USER DEFINABLE OPTIONS */
+
+#if DEBUG
+#define EVERY_ACCESS 0 /* Write a line on every scsi access */
+#define ERRORS_ONLY 1 /* Only write a line if there is an error */
+#define DEBUG_DETECT 1 /* Debug fd_mcs_detect() */
+#define DEBUG_MESSAGES 1 /* Debug MESSAGE IN phase */
+#define DEBUG_ABORT 1 /* Debug abort() routine */
+#define DEBUG_RESET 1 /* Debug reset() routine */
+#define DEBUG_RACE 1 /* Debug interrupt-driven race condition */
+#else
+#define EVERY_ACCESS 0 /* LEAVE THESE ALONE--CHANGE THE ONES ABOVE */
+#define ERRORS_ONLY 0
+#define DEBUG_DETECT 0
+#define DEBUG_MESSAGES 0
+#define DEBUG_ABORT 0
+#define DEBUG_RESET 0
+#define DEBUG_RACE 0
+#endif
+
+/* Errors are reported on the line, so we don't need to report them again */
+#if EVERY_ACCESS
+#undef ERRORS_ONLY
+#define ERRORS_ONLY 0
+#endif
+
+#if ENABLE_PARITY
+#define PARITY_MASK 0x08
+#else
+#define PARITY_MASK 0x00
+#endif
+
+enum chip_type {
+ unknown = 0x00,
+ tmc1800 = 0x01,
+ tmc18c50 = 0x02,
+ tmc18c30 = 0x03,
+};
+
+enum {
+ in_arbitration = 0x02,
+ in_selection = 0x04,
+ in_other = 0x08,
+ disconnect = 0x10,
+ aborted = 0x20,
+ sent_ident = 0x40,
+};
+
+enum in_port_type {
+ Read_SCSI_Data = 0,
+ SCSI_Status = 1,
+ TMC_Status = 2,
+ FIFO_Status = 3, /* tmc18c50/tmc18c30 only */
+ Interrupt_Cond = 4, /* tmc18c50/tmc18c30 only */
+ LSB_ID_Code = 5,
+ MSB_ID_Code = 6,
+ Read_Loopback = 7,
+ SCSI_Data_NoACK = 8,
+ Interrupt_Status = 9,
+ Configuration1 = 10,
+ Configuration2 = 11, /* tmc18c50/tmc18c30 only */
+ Read_FIFO = 12,
+ FIFO_Data_Count = 14
+};
+
+enum out_port_type {
+ Write_SCSI_Data = 0,
+ SCSI_Cntl = 1,
+ Interrupt_Cntl = 2,
+ SCSI_Mode_Cntl = 3,
+ TMC_Cntl = 4,
+ Memory_Cntl = 5, /* tmc18c50/tmc18c30 only */
+ Write_Loopback = 7,
+ IO_Control = 11, /* tmc18c30 only */
+ Write_FIFO = 12
+};
+
+struct fd_hostdata {
+ unsigned long _bios_base;
+ int _bios_major;
+ int _bios_minor;
+ volatile int _in_command;
+ Scsi_Cmnd *_current_SC;
+ enum chip_type _chip;
+ int _adapter_mask;
+ int _fifo_count; /* Number of 512 byte blocks before INTR */
+
+ char _adapter_name[64];
+#if DEBUG_RACE
+ volatile int _in_interrupt_flag;
+#endif
+
+ int _SCSI_Mode_Cntl_port;
+ int _FIFO_Data_Count_port;
+ int _Interrupt_Cntl_port;
+ int _Interrupt_Status_port;
+ int _Interrupt_Cond_port;
+ int _Read_FIFO_port;
+ int _Read_SCSI_Data_port;
+ int _SCSI_Cntl_port;
+ int _SCSI_Data_NoACK_port;
+ int _SCSI_Status_port;
+ int _TMC_Cntl_port;
+ int _TMC_Status_port;
+ int _Write_FIFO_port;
+ int _Write_SCSI_Data_port;
+
+ int _FIFO_Size; /* = 0x2000; 8k FIFO for
+ pre-tmc18c30 chips */
+ /* simple stats */
+ int _Bytes_Read;
+ int _Bytes_Written;
+ int _INTR_Processed;
+};
+
+#define FD_MAX_HOSTS 3 /* enough? */
+
+#define HOSTDATA(shpnt) ((struct fd_hostdata *) shpnt->hostdata)
+#define bios_base (HOSTDATA(shpnt)->_bios_base)
+#define bios_major (HOSTDATA(shpnt)->_bios_major)
+#define bios_minor (HOSTDATA(shpnt)->_bios_minor)
+#define in_command (HOSTDATA(shpnt)->_in_command)
+#define current_SC (HOSTDATA(shpnt)->_current_SC)
+#define chip (HOSTDATA(shpnt)->_chip)
+#define adapter_mask (HOSTDATA(shpnt)->_adapter_mask)
+#define FIFO_COUNT (HOSTDATA(shpnt)->_fifo_count)
+#define adapter_name (HOSTDATA(shpnt)->_adapter_name)
+#if DEBUG_RACE
+#define in_interrupt_flag (HOSTDATA(shpnt)->_in_interrupt_flag)
+#endif
+#define SCSI_Mode_Cntl_port (HOSTDATA(shpnt)->_SCSI_Mode_Cntl_port)
+#define FIFO_Data_Count_port (HOSTDATA(shpnt)->_FIFO_Data_Count_port)
+#define Interrupt_Cntl_port (HOSTDATA(shpnt)->_Interrupt_Cntl_port)
+#define Interrupt_Status_port (HOSTDATA(shpnt)->_Interrupt_Status_port)
+#define Interrupt_Cond_port (HOSTDATA(shpnt)->_Interrupt_Cond_port)
+#define Read_FIFO_port (HOSTDATA(shpnt)->_Read_FIFO_port)
+#define Read_SCSI_Data_port (HOSTDATA(shpnt)->_Read_SCSI_Data_port)
+#define SCSI_Cntl_port (HOSTDATA(shpnt)->_SCSI_Cntl_port)
+#define SCSI_Data_NoACK_port (HOSTDATA(shpnt)->_SCSI_Data_NoACK_port)
+#define SCSI_Status_port (HOSTDATA(shpnt)->_SCSI_Status_port)
+#define TMC_Cntl_port (HOSTDATA(shpnt)->_TMC_Cntl_port)
+#define TMC_Status_port (HOSTDATA(shpnt)->_TMC_Status_port)
+#define Write_FIFO_port (HOSTDATA(shpnt)->_Write_FIFO_port)
+#define Write_SCSI_Data_port (HOSTDATA(shpnt)->_Write_SCSI_Data_port)
+#define FIFO_Size (HOSTDATA(shpnt)->_FIFO_Size)
+#define Bytes_Read (HOSTDATA(shpnt)->_Bytes_Read)
+#define Bytes_Written (HOSTDATA(shpnt)->_Bytes_Written)
+#define INTR_Processed (HOSTDATA(shpnt)->_INTR_Processed)
+
+struct fd_mcs_adapters_struct {
+ char* name;
+ int id;
+ enum chip_type fd_chip;
+ int fifo_size;
+ int fifo_count;
+};
+
+#define REPLY_ID 0x5137
+
+static struct fd_mcs_adapters_struct fd_mcs_adapters[] = {
+ { "Future Domain SCSI Adapter MCS-700(18C50)",
+ 0x60e9,
+ tmc18c50,
+ 0x2000,
+ 4 },
+ { "Future Domain SCSI Adapter MCS-600/700(TMC-1800)",
+ 0x6127,
+ tmc1800,
+ 0x2000,
+ 4 },
+ { "Reply Sound Blaster/SCSI Adapter",
+ REPLY_ID,
+ tmc18c30,
+ 0x800,
+ 2 },
+};
+
+#define FD_BRDS sizeof(fd_mcs_adapters)/sizeof(struct fd_mcs_adapters_struct)
+
+static void fd_mcs_intr( int irq, void *dev_id, struct pt_regs * regs );
+
+static unsigned long addresses[] = {0xc8000, 0xca000, 0xce000, 0xde000};
+static unsigned short ports[] = { 0x140, 0x150, 0x160, 0x170 };
+static unsigned short ints[] = { 3, 5, 10, 11, 12, 14, 15, 0 };
+
+/* host information */
+static int found = 0;
+static struct Scsi_Host *hosts[FD_MAX_HOSTS+1] = { NULL };
+
+static int user_fifo_count = 0;
+static int user_fifo_size = 0;
+
+void fd_mcs_setup( char *str, int *ints )
+{
+ static int done_setup = 0;
+
+ if (done_setup++ || ints[0] < 1 || ints[0] > 2 ||
+ ints[1] < 1 || ints[1] > 16) {
+ printk( "fd_mcs: usage: fd_mcs=FIFO_COUNT, FIFO_SIZE\n" );
+ }
+
+ user_fifo_count = ints[0] >= 1 ? ints[1] : 0;
+ user_fifo_size = ints[0] >= 2 ? ints[2] : 0;
+}
+
+static void print_banner( struct Scsi_Host *shpnt )
+{
+ printk( "scsi%d <fd_mcs>: ", shpnt->host_no);
+
+ if (bios_base) {
+ printk( "BIOS at 0x%lX", bios_base);
+ } else {
+ printk( "No BIOS");
+ }
+
+ printk( ", HostID %d, %s Chip, IRQ %d, IO 0x%lX\n",
+ shpnt->this_id,
+ chip == tmc18c50 ? "TMC-18C50"
+ : (chip == tmc18c30 ? "TMC-18C30" :
+ (chip == tmc1800 ? "TMC-1800" : "Unknown")),
+ shpnt->irq,
+ shpnt->io_port );
+}
+
+
+static void do_pause( unsigned amount ) /* Pause for amount*10 milliseconds */
+{
+ do {
+ udelay(10*1000);
+ } while (--amount);
+}
+
+inline static void fd_mcs_make_bus_idle( struct Scsi_Host *shpnt )
+{
+ outb( 0, SCSI_Cntl_port );
+ outb( 0, SCSI_Mode_Cntl_port );
+ if (chip == tmc18c50 || chip == tmc18c30)
+ outb( 0x21 | PARITY_MASK, TMC_Cntl_port ); /* Clear forced intr. */
+ else
+ outb( 0x01 | PARITY_MASK, TMC_Cntl_port );
+}
+
+int fd_mcs_detect( Scsi_Host_Template *tpnt )
+{
+ int loop;
+ struct Scsi_Host *shpnt;
+
+ /* get id, port, bios, irq */
+ int slot;
+ u_char pos2, pos3, pos4;
+ int id, port, irq;
+ unsigned long bios;
+
+ /* if not MCA machine, return */
+ if (!MCA_bus)
+ return 0;
+
+ /* changeable? */
+ id = 7;
+
+ for( loop = 0; loop < FD_BRDS; loop++ ) {
+ slot = 0;
+ while ( MCA_NOTFOUND !=
+ (slot = mca_find_adapter(fd_mcs_adapters[loop].id,
+ slot)) ) {
+
+ /* if we get this far, an adapter has been detected and is
+ enabled */
+
+ printk("scsi <fd_mcs>: %s at slot %d\n",
+ fd_mcs_adapters[loop].name, slot + 1 );
+
+ pos2 = mca_read_stored_pos( slot, 2 );
+ pos3 = mca_read_stored_pos( slot, 3 );
+ pos4 = mca_read_stored_pos( slot, 4);
+
+ /* ready for next probe */
+ slot++;
+
+ if (fd_mcs_adapters[loop].id == REPLY_ID) { /* reply card */
+ static int reply_irq[] = {10, 11, 14, 15};
+
+ bios = 0; /* no bios */
+
+ if (pos2 & 0x2)
+ port = ports[pos4 & 0x3];
+ else
+ continue;
+
+ /* can't really disable it, same as irq=10 */
+ irq = reply_irq[((pos4 >> 2) & 0x1) + 2*((pos4 >> 4) & 0x1)];
+ } else {
+ bios = addresses[pos2 >> 6];
+ port = ports[(pos2 >> 4) & 0x03];
+ irq = ints[(pos2 >> 1) & 0x07];
+ }
+
+ if (irq) {
+ /* claim the slot */
+ mca_set_adapter_name( slot-1, fd_mcs_adapters[loop].name );
+
+ /* check irq/region */
+ if (check_region(port, 0x10) ||
+ request_irq(irq, fd_mcs_intr,
+ SA_SHIRQ, "fd_mcs", hosts)) {
+ printk( "fd_mcs: check_region() || request_irq() failed, Skip it\n");
+
+ continue;
+ }
+
+ /* register */
+ if (!(shpnt = scsi_register(tpnt, sizeof(struct fd_hostdata)))) {
+ printk( "fd_mcs: scsi_register() failed\n");
+ continue;
+ }
+
+ /* request I/O region */
+ request_region( port, 0x10, "fd_mcs" );
+
+ /* save name */
+ strcpy(adapter_name, fd_mcs_adapters[loop].name);
+
+ /* chip/fifo */
+ chip = fd_mcs_adapters[loop].fd_chip;
+ /* use boot time value if available */
+ FIFO_COUNT =
+ user_fifo_count?user_fifo_count:fd_mcs_adapters[loop].fifo_count;
+ FIFO_Size =
+ user_fifo_size?user_fifo_size:fd_mcs_adapters[loop].fifo_size;
+
+#ifdef NOT_USED
+ /* *************************************************** */
+ /* Try to toggle 32-bit mode. This only
+ works on an 18c30 chip. (User reports
+ say this works, so we should switch to
+ it in the near future.) */
+ outb( 0x80, port + IO_Control );
+ if ((inb( port + Configuration2 ) & 0x80) == 0x80) {
+ outb( 0x00, port + IO_Control );
+ if ((inb( port + Configuration2 ) & 0x80) == 0x00) {
+ chip = tmc18c30;
+ FIFO_Size = 0x800; /* 2k FIFO */
+
+ printk("FIRST: chip=%s, fifo_size=0x%x\n",
+ (chip == tmc18c30)?"tmc18c30":"tmc18c50", FIFO_Size);
+ }
+ }
+
+ /* That should have worked, but appears to
+ have problems. Let's assume it is an
+ 18c30 if the RAM is disabled. */
+
+ if (inb( port + Configuration2 ) & 0x02) {
+ chip = tmc18c30;
+ FIFO_Size = 0x800; /* 2k FIFO */
+
+ printk("SECOND: chip=%s, fifo_size=0x%x\n",
+ (chip == tmc18c30)?"tmc18c30":"tmc18c50", FIFO_Size);
+ }
+ /* *************************************************** */
+#endif
+
+#if 0
+ /* IBM/ANSI scsi scan ordering */
+ /* Stick this back in when the scsi.c changes are there */
+ shpnt->reverse_scan = 1;
+#endif
+
+
+ /* saving info */
+ hosts[found++] = shpnt;
+
+ shpnt->this_id = id;
+ shpnt->irq = irq;
+ shpnt->io_port = port;
+ shpnt->n_io_port = 0x10;
+
+ /* save */
+ bios_base = bios;
+ adapter_mask = (1 << id);
+
+ /* save more */
+ SCSI_Mode_Cntl_port = port + SCSI_Mode_Cntl;
+ FIFO_Data_Count_port = port + FIFO_Data_Count;
+ Interrupt_Cntl_port = port + Interrupt_Cntl;
+ Interrupt_Status_port = port + Interrupt_Status;
+ Interrupt_Cond_port = port + Interrupt_Cond;
+ Read_FIFO_port = port + Read_FIFO;
+ Read_SCSI_Data_port = port + Read_SCSI_Data;
+ SCSI_Cntl_port = port + SCSI_Cntl;
+ SCSI_Data_NoACK_port = port + SCSI_Data_NoACK;
+ SCSI_Status_port = port + SCSI_Status;
+ TMC_Cntl_port = port + TMC_Cntl;
+ TMC_Status_port = port + TMC_Status;
+ Write_FIFO_port = port + Write_FIFO;
+ Write_SCSI_Data_port = port + Write_SCSI_Data;
+
+ Bytes_Read = 0;
+ Bytes_Written = 0;
+ INTR_Processed = 0;
+
+ /* say something */
+ print_banner( shpnt );
+
+ /* reset */
+ outb( 1, SCSI_Cntl_port );
+ do_pause( 2 );
+ outb( 0, SCSI_Cntl_port );
+ do_pause( 115 );
+ outb( 0, SCSI_Mode_Cntl_port );
+ outb( PARITY_MASK, TMC_Cntl_port );
+ /* done reset */
+
+#if DO_DETECT
+ /* scan devices attached */
+ {
+ const int buflen = 255;
+ int i, j, retcode;
+ Scsi_Cmnd SCinit;
+ unsigned char do_inquiry[] = { INQUIRY, 0, 0, 0, buflen, 0 };
+ unsigned char do_request_sense[] = { REQUEST_SENSE,
+ 0, 0, 0, buflen, 0 };
+ unsigned char do_read_capacity[] = { READ_CAPACITY,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ unsigned char buf[buflen];
+
+ SCinit.request_buffer = SCinit.buffer = buf;
+ SCinit.request_bufflen = SCinit.bufflen = sizeof(buf)-1;
+ SCinit.use_sg = 0;
+ SCinit.lun = 0;
+ SCinit.host = shpnt;
+
+ printk( "fd_mcs: detection routine scanning for devices:\n" );
+ for (i = 0; i < 8; i++) {
+ if (i == shpnt->this_id) /* Skip host adapter */
+ continue;
+ SCinit.target = i;
+ memcpy(SCinit.cmnd, do_request_sense,
+ sizeof(do_request_sense));
+ retcode = fd_mcs_command(&SCinit);
+ if (!retcode) {
+ memcpy(SCinit.cmnd, do_inquiry, sizeof(do_inquiry));
+ retcode = fd_mcs_command(&SCinit);
+ if (!retcode) {
+ printk( " SCSI ID %d: ", i );
+ for (j = 8; j < (buf[4] < 32 ? buf[4] : 32); j++)
+ printk( "%c", buf[j] >= 20 ? buf[j] : ' ' );
+ memcpy(SCinit.cmnd, do_read_capacity,
+ sizeof(do_read_capacity));
+ retcode = fd_mcs_command(&SCinit);
+ if (!retcode) {
+ unsigned long blocks, size, capacity;
+
+ blocks = (buf[0] << 24) | (buf[1] << 16)
+ | (buf[2] << 8) | buf[3];
+ size = (buf[4] << 24) | (buf[5] << 16) |
+ (buf[6] << 8) | buf[7];
+ capacity = +( +(blocks / 1024L) * +(size * 10L)) / 1024L;
+
+ printk( "%lu MB (%lu byte blocks)\n",
+ ((capacity + 5L) / 10L), size );
+ }
+ }
+ }
+ }
+ }
+#endif
+ }
+ }
+
+ if (found == FD_MAX_HOSTS) {
+ printk( "fd_mcs: detecting reached max=%d host adapters.\n",
+ FD_MAX_HOSTS);
+ break;
+ }
+ }
+
+ return found;
+}
+
+const char *fd_mcs_info(struct Scsi_Host *shpnt)
+{
+ return adapter_name;
+}
+
+static int TOTAL_INTR = 0;
+
+/*
+ * inout : decides on the direction of the dataflow and the meaning of the
+ * variables
+ * buffer: If inout==FALSE data is being written to it else read from it
+ * *start: If inout==FALSE start of the valid data in the buffer
+ * offset: If inout==FALSE offset from the beginning of the imaginary file
+ * from which we start writing into the buffer
+ * length: If inout==FALSE max number of bytes to be written into the buffer
+ * else number of bytes in the buffer
+ */
+int fd_mcs_proc_info( char *buffer, char **start, off_t offset,
+ int length, int hostno, int inout )
+{
+ struct Scsi_Host *shpnt;
+ int len = 0;
+ int i;
+
+ if (inout)
+ return(-ENOSYS);
+
+ *start = buffer + offset;
+
+ for (i = 0; hosts[i] && hosts[i]->host_no != hostno; i++);
+ shpnt = hosts[i];
+
+ if (!shpnt) {
+ return(-ENOENT);
+ } else {
+ len += sprintf(buffer+len, "Future Domain MCS-600/700 Driver %s\n",
+ DRIVER_VERSION);
+
+ len += sprintf(buffer+len, "HOST #%d: %s\n",
+ hostno, adapter_name);
+
+ len += sprintf(buffer+len, "FIFO Size=0x%x, FIFO Count=%d\n",
+ FIFO_Size, FIFO_COUNT);
+
+ len += sprintf(buffer+len, "DriverCalls=%d, Interrupts=%d, BytesRead=%d, BytesWrite=%d\n\n",
+ TOTAL_INTR, INTR_Processed, Bytes_Read, Bytes_Written);
+ }
+
+ if ((len -= offset) <= 0)
+ return 0;
+ if (len > length)
+ len = length;
+ return len;
+}
+
+static int fd_mcs_select(struct Scsi_Host *shpnt, int target )
+{
+ int status;
+ unsigned long timeout;
+
+ outb( 0x82, SCSI_Cntl_port ); /* Bus Enable + Select */
+ outb( adapter_mask | (1 << target), SCSI_Data_NoACK_port );
+
+ /* Stop arbitration and enable parity */
+ outb( PARITY_MASK, TMC_Cntl_port );
+
+ timeout = 350; /* 350mS -- because of timeouts
+ (was 250mS) */
+
+ do {
+ status = inb( SCSI_Status_port ); /* Read adapter status */
+ if (status & 1) { /* Busy asserted */
+ /* Enable SCSI Bus (on error, should make bus idle with 0) */
+ outb( 0x80, SCSI_Cntl_port );
+ return 0;
+ }
+ udelay(1000); /* wait one msec */
+ } while (--timeout);
+
+ /* Make bus idle */
+ fd_mcs_make_bus_idle(shpnt);
+#if EVERY_ACCESS
+ if (!target) printk( "Selection failed\n" );
+#endif
+#if ERRORS_ONLY
+ if (!target) {
+ static int flag = 0;
+
+ if (!flag) /* Skip first failure for all chips. */
+ ++flag;
+ else
+ printk( "fd_mcs: Selection failed\n" );
+ }
+#endif
+ return 1;
+}
+
+static void my_done( struct Scsi_Host *shpnt, int error )
+{
+ if (in_command) {
+ in_command = 0;
+ outb( 0x00, Interrupt_Cntl_port );
+ fd_mcs_make_bus_idle(shpnt);
+ current_SC->result = error;
+ current_SC->scsi_done( current_SC );
+ } else {
+ panic( "fd_mcs: my_done() called outside of command\n" );
+ }
+#if DEBUG_RACE
+ in_interrupt_flag = 0;
+#endif
+}
+
+/* only my_done needs to be protected */
+static void fd_mcs_intr( int irq, void *dev_id, struct pt_regs * regs )
+{
+ unsigned long flags;
+ int status;
+ int done = 0;
+ unsigned data_count, tmp_count;
+
+ int i = 0;
+ struct Scsi_Host *shpnt;
+
+ TOTAL_INTR++;
+
+ /* search for one adapter-response on shared interrupt */
+ while ((shpnt = hosts[i++])) {
+ if ((inb(TMC_Status_port)) & 1)
+ break;
+ }
+
+ /* return if some other device on this IRQ caused the interrupt */
+ if (!shpnt) {
+ return;
+ }
+
+ INTR_Processed++;
+
+ outb( 0x00, Interrupt_Cntl_port );
+
+ /* Abort calls my_done, so we do nothing here. */
+ if (current_SC->SCp.phase & aborted) {
+#if DEBUG_ABORT
+ printk( "Interrupt after abort, ignoring\n" );
+#endif
+ /* return; */
+ }
+
+#if DEBUG_RACE
+ ++in_interrupt_flag;
+#endif
+
+ if (current_SC->SCp.phase & in_arbitration) {
+ status = inb( TMC_Status_port ); /* Read adapter status */
+ if (!(status & 0x02)) {
+#if EVERY_ACCESS
+ printk( " AFAIL " );
+#endif
+ spin_lock_irqsave(&io_request_lock, flags);
+ my_done( shpnt, DID_BUS_BUSY << 16 );
+ spin_unlock_irqrestore(&io_request_lock, flags);
+ return;
+ }
+ current_SC->SCp.phase = in_selection;
+
+ outb( 0x40 | FIFO_COUNT, Interrupt_Cntl_port );
+
+ outb( 0x82, SCSI_Cntl_port ); /* Bus Enable + Select */
+ outb( adapter_mask | (1 << current_SC->target), SCSI_Data_NoACK_port );
+
+ /* Stop arbitration and enable parity */
+ outb( 0x10 | PARITY_MASK, TMC_Cntl_port );
+#if DEBUG_RACE
+ in_interrupt_flag = 0;
+#endif
+ return;
+ } else if (current_SC->SCp.phase & in_selection) {
+ status = inb( SCSI_Status_port );
+ if (!(status & 0x01)) {
+ /* Try again, for slow devices */
+ if (fd_mcs_select(shpnt, current_SC->target )) {
+#if EVERY_ACCESS
+ printk( " SFAIL " );
+#endif
+ spin_lock_irqsave(&io_request_lock, flags);
+ my_done( shpnt, DID_NO_CONNECT << 16 );
+ spin_unlock_irqrestore(&io_request_lock, flags);
+ return;
+ } else {
+#if EVERY_ACCESS
+ printk( " AltSel " );
+#endif
+ /* Stop arbitration and enable parity */
+ outb( 0x10 | PARITY_MASK, TMC_Cntl_port );
+ }
+ }
+ current_SC->SCp.phase = in_other;
+ outb( 0x90 | FIFO_COUNT, Interrupt_Cntl_port );
+ outb( 0x80, SCSI_Cntl_port );
+#if DEBUG_RACE
+ in_interrupt_flag = 0;
+#endif
+ return;
+ }
+
+ /* current_SC->SCp.phase == in_other: this is the body of the routine */
+
+ status = inb( SCSI_Status_port );
+
+ if (status & 0x10) { /* REQ */
+
+ switch (status & 0x0e) {
+
+ case 0x08: /* COMMAND OUT */
+ outb( current_SC->cmnd[current_SC->SCp.sent_command++],
+ Write_SCSI_Data_port );
+#if EVERY_ACCESS
+ printk( "CMD = %x,",
+ current_SC->cmnd[ current_SC->SCp.sent_command - 1] );
+#endif
+ break;
+ case 0x00: /* DATA OUT -- tmc18c50/tmc18c30 only */
+ if (chip != tmc1800 && !current_SC->SCp.have_data_in) {
+ current_SC->SCp.have_data_in = -1;
+ outb( 0xd0 | PARITY_MASK, TMC_Cntl_port );
+ }
+ break;
+ case 0x04: /* DATA IN -- tmc18c50/tmc18c30 only */
+ if (chip != tmc1800 && !current_SC->SCp.have_data_in) {
+ current_SC->SCp.have_data_in = 1;
+ outb( 0x90 | PARITY_MASK, TMC_Cntl_port );
+ }
+ break;
+ case 0x0c: /* STATUS IN */
+ current_SC->SCp.Status = inb( Read_SCSI_Data_port );
+#if EVERY_ACCESS
+ printk( "Status = %x, ", current_SC->SCp.Status );
+#endif
+#if ERRORS_ONLY
+ if (current_SC->SCp.Status
+ && current_SC->SCp.Status != 2
+ && current_SC->SCp.Status != 8) {
+ printk( "ERROR fd_mcs: target = %d, command = %x, status = %x\n",
+ current_SC->target,
+ current_SC->cmnd[0],
+ current_SC->SCp.Status );
+ }
+#endif
+ break;
+ case 0x0a: /* MESSAGE OUT */
+ outb( MESSAGE_REJECT, Write_SCSI_Data_port ); /* Reject */
+ break;
+ case 0x0e: /* MESSAGE IN */
+ current_SC->SCp.Message = inb( Read_SCSI_Data_port );
+#if EVERY_ACCESS
+ printk( "Message = %x, ", current_SC->SCp.Message );
+#endif
+ if (!current_SC->SCp.Message) ++done;
+#if DEBUG_MESSAGES || EVERY_ACCESS
+ if (current_SC->SCp.Message) {
+ printk( "fd_mcs: message = %x\n", current_SC->SCp.Message );
+ }
+#endif
+ break;
+ }
+ }
+
+ if (chip == tmc1800
+ && !current_SC->SCp.have_data_in
+ && (current_SC->SCp.sent_command
+ >= current_SC->cmd_len)) {
+ /* We have to get the FIFO direction
+ correct, so I've made a table based
+ on the SCSI Standard of which commands
+ appear to require a DATA OUT phase.
+ */
+ /*
+ p. 94: Command for all device types
+ CHANGE DEFINITION 40 DATA OUT
+ COMPARE 39 DATA OUT
+ COPY 18 DATA OUT
+ COPY AND VERIFY 3a DATA OUT
+ INQUIRY 12
+ LOG SELECT 4c DATA OUT
+ LOG SENSE 4d
+ MODE SELECT (6) 15 DATA OUT
+ MODE SELECT (10) 55 DATA OUT
+ MODE SENSE (6) 1a
+ MODE SENSE (10) 5a
+ READ BUFFER 3c
+ RECEIVE DIAGNOSTIC RESULTS 1c
+ REQUEST SENSE 03
+ SEND DIAGNOSTIC 1d DATA OUT
+ TEST UNIT READY 00
+ WRITE BUFFER 3b DATA OUT
+
+ p.178: Commands for direct-access devices (not listed on p. 94)
+ FORMAT UNIT 04 DATA OUT
+ LOCK-UNLOCK CACHE 36
+ PRE-FETCH 34
+ PREVENT-ALLOW MEDIUM REMOVAL 1e
+ READ (6)/RECEIVE 08
+ READ (10) 3c
+ READ CAPACITY 25
+ READ DEFECT DATA (10) 37
+ READ LONG 3e
+ REASSIGN BLOCKS 07 DATA OUT
+ RELEASE 17
+ RESERVE 16 DATA OUT
+ REZERO UNIT/REWIND 01
+ SEARCH DATA EQUAL (10) 31 DATA OUT
+ SEARCH DATA HIGH (10) 30 DATA OUT
+ SEARCH DATA LOW (10) 32 DATA OUT
+ SEEK (6) 0b
+ SEEK (10) 2b
+ SET LIMITS (10) 33
+ START STOP UNIT 1b
+ SYNCHRONIZE CACHE 35
+ VERIFY (10) 2f
+ WRITE (6)/PRINT/SEND 0a DATA OUT
+ WRITE (10)/SEND 2a DATA OUT
+ WRITE AND VERIFY (10) 2e DATA OUT
+ WRITE LONG 3f DATA OUT
+ WRITE SAME 41 DATA OUT ?
+
+ p. 261: Commands for sequential-access devices (not previously listed)
+ ERASE 19
+ LOAD UNLOAD 1b
+ LOCATE 2b
+ READ BLOCK LIMITS 05
+ READ POSITION 34
+ READ REVERSE 0f
+ RECOVER BUFFERED DATA 14
+ SPACE 11
+ WRITE FILEMARKS 10 ?
+
+ p. 298: Commands for printer devices (not previously listed)
+ ****** NOT SUPPORTED BY THIS DRIVER, since 0b is SEEK (6) *****
+ SLEW AND PRINT 0b DATA OUT -- same as seek
+ STOP PRINT 1b
+ SYNCHRONIZE BUFFER 10
+
+ p. 315: Commands for processor devices (not previously listed)
+
+ p. 321: Commands for write-once devices (not previously listed)
+ MEDIUM SCAN 38
+ READ (12) a8
+ SEARCH DATA EQUAL (12) b1 DATA OUT
+ SEARCH DATA HIGH (12) b0 DATA OUT
+ SEARCH DATA LOW (12) b2 DATA OUT
+ SET LIMITS (12) b3
+ VERIFY (12) af
+ WRITE (12) aa DATA OUT
+ WRITE AND VERIFY (12) ae DATA OUT
+
+ p. 332: Commands for CD-ROM devices (not previously listed)
+ PAUSE/RESUME 4b
+ PLAY AUDIO (10) 45
+ PLAY AUDIO (12) a5
+ PLAY AUDIO MSF 47
+ PLAY TRACK RELATIVE (10) 49
+ PLAY TRACK RELATIVE (12) a9
+ READ HEADER 44
+ READ SUB-CHANNEL 42
+ READ TOC 43
+
+ p. 370: Commands for scanner devices (not previously listed)
+ GET DATA BUFFER STATUS 34
+ GET WINDOW 25
+ OBJECT POSITION 31
+ SCAN 1b
+ SET WINDOW 24 DATA OUT
+
+ p. 391: Commands for optical memory devices (not listed)
+ ERASE (10) 2c
+ ERASE (12) ac
+ MEDIUM SCAN 38 DATA OUT
+ READ DEFECT DATA (12) b7
+ READ GENERATION 29
+ READ UPDATED BLOCK 2d
+ UPDATE BLOCK 3d DATA OUT
+
+ p. 419: Commands for medium changer devices (not listed)
+ EXCHANGE MEDIUM 46
+ INITIALIZE ELEMENT STATUS 07
+ MOVE MEDIUM a5
+ POSITION TO ELEMENT 2b
+ READ ELEMENT STATUS b8
+ REQUEST VOL. ELEMENT ADDRESS b5
+ SEND VOLUME TAG b6 DATA OUT
+
+ p. 454: Commands for communications devices (not listed previously)
+ GET MESSAGE (6) 08
+ GET MESSAGE (10) 28
+ GET MESSAGE (12) a8
+ */
+
+ switch (current_SC->cmnd[0]) {
+ case CHANGE_DEFINITION: case COMPARE: case COPY:
+ case COPY_VERIFY: case LOG_SELECT: case MODE_SELECT:
+ case MODE_SELECT_10: case SEND_DIAGNOSTIC: case WRITE_BUFFER:
+
+ case FORMAT_UNIT: case REASSIGN_BLOCKS: case RESERVE:
+ case SEARCH_EQUAL: case SEARCH_HIGH: case SEARCH_LOW:
+ case WRITE_6: case WRITE_10: case WRITE_VERIFY:
+ case 0x3f: case 0x41:
+
+ case 0xb1: case 0xb0: case 0xb2:
+ case 0xaa: case 0xae:
+
+ case 0x24:
+
+ case 0x38: case 0x3d:
+
+ case 0xb6:
+
+ case 0xea: /* alternate number for WRITE LONG */
+
+ current_SC->SCp.have_data_in = -1;
+ outb( 0xd0 | PARITY_MASK, TMC_Cntl_port );
+ break;
+
+ case 0x00:
+ default:
+
+ current_SC->SCp.have_data_in = 1;
+ outb( 0x90 | PARITY_MASK, TMC_Cntl_port );
+ break;
+ }
+ }
+
+ if (current_SC->SCp.have_data_in == -1) { /* DATA OUT */
+ while ( (data_count = FIFO_Size - inw( FIFO_Data_Count_port )) > 512 ) {
+#if EVERY_ACCESS
+ printk( "DC=%d, ", data_count ) ;
+#endif
+ if (data_count > current_SC->SCp.this_residual)
+ data_count = current_SC->SCp.this_residual;
+ if (data_count > 0) {
+#if EVERY_ACCESS
+ printk( "%d OUT, ", data_count );
+#endif
+ if (data_count == 1) {
+ Bytes_Written++;
+
+ outb( *current_SC->SCp.ptr++, Write_FIFO_port );
+ --current_SC->SCp.this_residual;
+ } else {
+ data_count >>= 1;
+ tmp_count = data_count << 1;
+ outsw( Write_FIFO_port, current_SC->SCp.ptr, data_count );
+ current_SC->SCp.ptr += tmp_count;
+ Bytes_Written += tmp_count;
+ current_SC->SCp.this_residual -= tmp_count;
+ }
+ }
+ if (!current_SC->SCp.this_residual) {
+ if (current_SC->SCp.buffers_residual) {
+ --current_SC->SCp.buffers_residual;
+ ++current_SC->SCp.buffer;
+ current_SC->SCp.ptr = current_SC->SCp.buffer->address;
+ current_SC->SCp.this_residual = current_SC->SCp.buffer->length;
+ } else
+ break;
+ }
+ }
+ } else if (current_SC->SCp.have_data_in == 1) { /* DATA IN */
+ while ((data_count = inw( FIFO_Data_Count_port )) > 0) {
+#if EVERY_ACCESS
+ printk( "DC=%d, ", data_count );
+#endif
+ if (data_count > current_SC->SCp.this_residual)
+ data_count = current_SC->SCp.this_residual;
+ if (data_count) {
+#if EVERY_ACCESS
+ printk( "%d IN, ", data_count );
+#endif
+ if (data_count == 1) {
+ Bytes_Read++;
+ *current_SC->SCp.ptr++ = inb( Read_FIFO_port );
+ --current_SC->SCp.this_residual;
+ } else {
+ data_count >>= 1; /* Number of words */
+ tmp_count = data_count << 1;
+ insw( Read_FIFO_port, current_SC->SCp.ptr, data_count );
+ current_SC->SCp.ptr += tmp_count;
+ Bytes_Read += tmp_count;
+ current_SC->SCp.this_residual -= tmp_count;
+ }
+ }
+ if (!current_SC->SCp.this_residual
+ && current_SC->SCp.buffers_residual) {
+ --current_SC->SCp.buffers_residual;
+ ++current_SC->SCp.buffer;
+ current_SC->SCp.ptr = current_SC->SCp.buffer->address;
+ current_SC->SCp.this_residual = current_SC->SCp.buffer->length;
+ }
+ }
+ }
+
+ if (done) {
+#if EVERY_ACCESS
+ printk( " ** IN DONE %d ** ", current_SC->SCp.have_data_in );
+#endif
+
+#if ERRORS_ONLY
+ if (current_SC->cmnd[0] == REQUEST_SENSE && !current_SC->SCp.Status) {
+ if ((unsigned char)(*((char *)current_SC->request_buffer+2)) & 0x0f) {
+ unsigned char key;
+ unsigned char code;
+ unsigned char qualifier;
+
+ key = (unsigned char)(*((char *)current_SC->request_buffer + 2))
+ & 0x0f;
+ code = (unsigned char)(*((char *)current_SC->request_buffer + 12));
+ qualifier = (unsigned char)(*((char *)current_SC->request_buffer
+ + 13));
+
+ if (key != UNIT_ATTENTION
+ && !(key == NOT_READY
+ && code == 0x04
+ && (!qualifier || qualifier == 0x02 || qualifier == 0x01))
+ && !(key == ILLEGAL_REQUEST && (code == 0x25
+ || code == 0x24
+ || !code)))
+
+ printk( "fd_mcs: REQUEST SENSE "
+ "Key = %x, Code = %x, Qualifier = %x\n",
+ key, code, qualifier );
+ }
+ }
+#endif
+#if EVERY_ACCESS
+ printk( "BEFORE MY_DONE. . ." );
+#endif
+ spin_lock_irqsave(&io_request_lock, flags);
+ my_done( shpnt,
+ (current_SC->SCp.Status & 0xff)
+ | ((current_SC->SCp.Message & 0xff) << 8) | (DID_OK << 16) );
+ spin_unlock_irqrestore(&io_request_lock, flags);
+#if EVERY_ACCESS
+ printk( "RETURNING.\n" );
+#endif
+
+ } else {
+ if (current_SC->SCp.phase & disconnect) {
+ outb( 0xd0 | FIFO_COUNT, Interrupt_Cntl_port );
+ outb( 0x00, SCSI_Cntl_port );
+ } else {
+ outb( 0x90 | FIFO_COUNT, Interrupt_Cntl_port );
+ }
+ }
+#if DEBUG_RACE
+ in_interrupt_flag = 0;
+#endif
+ return;
+}
+
+int fd_mcs_release(struct Scsi_Host *shpnt)
+{
+ int i, this_host, irq_usage;
+
+ release_region(shpnt->io_port, shpnt->n_io_port);
+
+ this_host = -1;
+ irq_usage = 0;
+ for (i = 0; i < found; i++) {
+ if (shpnt == hosts[i])
+ this_host = i;
+ if (shpnt->irq == hosts[i]->irq)
+ irq_usage++;
+ }
+
+ /* only for the last one */
+ if (1 == irq_usage)
+ free_irq(shpnt->irq, hosts);
+
+ found--;
+
+ for (i = this_host; i < found; i++)
+ hosts[i] = hosts[i+1];
+
+ hosts[found] = NULL;
+
+ return 0;
+}
+
+int fd_mcs_queue( Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
+{
+ struct Scsi_Host *shpnt = SCpnt->host;
+
+ if (in_command) {
+ panic( "fd_mcs: fd_mcs_queue() NOT REENTRANT!\n" );
+ }
+#if EVERY_ACCESS
+ printk( "queue: target = %d cmnd = 0x%02x pieces = %d size = %u\n",
+ SCpnt->target,
+ *(unsigned char *)SCpnt->cmnd,
+ SCpnt->use_sg,
+ SCpnt->request_bufflen );
+#endif
+
+ fd_mcs_make_bus_idle(shpnt);
+
+ SCpnt->scsi_done = done; /* Save this for the done function */
+ current_SC = SCpnt;
+
+ /* Initialize static data */
+
+ if (current_SC->use_sg) {
+ current_SC->SCp.buffer =
+ (struct scatterlist *)current_SC->request_buffer;
+ current_SC->SCp.ptr = current_SC->SCp.buffer->address;
+ current_SC->SCp.this_residual = current_SC->SCp.buffer->length;
+ current_SC->SCp.buffers_residual = current_SC->use_sg - 1;
+ } else {
+ current_SC->SCp.ptr = (char *)current_SC->request_buffer;
+ current_SC->SCp.this_residual = current_SC->request_bufflen;
+ current_SC->SCp.buffer = NULL;
+ current_SC->SCp.buffers_residual = 0;
+ }
+
+
+ current_SC->SCp.Status = 0;
+ current_SC->SCp.Message = 0;
+ current_SC->SCp.have_data_in = 0;
+ current_SC->SCp.sent_command = 0;
+ current_SC->SCp.phase = in_arbitration;
+
+ /* Start arbitration */
+ outb( 0x00, Interrupt_Cntl_port );
+ outb( 0x00, SCSI_Cntl_port ); /* Disable data drivers */
+ outb( adapter_mask, SCSI_Data_NoACK_port ); /* Set our id bit */
+ in_command = 1;
+ outb( 0x20, Interrupt_Cntl_port );
+ outb( 0x14 | PARITY_MASK, TMC_Cntl_port ); /* Start arbitration */
+
+ return 0;
+}
+
+static void internal_done( Scsi_Cmnd *SCpnt )
+{
+ /* flag it done */
+ SCpnt->host_scribble = (unsigned char *)1;
+}
+
+int fd_mcs_command( Scsi_Cmnd *SCpnt )
+{
+ fd_mcs_queue( SCpnt, internal_done );
+ /* host_scribble is used for status here */
+ SCpnt->host_scribble = NULL;
+ while (!SCpnt->host_scribble)
+ barrier();
+ return SCpnt->result;
+}
+
+#if DEBUG_ABORT || DEBUG_RESET
+static void fd_mcs_print_info( Scsi_Cmnd *SCpnt )
+{
+ unsigned int imr;
+ unsigned int irr;
+ unsigned int isr;
+ struct Scsi_Host *shpnt = SCpnt->host;
+
+ if (!SCpnt || !SCpnt->host) {
+ printk( "fd_mcs: cannot provide detailed information\n" );
+ }
+
+ printk( "%s\n", fd_mcs_info( SCpnt->host ) );
+ print_banner( SCpnt->host );
+ switch (SCpnt->SCp.phase) {
+ case in_arbitration: printk( "arbitration " ); break;
+ case in_selection: printk( "selection " ); break;
+ case in_other: printk( "other " ); break;
+ default: printk( "unknown " ); break;
+ }
+
+ printk( "(%d), target = %d cmnd = 0x%02x pieces = %d size = %u\n",
+ SCpnt->SCp.phase,
+ SCpnt->target,
+ *(unsigned char *)SCpnt->cmnd,
+ SCpnt->use_sg,
+ SCpnt->request_bufflen );
+ printk( "sent_command = %d, have_data_in = %d, timeout = %d\n",
+ SCpnt->SCp.sent_command,
+ SCpnt->SCp.have_data_in,
+ SCpnt->timeout );
+#if DEBUG_RACE
+ printk( "in_interrupt_flag = %d\n", in_interrupt_flag );
+#endif
+
+ imr = (inb( 0x0a1 ) << 8) + inb( 0x21 );
+ outb( 0x0a, 0xa0 );
+ irr = inb( 0xa0 ) << 8;
+ outb( 0x0a, 0x20 );
+ irr += inb( 0x20 );
+ outb( 0x0b, 0xa0 );
+ isr = inb( 0xa0 ) << 8;
+ outb( 0x0b, 0x20 );
+ isr += inb( 0x20 );
+
+ /* Print out interesting information */
+ printk( "IMR = 0x%04x", imr );
+ if (imr & (1 << shpnt->irq))
+ printk( " (masked)" );
+ printk( ", IRR = 0x%04x, ISR = 0x%04x\n", irr, isr );
+
+ printk( "SCSI Status = 0x%02x\n", inb( SCSI_Status_port ) );
+ printk( "TMC Status = 0x%02x", inb( TMC_Status_port ) );
+ if (inb( TMC_Status_port ) & 1)
+ printk( " (interrupt)" );
+ printk( "\n" );
+ printk( "Interrupt Status = 0x%02x", inb( Interrupt_Status_port ) );
+ if (inb( Interrupt_Status_port ) & 0x08)
+ printk( " (enabled)" );
+ printk( "\n" );
+ if (chip == tmc18c50 || chip == tmc18c30) {
+ printk( "FIFO Status = 0x%02x\n", inb( shpnt->io_port + FIFO_Status ) );
+ printk( "Int. Condition = 0x%02x\n",
+ inb( shpnt->io_port + Interrupt_Cond ) );
+ }
+ printk( "Configuration 1 = 0x%02x\n", inb( shpnt->io_port + Configuration1 ) );
+ if (chip == tmc18c50 || chip == tmc18c30)
+ printk( "Configuration 2 = 0x%02x\n",
+ inb( shpnt->io_port + Configuration2 ) );
+}
+#endif
+
+int fd_mcs_abort( Scsi_Cmnd *SCpnt)
+{
+ struct Scsi_Host *shpnt = SCpnt->host;
+
+ unsigned long flags;
+#if EVERY_ACCESS || ERRORS_ONLY || DEBUG_ABORT
+ printk( "fd_mcs: abort " );
+#endif
+
+ save_flags( flags );
+ cli();
+ if (!in_command) {
+#if EVERY_ACCESS || ERRORS_ONLY
+ printk( " (not in command)\n" );
+#endif
+ restore_flags( flags );
+ return SCSI_ABORT_NOT_RUNNING;
+ } else printk( "\n" );
+
+#if DEBUG_ABORT
+ fd_mcs_print_info( SCpnt );
+#endif
+
+ fd_mcs_make_bus_idle(shpnt);
+
+ current_SC->SCp.phase |= aborted;
+
+ current_SC->result = DID_ABORT << 16;
+
+ restore_flags( flags );
+
+ /* Aborts are not done well. . . */
+ spin_lock_irqsave(&io_request_lock, flags);
+ my_done( shpnt, DID_ABORT << 16 );
+ spin_unlock_irqrestore(&io_request_lock, flags);
+
+ return SCSI_ABORT_SUCCESS;
+}
+
+int fd_mcs_reset( Scsi_Cmnd *SCpnt, unsigned int reset_flags )
+{
+ struct Scsi_Host *shpnt = SCpnt->host;
+
+#if DEBUG_RESET
+ static int called_once = 0;
+#endif
+
+#if ERRORS_ONLY
+ if (SCpnt) printk( "fd_mcs: SCSI Bus Reset\n" );
+#endif
+
+#if DEBUG_RESET
+ if (called_once) fd_mcs_print_info( current_SC );
+ called_once = 1;
+#endif
+
+ outb( 1, SCSI_Cntl_port );
+ do_pause( 2 );
+ outb( 0, SCSI_Cntl_port );
+ do_pause( 115 );
+ outb( 0, SCSI_Mode_Cntl_port );
+ outb( PARITY_MASK, TMC_Cntl_port );
+
+ /* Unless this is the very first call (i.e., SCPnt == NULL), everything
+ is probably hosed at this point. We will, however, try to keep
+ things going by informing the high-level code that we need help. */
+
+ return SCSI_RESET_WAKEUP;
+}
+
+#include "sd.h"
+#include <scsi/scsi_ioctl.h>
+
+int fd_mcs_biosparam( Scsi_Disk *disk, kdev_t dev, int *info_array )
+{
+ int drive;
+ unsigned char buf[512 + sizeof( int ) * 2];
+ int size = disk->capacity;
+ int *sizes = (int *)buf;
+ unsigned char *data = (unsigned char *)(sizes + 2);
+ unsigned char do_read[] = { READ_6, 0, 0, 0, 1, 0 };
+ int retcode;
+
+ /* BIOS >= 3.4 for MCA cards */
+ drive = MINOR(dev) / 16;
+
+ /* This algorithm was provided by Future Domain (much thanks!). */
+
+ sizes[0] = 0; /* zero bytes out */
+ sizes[1] = 512; /* one sector in */
+ memcpy( data, do_read, sizeof( do_read ) );
+ retcode = kernel_scsi_ioctl( disk->device,
+ SCSI_IOCTL_SEND_COMMAND,
+ (void *)buf );
+ if (!retcode /* SCSI command ok */
+ && data[511] == 0xaa && data[510] == 0x55 /* Partition table valid */
+ && data[0x1c2]) { /* Partition type */
+
+ /* The partition table layout is as follows:
+
+ Start: 0x1b3h
+ Offset: 0 = partition status
+ 1 = starting head
+ 2 = starting sector and cylinder (word, encoded)
+ 4 = partition type
+ 5 = ending head
+ 6 = ending sector and cylinder (word, encoded)
+ 8 = starting absolute sector (double word)
+ c = number of sectors (double word)
+ Signature: 0x1fe = 0x55aa
+
+ So, this algorithm assumes:
+ 1) the first partition table is in use,
+ 2) the data in the first entry is correct, and
+ 3) partitions never divide cylinders
+
+ Note that (1) may be FALSE for NetBSD (and other BSD flavors),
+ as well as for Linux. Note also, that Linux doesn't pay any
+ attention to the fields that are used by this algorithm -- it
+ only uses the absolute sector data. Recent versions of Linux's
+ fdisk(1) will fill this data in correctly, and forthcoming
+ versions will check for consistency.
+
+ Checking for a non-zero partition type is not part of the
+ Future Domain algorithm, but it seemed to be a reasonable thing
+ to do, especially in the Linux and BSD worlds. */
+
+ info_array[0] = data[0x1c3] + 1; /* heads */
+ info_array[1] = data[0x1c4] & 0x3f; /* sectors */
+ } else {
+
+ /* Note that this new method guarantees that there will always be
+ less than 1024 cylinders on a platter. This is good for drives
+ up to approximately 7.85GB (where 1GB = 1024 * 1024 kB). */
+
+ if ((unsigned int)size >= 0x7e0000U) {
+ info_array[0] = 0xff; /* heads = 255 */
+ info_array[1] = 0x3f; /* sectors = 63 */
+ } else if ((unsigned int)size >= 0x200000U) {
+ info_array[0] = 0x80; /* heads = 128 */
+ info_array[1] = 0x3f; /* sectors = 63 */
+ } else {
+ info_array[0] = 0x40; /* heads = 64 */
+ info_array[1] = 0x20; /* sectors = 32 */
+ }
+ }
+ /* For both methods, compute the cylinders */
+ info_array[2] = (unsigned int)size / (info_array[0] * info_array[1] );
+
+
+ return 0;
+}
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = FD_MCS;
+
+#include "scsi_module.c"
+#endif
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov