summaryrefslogtreecommitdiff
path: root/linux
diff options
context:
space:
mode:
authorThomas Bushnell <thomas@gnu.org>1999-04-26 05:44:30 +0000
committerThomas Bushnell <thomas@gnu.org>1999-04-26 05:44:30 +0000
commit063bab9f9919bd7460386ed11a15e7f67673077e (patch)
treeead73696a2296ce80fd8374f18258eb255b29ef8 /linux
parent1007688e7015f5e8d37743353ca153391120feb3 (diff)
1999-04-12 OKUJI Yoshinori <okuji@kuicr.kyoto-u.ac.jp>
* linux/dev/drivers/block/ide.c (init_hwif_data) [MACH]: Print Mach device name instead of Linux one. * linux/dev/drivers/block/genhd.c (disk_name): Likewise. * linux/dev/drivers/scsi/sd.c (sd_init_onedisk): Likewise. (sd_detect): Likewise. * linux/dev/drivers/sr.c (sr_detect): Likewise. (get_sectorsize): Likewise. 1999-02-04 OKUJI Yoshinori <okuji@kuicr.kyoto-u.ac.jp> * device/kmsg.c (kmsginit): Add a missing semicolon. (kmsggetstat): Fix typos, DEV_GET_DEVICE_SIZE -> DEV_GET_SIZE_DEVICE_SIZE and DEV_GET_RECORD_SIZE -> DEV_GET_SIZE_RECORD_SIZE. (kmsg_putchar): Fix a typo kmsg_done_init -> kmsg_init_done. * linux/dev/glue/block.c (device_get_status): Allocate a hd_geometry on the stack. * linux/dev/drivers/block/ide.c: New file. * linux/dev/drivers/scsi/sd_ioctl.c: New file.
Diffstat (limited to 'linux')
-rw-r--r--linux/dev/drivers/scsi/aha152x.c3281
-rw-r--r--linux/dev/drivers/scsi/eata_dma.c1607
-rw-r--r--linux/dev/drivers/scsi/g_NCR5380.c739
-rw-r--r--linux/dev/drivers/scsi/hosts.c549
-rw-r--r--linux/dev/drivers/scsi/scsi.c3585
-rw-r--r--linux/dev/drivers/scsi/scsi.h650
-rw-r--r--linux/dev/drivers/scsi/sd.c1691
-rw-r--r--linux/dev/drivers/scsi/sd_ioctl.c128
-rw-r--r--linux/dev/drivers/scsi/seagate.c1761
-rw-r--r--linux/dev/drivers/scsi/sr.c1290
10 files changed, 15281 insertions, 0 deletions
diff --git a/linux/dev/drivers/scsi/aha152x.c b/linux/dev/drivers/scsi/aha152x.c
new file mode 100644
index 0000000..59ccea6
--- /dev/null
+++ b/linux/dev/drivers/scsi/aha152x.c
@@ -0,0 +1,3281 @@
+/* aha152x.c -- Adaptec AHA-152x driver
+ * Author: Jürgen E. Fischer, fischer@et-inf.fho-emden.de
+ * Copyright 1993, 1994, 1995, 1996 Jürgen E. Fischer
+ *
+ *
+ * This driver is based on
+ * fdomain.c -- Future Domain TMC-16x0 driver
+ * which is
+ * Copyright 1992, 1993 Rickard E. Faith (faith@cs.unc.edu)
+ *
+ *
+ * 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.
+ *
+ *
+ * $Id: aha152x.c,v 1.1 1999/04/26 05:44:22 tb Exp $
+ *
+ * $Log: aha152x.c,v $
+ * Revision 1.18 1996/09/07 20:10:40 fischer
+ * - fixed can_queue handling (multiple outstanding commands working again)
+ *
+ * Revision 1.17 1996/08/17 16:05:14 fischer
+ * - biosparam improved
+ * - interrupt verification
+ * - updated documentation
+ * - cleanups
+ *
+ * Revision 1.16 1996/06/09 00:04:56 root
+ * - added configuration symbols for insmod (aha152x/aha152x1)
+ *
+ * Revision 1.15 1996/04/30 14:52:06 fischer
+ * - proc info fixed
+ * - support for extended translation for >1GB disks
+ *
+ * Revision 1.14 1996/01/17 15:11:20 fischer
+ * - fixed lockup in MESSAGE IN phase after reconnection
+ *
+ * Revision 1.13 1996/01/09 02:15:53 fischer
+ * - some cleanups
+ * - moved request_irq behind controller initialization
+ * (to avoid spurious interrupts)
+ *
+ * Revision 1.12 1995/12/16 12:26:07 fischer
+ * - barrier()s added
+ * - configurable RESET delay added
+ *
+ * Revision 1.11 1995/12/06 21:18:35 fischer
+ * - some minor updates
+ *
+ * Revision 1.10 1995/07/22 19:18:45 fischer
+ * - support for 2 controllers
+ * - started synchronous data transfers (not working yet)
+ *
+ * Revision 1.9 1995/03/18 09:20:24 root
+ * - patches for PCMCIA and modules
+ *
+ * Revision 1.8 1995/01/21 22:07:19 root
+ * - snarf_region => request_region
+ * - aha152x_intr interface change
+ *
+ * Revision 1.7 1995/01/02 23:19:36 root
+ * - updated COMMAND_SIZE to cmd_len
+ * - changed sti() to restore_flags()
+ * - fixed some #ifdef which generated warnings
+ *
+ * Revision 1.6 1994/11/24 20:35:27 root
+ * - problem with odd number of bytes in fifo fixed
+ *
+ * Revision 1.5 1994/10/30 14:39:56 root
+ * - abort code fixed
+ * - debugging improved
+ *
+ * Revision 1.4 1994/09/12 11:33:01 root
+ * - irqaction to request_irq
+ * - abortion updated
+ *
+ * Revision 1.3 1994/08/04 13:53:05 root
+ * - updates for mid-level-driver changes
+ * - accept unexpected BUSFREE phase as error condition
+ * - parity check now configurable
+ *
+ * Revision 1.2 1994/07/03 12:56:36 root
+ * - cleaned up debugging code
+ * - more tweaking on reset delays
+ * - updated abort/reset code (pretty untested...)
+ *
+ * Revision 1.1 1994/05/28 21:18:49 root
+ * - update for mid-level interface change (abort-reset)
+ * - delays after resets adjusted for some slow devices
+ *
+ * Revision 1.0 1994/03/25 12:52:00 root
+ * - Fixed "more data than expected" problem
+ * - added new BIOS signatures
+ *
+ * Revision 0.102 1994/01/31 20:44:12 root
+ * - minor changes in insw/outsw handling
+ *
+ * Revision 0.101 1993/12/13 01:16:27 root
+ * - fixed STATUS phase (non-GOOD stati were dropped sometimes;
+ * fixes problems with CD-ROM sector size detection & media change)
+ *
+ * Revision 0.100 1993/12/10 16:58:47 root
+ * - fix for unsuccessful selections in case of non-continuous id assignments
+ * on the scsi bus.
+ *
+ * Revision 0.99 1993/10/24 16:19:59 root
+ * - fixed DATA IN (rare read errors gone)
+ *
+ * Revision 0.98 1993/10/17 12:54:44 root
+ * - fixed some recent fixes (shame on me)
+ * - moved initialization of scratch area to aha152x_queue
+ *
+ * Revision 0.97 1993/10/09 18:53:53 root
+ * - DATA IN fixed. Rarely left data in the fifo.
+ *
+ * Revision 0.96 1993/10/03 00:53:59 root
+ * - minor changes on DATA IN
+ *
+ * Revision 0.95 1993/09/24 10:36:01 root
+ * - change handling of MSGI after reselection
+ * - fixed sti/cli
+ * - minor changes
+ *
+ * Revision 0.94 1993/09/18 14:08:22 root
+ * - fixed bug in multiple outstanding command code
+ * - changed detection
+ * - support for kernel command line configuration
+ * - reset corrected
+ * - changed message handling
+ *
+ * Revision 0.93 1993/09/15 20:41:19 root
+ * - fixed bugs with multiple outstanding commands
+ *
+ * Revision 0.92 1993/09/13 02:46:33 root
+ * - multiple outstanding commands work (no problems with IBM drive)
+ *
+ * Revision 0.91 1993/09/12 20:51:46 root
+ * added multiple outstanding commands
+ * (some problem with this $%&? IBM device remain)
+ *
+ * Revision 0.9 1993/09/12 11:11:22 root
+ * - corrected auto-configuration
+ * - changed the auto-configuration (added some '#define's)
+ * - added support for dis-/reconnection
+ *
+ * Revision 0.8 1993/09/06 23:09:39 root
+ * - added support for the drive activity light
+ * - minor changes
+ *
+ * Revision 0.7 1993/09/05 14:30:15 root
+ * - improved phase detection
+ * - now using the new snarf_region code of 0.99pl13
+ *
+ * Revision 0.6 1993/09/02 11:01:38 root
+ * first public release; added some signatures and biosparam()
+ *
+ * Revision 0.5 1993/08/30 10:23:30 root
+ * fixed timing problems with my IBM drive
+ *
+ * Revision 0.4 1993/08/29 14:06:52 root
+ * fixed some problems with timeouts due incomplete commands
+ *
+ * Revision 0.3 1993/08/28 15:55:03 root
+ * writing data works too. mounted and worked on a dos partition
+ *
+ * Revision 0.2 1993/08/27 22:42:07 root
+ * reading data works. Mounted a msdos partition.
+ *
+ * Revision 0.1 1993/08/25 13:38:30 root
+ * first "damn thing doesn't work" version
+ *
+ * Revision 0.0 1993/08/14 19:54:25 root
+ * empty function bodies; detect() works.
+ *
+ *
+ **************************************************************************
+
+
+
+ DESCRIPTION:
+
+ This is the Linux low-level SCSI driver for Adaptec AHA-1520/1522 SCSI
+ host adapters.
+
+
+ CONFIGURATION ARGUMENTS:
+
+ IOPORT base io address (0x340/0x140)
+ IRQ interrupt level (9-12; default 11)
+ SCSI_ID scsi id of controller (0-7; default 7)
+ RECONNECT allow targets to disconnect from the bus (0/1; default 1 [on])
+ PARITY enable parity checking (0/1; default 1 [on])
+ SYNCHRONOUS enable synchronous transfers (0/1; default 0 [off])
+ (NOT WORKING YET)
+ DELAY: bus reset delay (default 100)
+ EXT_TRANS: enable extended translation (0/1: default 0 [off])
+ (see NOTES below)
+
+ COMPILE TIME CONFIGURATION (put into AHA152X in drivers/scsi/Makefile):
+
+ -DAUTOCONF
+ use configuration the controller reports (AHA-152x only)
+
+ -DSKIP_BIOSTEST
+ Don't test for BIOS signature (AHA-1510 or disabled BIOS)
+
+ -DSETUP0="{ IOPORT, IRQ, SCSI_ID, RECONNECT, PARITY, SYNCHRONOUS, DELAY, EXT_TRANS }"
+ override for the first controller
+
+ -DSETUP1="{ IOPORT, IRQ, SCSI_ID, RECONNECT, PARITY, SYNCHRONOUS, DELAY, EXT_TRANS }"
+ override for the second controller
+
+
+ LILO COMMAND LINE OPTIONS:
+
+ aha152x=<IOPORT>[,<IRQ>[,<SCSI-ID>[,<RECONNECT>[,<PARITY>[,<SYNCHRONOUS>[,<DELAY> [,<EXT_TRANS]]]]]]]
+
+ The normal configuration can be overridden by specifying a command line.
+ When you do this, the BIOS test is skipped. Entered values have to be
+ valid (known). Don't use values that aren't supported under normal
+ operation. If you think that you need other values: contact me.
+ For two controllers use the aha152x statement twice.
+
+
+ SYMBOLS FOR MODULE CONFIGURATION:
+
+ aha152x=IOPORT,IRQ,SCSI_ID,RECONNECT,PARITY,SYNCHRONOUS,DELAY,EXT_TRANS
+ configuration override of first controller
+
+
+ aha152x1=IOPORT,IRQ,SCSI_ID,RECONNECT,PARITY,SYNCHRONOUS,DELAY,EXT_TRANS
+ configuration override of second controller
+
+
+ NOTES ON EXT_TRANS:
+
+ SCSI uses block numbers to address blocks/sectors on a device.
+ The BIOS uses a cylinder/head/sector addressing scheme (C/H/S)
+ scheme instead. DOS expects a BIOS or driver that understands this
+ C/H/S addressing.
+
+ The number of cylinders/heads/sectors is called geometry and is required
+ as base for requests in C/H/S adressing. SCSI only knows about the
+ total capacity of disks in blocks (sectors).
+
+ Therefore the SCSI BIOS/DOS driver has to calculate a logical/virtual
+ geometry just to be able to support that addressing scheme. The geometry
+ returned by the SCSI BIOS is a pure calculation and has nothing to
+ do with the real/physical geometry of the disk (which is usually
+ irrelevant anyway).
+
+ Basically this has no impact at all on Linux, because it also uses block
+ instead of C/H/S addressing. Unfortunately C/H/S addressing is also used
+ in the partition table and therefore every operating system has to know
+ the right geometry to be able to interpret it.
+
+ Moreover there are certain limitations to the C/H/S addressing scheme,
+ namely the address space is limited to upto 255 heads, upto 63 sectors
+ and a maximum of 1023 cylinders.
+
+ The AHA-1522 BIOS calculates the geometry by fixing the number of heads
+ to 64, the number of sectors to 32 and by calculating the number of
+ cylinders by dividing the capacity reported by the disk by 64*32 (1 MB).
+ This is considered to be the default translation.
+
+ With respect to the limit of 1023 cylinders using C/H/S you can only
+ address the first GB of your disk in the partition table. Therefore
+ BIOSes of some newer controllers based on the AIC-6260/6360 support
+ extended translation. This means that the BIOS uses 255 for heads,
+ 63 for sectors and then divides the capacity of the disk by 255*63
+ (about 8 MB), as soon it sees a disk greater than 1 GB. That results
+ in a maximum of about 8 GB adressable diskspace in the partition table
+ (but there are already bigger disks out there today).
+
+ To make it even more complicated the translation mode might/might
+ not be configurable in certain BIOS setups.
+
+ This driver does some more or less failsafe guessing to get the
+ geometry right in most cases:
+
+ - for disks<1GB: use default translation (C/32/64)
+ - for disks>1GB:
+ - take current geometry from the partition table
+ (using scsicam_bios_param and accept only `valid' geometries,
+ ie. either (C/32/64) or (C/63/255)). This can be extended
+ translation even if it's not enabled in the driver.
+ - if that fails, take extended translation if enabled by override,
+ kernel or module parameter, otherwise take default translation and
+ ask the user for verification. This might on not yet partitioned
+ disks or
+
+
+ REFERENCES USED:
+
+ "AIC-6260 SCSI Chip Specification", Adaptec Corporation.
+
+ "SCSI COMPUTER SYSTEM INTERFACE - 2 (SCSI-2)", X3T9.2/86-109 rev. 10h
+
+ "Writing a SCSI device driver for Linux", Rik Faith (faith@cs.unc.edu)
+
+ "Kernel Hacker's Guide", Michael K. Johnson (johnsonm@sunsite.unc.edu)
+
+ "Adaptec 1520/1522 User's Guide", Adaptec Corporation.
+
+ Michael K. Johnson (johnsonm@sunsite.unc.edu)
+
+ Drew Eckhardt (drew@cs.colorado.edu)
+
+ Eric Youngdale (ericy@cais.com)
+
+ special thanks to Eric Youngdale for the free(!) supplying the
+ documentation on the chip.
+
+ **************************************************************************/
+
+#ifdef MACH
+#define AUTOCONF 1
+#endif
+
+#ifdef PCMCIA
+#define MODULE
+#endif
+
+#include <linux/module.h>
+
+#ifdef PCMCIA
+#undef MODULE
+#endif
+
+#include <linux/sched.h>
+#include <asm/io.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "sd.h"
+#include "hosts.h"
+#include "constants.h"
+#include <asm/system.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/wait.h>
+#include <linux/ioport.h>
+#include <linux/proc_fs.h>
+
+#include "aha152x.h"
+#include <linux/stat.h>
+
+#include <scsi/scsicam.h>
+
+struct proc_dir_entry proc_scsi_aha152x = {
+ PROC_SCSI_AHA152X, 7, "aha152x",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+/* DEFINES */
+
+/* For PCMCIA cards, always use AUTOCONF */
+#if defined(PCMCIA) || defined(MODULE)
+#if !defined(AUTOCONF)
+#define AUTOCONF
+#endif
+#endif
+
+#if !defined(AUTOCONF) && !defined(SETUP0)
+#error define AUTOCONF or SETUP0
+#endif
+
+#if defined(DEBUG_AHA152X)
+
+#undef SKIP_PORTS /* don't display ports */
+
+#undef DEBUG_QUEUE /* debug queue() */
+#undef DEBUG_RESET /* debug reset() */
+#undef DEBUG_INTR /* debug intr() */
+#undef DEBUG_SELECTION /* debug selection part in intr() */
+#undef DEBUG_MSGO /* debug message out phase in intr() */
+#undef DEBUG_MSGI /* debug message in phase in intr() */
+#undef DEBUG_STATUS /* debug status phase in intr() */
+#undef DEBUG_CMD /* debug command phase in intr() */
+#undef DEBUG_DATAI /* debug data in phase in intr() */
+#undef DEBUG_DATAO /* debug data out phase in intr() */
+#undef DEBUG_ABORT /* debug abort() */
+#undef DEBUG_DONE /* debug done() */
+#undef DEBUG_BIOSPARAM /* debug biosparam() */
+
+#undef DEBUG_RACE /* debug race conditions */
+#undef DEBUG_PHASES /* debug phases (useful to trace) */
+#undef DEBUG_QUEUES /* debug reselection */
+
+/* recently used for debugging */
+#if 0
+#endif
+
+#define DEBUG_SELECTION
+#define DEBUG_PHASES
+#define DEBUG_RESET
+#define DEBUG_ABORT
+
+#define DEBUG_DEFAULT (debug_reset|debug_abort)
+
+#endif
+
+/* END OF DEFINES */
+
+extern unsigned long loops_per_sec;
+
+#define DELAY_DEFAULT 100
+
+/* some additional "phases" for getphase() */
+#define P_BUSFREE 1
+#define P_PARITY 2
+
+/* possible irq range */
+#define IRQ_MIN 9
+#define IRQ_MAX 12
+#define IRQS IRQ_MAX-IRQ_MIN+1
+
+enum {
+ not_issued = 0x0001,
+ in_selection = 0x0002,
+ disconnected = 0x0004,
+ aborted = 0x0008,
+ sent_ident = 0x0010,
+ in_other = 0x0020,
+ in_sync = 0x0040,
+ sync_ok = 0x0080,
+};
+
+#if defined(MODULE)
+#if defined(DEBUG_AHA152X)
+int aha152x[] = { 0, 11, 7, 1, 1, 0, DELAY_DEFAULT, 0, DEBUG_DEFAULT };
+int aha152x1[] = { 0, 11, 7, 1, 1, 0, DELAY_DEFAULT, 0, DEBUG_DEFAULT };
+#else
+int aha152x[] = { 0, 11, 7, 1, 1, 0, DELAY_DEFAULT, 0 };
+int aha152x1[] = { 0, 11, 7, 1, 1, 0, DELAY_DEFAULT, 0 };
+#endif
+#endif
+
+/* set by aha152x_setup according to the command line */
+static int setup_count=0;
+static struct aha152x_setup {
+ int io_port;
+ int irq;
+ int scsiid;
+ int reconnect;
+ int parity;
+ int synchronous;
+ int delay;
+ int ext_trans;
+#ifdef DEBUG_AHA152X
+ int debug;
+#endif
+ char *conf;
+} setup[2];
+
+static struct Scsi_Host *aha152x_host[IRQS];
+
+#define HOSTDATA(shpnt) ((struct aha152x_hostdata *) &shpnt->hostdata)
+#define CURRENT_SC (HOSTDATA(shpnt)->current_SC)
+#define ISSUE_SC (HOSTDATA(shpnt)->issue_SC)
+#define DISCONNECTED_SC (HOSTDATA(shpnt)->disconnected_SC)
+#define DELAY (HOSTDATA(shpnt)->delay)
+#define EXT_TRANS (HOSTDATA(shpnt)->ext_trans)
+#define SYNCRATE (HOSTDATA(shpnt)->syncrate[CURRENT_SC->target])
+#define MSG(i) (HOSTDATA(shpnt)->message[i])
+#define MSGLEN (HOSTDATA(shpnt)->message_len)
+#define ADDMSG(x) (MSG(MSGLEN++)=x)
+
+struct aha152x_hostdata {
+ Scsi_Cmnd *issue_SC;
+ Scsi_Cmnd *current_SC;
+ Scsi_Cmnd *disconnected_SC;
+ int aborting;
+ int abortion_complete;
+ int abort_result;
+ int commands;
+
+ int reconnect;
+ int parity;
+ int synchronous;
+ int delay;
+ int ext_trans;
+
+ int swint;
+
+ unsigned char syncrate[8];
+
+ unsigned char message[256];
+ int message_len;
+
+#ifdef DEBUG_AHA152X
+ int debug;
+#endif
+};
+
+void aha152x_intr(int irq, void *dev_id, struct pt_regs *);
+void aha152x_done(struct Scsi_Host *shpnt, int error);
+void aha152x_setup(char *str, int *ints);
+int aha152x_checksetup(struct aha152x_setup *setup);
+
+static void aha152x_reset_ports(struct Scsi_Host *shpnt);
+static void aha152x_panic(struct Scsi_Host *shpnt, char *msg);
+
+static void disp_ports(struct Scsi_Host *shpnt);
+static void show_command(Scsi_Cmnd *ptr);
+static void show_queues(struct Scsi_Host *shpnt);
+static void disp_enintr(struct Scsi_Host *shpnt);
+
+#if defined(DEBUG_RACE)
+static void enter_driver(const char *);
+static void leave_driver(const char *);
+#endif
+
+/* possible i/o addresses for the AIC-6260 */
+static unsigned short ports[] =
+{
+ 0x340, /* default first */
+ 0x140
+};
+#define PORT_COUNT (sizeof(ports) / sizeof(unsigned short))
+
+#if !defined(SKIP_BIOSTEST)
+/* possible locations for the Adaptec BIOS */
+static void *addresses[] =
+{
+ (void *) 0xdc000, /* default first */
+ (void *) 0xc8000,
+ (void *) 0xcc000,
+ (void *) 0xd0000,
+ (void *) 0xd4000,
+ (void *) 0xd8000,
+ (void *) 0xe0000,
+ (void *) 0xeb800, /* VTech Platinum SMP */
+ (void *) 0xf0000,
+};
+#define ADDRESS_COUNT (sizeof(addresses) / sizeof(void *))
+
+/* signatures for various AIC-6[23]60 based controllers.
+ The point in detecting signatures is to avoid useless and maybe
+ harmful probes on ports. I'm not sure that all listed boards pass
+ auto-configuration. For those which fail the BIOS signature is
+ obsolete, because user intervention to supply the configuration is
+ needed anyway. May be an information whether or not the BIOS supports
+ extended translation could be also useful here. */
+static struct signature {
+ char *signature;
+ int sig_offset;
+ int sig_length;
+} signatures[] =
+{
+ { "Adaptec AHA-1520 BIOS", 0x102e, 21 }, /* Adaptec 152x */
+ { "Adaptec AHA-1520B", 0x0b, 17 }, /* Adaptec 152x rev B */
+ { "Adaptec AHA-1520B/1522B", 0x3e20, 23 }, /* Adaptec 1520B/1522B */
+ { "Adaptec ASW-B626 BIOS", 0x1029, 21 }, /* on-board controller */
+ { "Adaptec BIOS: ASW-B626", 0x0f, 22 }, /* on-board controller */
+ { "Adaptec ASW-B626 S2", 0x2e6c, 19 }, /* on-board controller */
+ { "Adaptec BIOS:AIC-6360", 0xc, 21 }, /* on-board controller */
+ { "ScsiPro SP-360 BIOS", 0x2873, 19 }, /* ScsiPro-Controller */
+ { "GA-400 LOCAL BUS SCSI BIOS", 0x102e, 26 }, /* Gigabyte Local-Bus-SCSI */
+ { "Adaptec BIOS:AVA-282X", 0xc, 21 }, /* Adaptec 282x */
+ { "Adaptec IBM Dock II SCSI", 0x2edd, 24 }, /* IBM Thinkpad Dock II */
+ { "Adaptec BIOS:AHA-1532P", 0x1c, 22 }, /* IBM Thinkpad Dock II SCSI */
+};
+#define SIGNATURE_COUNT (sizeof(signatures) / sizeof(struct signature))
+#endif
+
+
+static void do_pause(unsigned amount) /* Pause for amount*10 milliseconds */
+{
+ unsigned long the_time = jiffies + amount; /* 0.01 seconds per jiffy */
+
+ while (jiffies < the_time)
+ barrier();
+}
+
+/*
+ * queue services:
+ */
+static inline void append_SC(Scsi_Cmnd **SC, Scsi_Cmnd *new_SC)
+{
+ Scsi_Cmnd *end;
+
+ new_SC->host_scribble = (unsigned char *) NULL;
+ if(!*SC)
+ *SC=new_SC;
+ else {
+ for(end=*SC; end->host_scribble; end = (Scsi_Cmnd *) end->host_scribble)
+ ;
+ end->host_scribble = (unsigned char *) new_SC;
+ }
+}
+
+static inline Scsi_Cmnd *remove_first_SC(Scsi_Cmnd **SC)
+{
+ Scsi_Cmnd *ptr;
+
+ ptr=*SC;
+ if(ptr)
+ *SC= (Scsi_Cmnd *) (*SC)->host_scribble;
+ return ptr;
+}
+
+static inline Scsi_Cmnd *remove_SC(Scsi_Cmnd **SC, int target, int lun)
+{
+ Scsi_Cmnd *ptr, *prev;
+
+ for(ptr=*SC, prev=NULL;
+ ptr && ((ptr->target!=target) || (ptr->lun!=lun));
+ prev = ptr, ptr = (Scsi_Cmnd *) ptr->host_scribble)
+ ;
+
+ if(ptr){
+ if(prev)
+ prev->host_scribble = ptr->host_scribble;
+ else
+ *SC= (Scsi_Cmnd *) ptr->host_scribble;
+ }
+
+ return ptr;
+}
+
+/*
+ * read inbound byte and wait for ACK to get low
+ */
+static void make_acklow(struct Scsi_Host *shpnt)
+{
+ SETPORT(SXFRCTL0, CH1|SPIOEN);
+ GETPORT(SCSIDAT);
+ SETPORT(SXFRCTL0, CH1);
+
+ while(TESTHI(SCSISIG, ACKI))
+ barrier();
+}
+
+/*
+ * detect current phase more reliable:
+ * phase is valid, when the target asserts REQ after we've deasserted ACK.
+ *
+ * return value is a valid phase or an error code.
+ *
+ * errorcodes:
+ * P_BUSFREE BUS FREE phase detected
+ * P_PARITY parity error in DATA phase
+ */
+static int getphase(struct Scsi_Host *shpnt)
+{
+ int phase, sstat1;
+
+ while(1) {
+ do {
+ while(!((sstat1 = GETPORT(SSTAT1)) & (BUSFREE|SCSIRSTI|REQINIT)))
+ barrier();
+ if(sstat1 & BUSFREE)
+ return P_BUSFREE;
+ if(sstat1 & SCSIRSTI) {
+ printk("aha152x: RESET IN\n");
+ SETPORT(SSTAT1, SCSIRSTI);
+ }
+ } while(TESTHI(SCSISIG, ACKI) || TESTLO(SSTAT1, REQINIT));
+
+ SETPORT(SSTAT1, CLRSCSIPERR);
+
+ phase = GETPORT(SCSISIG) & P_MASK ;
+
+ if(TESTHI(SSTAT1, SCSIPERR)) {
+ if((phase & (CDO|MSGO))==0) /* DATA phase */
+ return P_PARITY;
+
+ make_acklow(shpnt);
+ } else
+ return phase;
+ }
+}
+
+/* called from init/main.c */
+void aha152x_setup(char *str, int *ints)
+{
+ if(setup_count>2)
+ panic("aha152x: you can only configure up to two controllers\n");
+
+ setup[setup_count].conf = str;
+ setup[setup_count].io_port = ints[0] >= 1 ? ints[1] : 0x340;
+ setup[setup_count].irq = ints[0] >= 2 ? ints[2] : 11;
+ setup[setup_count].scsiid = ints[0] >= 3 ? ints[3] : 7;
+ setup[setup_count].reconnect = ints[0] >= 4 ? ints[4] : 1;
+ setup[setup_count].parity = ints[0] >= 5 ? ints[5] : 1;
+ setup[setup_count].synchronous = ints[0] >= 6 ? ints[6] : 0 /* FIXME: 1 */;
+ setup[setup_count].delay = ints[0] >= 7 ? ints[7] : DELAY_DEFAULT;
+ setup[setup_count].ext_trans = ints[0] >= 8 ? ints[8] : 0;
+#ifdef DEBUG_AHA152X
+ setup[setup_count].debug = ints[0] >= 9 ? ints[9] : DEBUG_DEFAULT;
+ if(ints[0]>9) {
+ printk("aha152x: usage: aha152x=<IOBASE>[,<IRQ>[,<SCSI ID>"
+ "[,<RECONNECT>[,<PARITY>[,<SYNCHRONOUS>[,<DELAY>[,<EXT_TRANS>[,<DEBUG>]]]]]]]]\n");
+#else
+ if(ints[0]>8) {
+ printk("aha152x: usage: aha152x=<IOBASE>[,<IRQ>[,<SCSI ID>"
+ "[,<RECONNECT>[,<PARITY>[,<SYNCHRONOUS>[,<DELAY>[,<EXT_TRANS>]]]]]]]\n");
+#endif
+ } else
+ setup_count++;
+}
+
+/*
+ * Test, if port_base is valid.
+ */
+static int aha152x_porttest(int io_port)
+{
+ int i;
+
+ if(check_region(io_port, IO_RANGE))
+ return 0;
+
+ SETPORT(io_port+O_DMACNTRL1, 0); /* reset stack pointer */
+ for(i=0; i<16; i++)
+ SETPORT(io_port+O_STACK, i);
+
+ SETPORT(io_port+O_DMACNTRL1, 0); /* reset stack pointer */
+ for(i=0; i<16 && GETPORT(io_port+O_STACK)==i; i++)
+ ;
+
+ return(i==16);
+}
+
+int aha152x_checksetup(struct aha152x_setup *setup)
+{
+ int i;
+
+#ifndef PCMCIA
+ for(i=0; i<PORT_COUNT && (setup->io_port != ports[i]); i++)
+ ;
+
+ if(i==PORT_COUNT)
+ return 0;
+#endif
+
+ if(!aha152x_porttest(setup->io_port))
+ return 0;
+
+ if(setup->irq<IRQ_MIN && setup->irq>IRQ_MAX)
+ return 0;
+
+ if((setup->scsiid < 0) || (setup->scsiid > 7))
+ return 0;
+
+ if((setup->reconnect < 0) || (setup->reconnect > 1))
+ return 0;
+
+ if((setup->parity < 0) || (setup->parity > 1))
+ return 0;
+
+ if((setup->synchronous < 0) || (setup->synchronous > 1))
+ return 0;
+
+ if((setup->ext_trans < 0) || (setup->ext_trans > 1))
+ return 0;
+
+
+ return 1;
+}
+
+void aha152x_swintr(int irqno, void *dev_id, struct pt_regs * regs)
+{
+ struct Scsi_Host *shpnt = aha152x_host[irqno-IRQ_MIN];
+
+ if(!shpnt)
+ panic("aha152x: catched software interrupt for unknown controller.\n");
+
+ HOSTDATA(shpnt)->swint++;
+}
+
+
+int aha152x_detect(Scsi_Host_Template * tpnt)
+{
+ int i, j, ok;
+#if defined(AUTOCONF)
+ aha152x_config conf;
+#endif
+
+ tpnt->proc_dir = &proc_scsi_aha152x;
+
+ for(i=0; i<IRQS; i++)
+ aha152x_host[i] = (struct Scsi_Host *) NULL;
+
+ if(setup_count) {
+ printk("aha152x: processing commandline: ");
+
+ for(i=0; i<setup_count; i++)
+ if(!aha152x_checksetup(&setup[i])) {
+ printk("\naha152x: %s\n", setup[i].conf);
+ printk("aha152x: invalid line (controller=%d)\n", i+1);
+ }
+
+ printk("ok\n");
+ }
+
+#ifdef SETUP0
+ if(setup_count<2) {
+ struct aha152x_setup override = SETUP0;
+
+ if(setup_count==0 || (override.io_port != setup[0].io_port))
+ if(!aha152x_checksetup(&override)) {
+ printk("\naha152x: invalid override SETUP0={0x%x,%d,%d,%d,%d,%d,%d,%d}\n",
+ override.io_port,
+ override.irq,
+ override.scsiid,
+ override.reconnect,
+ override.parity,
+ override.synchronous,
+ override.delay,
+ override.ext_trans);
+ } else
+ setup[setup_count++] = override;
+ }
+#endif
+
+#ifdef SETUP1
+ if(setup_count<2) {
+ struct aha152x_setup override = SETUP1;
+
+ if(setup_count==0 || (override.io_port != setup[0].io_port))
+ if(!aha152x_checksetup(&override)) {
+ printk("\naha152x: invalid override SETUP1={0x%x,%d,%d,%d,%d,%d,%d,%d}\n",
+ override.io_port,
+ override.irq,
+ override.scsiid,
+ override.reconnect,
+ override.parity,
+ override.synchronous,
+ override.delay,
+ override.ext_trans);
+ } else
+ setup[setup_count++] = override;
+ }
+#endif
+
+#if defined(MODULE)
+ if(setup_count<2 && aha152x[0]!=0) {
+ setup[setup_count].conf = "";
+ setup[setup_count].io_port = aha152x[0];
+ setup[setup_count].irq = aha152x[1];
+ setup[setup_count].scsiid = aha152x[2];
+ setup[setup_count].reconnect = aha152x[3];
+ setup[setup_count].parity = aha152x[4];
+ setup[setup_count].synchronous = aha152x[5];
+ setup[setup_count].delay = aha152x[6];
+ setup[setup_count].ext_trans = aha152x[7];
+#ifdef DEBUG_AHA152X
+ setup[setup_count].debug = aha152x[8];
+#endif
+ if(aha152x_checksetup(&setup[setup_count]))
+ setup_count++;
+ else
+ printk("\naha152x: invalid module argument aha152x=0x%x,%d,%d,%d,%d,%d,%d,%d\n",
+ setup[setup_count].io_port,
+ setup[setup_count].irq,
+ setup[setup_count].scsiid,
+ setup[setup_count].reconnect,
+ setup[setup_count].parity,
+ setup[setup_count].synchronous,
+ setup[setup_count].delay,
+ setup[setup_count].ext_trans);
+ }
+
+ if(setup_count<2 && aha152x1[0]!=0) {
+ setup[setup_count].conf = "";
+ setup[setup_count].io_port = aha152x1[0];
+ setup[setup_count].irq = aha152x1[1];
+ setup[setup_count].scsiid = aha152x1[2];
+ setup[setup_count].reconnect = aha152x1[3];
+ setup[setup_count].parity = aha152x1[4];
+ setup[setup_count].synchronous = aha152x1[5];
+ setup[setup_count].delay = aha152x1[6];
+ setup[setup_count].ext_trans = aha152x1[7];
+#ifdef DEBUG_AHA152X
+ setup[setup_count].debug = aha152x1[8];
+#endif
+ if(aha152x_checksetup(&setup[setup_count]))
+ setup_count++;
+ else
+ printk("\naha152x: invalid module argument aha152x1=0x%x,%d,%d,%d,%d,%d,%d,%d\n",
+ setup[setup_count].io_port,
+ setup[setup_count].irq,
+ setup[setup_count].scsiid,
+ setup[setup_count].reconnect,
+ setup[setup_count].parity,
+ setup[setup_count].synchronous,
+ setup[setup_count].delay,
+ setup[setup_count].ext_trans);
+ }
+#endif
+
+#if defined(AUTOCONF)
+ if(setup_count<2) {
+#if !defined(SKIP_BIOSTEST)
+ ok=0;
+ for(i=0; i < ADDRESS_COUNT && !ok; i++)
+ for(j=0; (j < SIGNATURE_COUNT) && !ok; j++)
+ ok=!memcmp((void *) addresses[i]+signatures[j].sig_offset,
+ (void *) signatures[j].signature,
+ (int) signatures[j].sig_length);
+
+ if(!ok && setup_count==0)
+ return 0;
+
+ printk("aha152x: BIOS test: passed, ");
+#else
+ printk("aha152x: ");
+#endif /* !SKIP_BIOSTEST */
+
+ ok=0;
+ for(i=0; i<PORT_COUNT && setup_count<2; i++) {
+ if((setup_count==1) && (setup[0].io_port == ports[i]))
+ continue;
+
+ if(aha152x_porttest(ports[i])) {
+ ok++;
+ setup[setup_count].io_port = ports[i];
+
+ conf.cf_port =
+ (GETPORT(ports[i]+O_PORTA)<<8) + GETPORT(ports[i]+O_PORTB);
+
+ setup[setup_count].irq = IRQ_MIN + conf.cf_irq;
+ setup[setup_count].scsiid = conf.cf_id;
+ setup[setup_count].reconnect = conf.cf_tardisc;
+ setup[setup_count].parity = !conf.cf_parity;
+ setup[setup_count].synchronous = 0 /* FIXME: conf.cf_syncneg */;
+ setup[setup_count].delay = DELAY_DEFAULT;
+ setup[setup_count].ext_trans = 0;
+#ifdef DEBUG_AHA152X
+ setup[setup_count].debug = DEBUG_DEFAULT;
+#endif
+ setup_count++;
+ }
+ }
+
+ if(ok)
+ printk("auto configuration: ok, ");
+ }
+#endif
+
+ printk("detected %d controller(s)\n", setup_count);
+
+ for(i=0; i<setup_count; i++) {
+ struct Scsi_Host *shpnt;
+ unsigned long int the_time;
+
+ shpnt = aha152x_host[setup[i].irq-IRQ_MIN] =
+ scsi_register(tpnt, sizeof(struct aha152x_hostdata));
+
+ shpnt->io_port = setup[i].io_port;
+ shpnt->n_io_port = IO_RANGE;
+ shpnt->irq = setup[i].irq;
+
+ ISSUE_SC = (Scsi_Cmnd *) NULL;
+ CURRENT_SC = (Scsi_Cmnd *) NULL;
+ DISCONNECTED_SC = (Scsi_Cmnd *) NULL;
+
+ HOSTDATA(shpnt)->reconnect = setup[i].reconnect;
+ HOSTDATA(shpnt)->parity = setup[i].parity;
+ HOSTDATA(shpnt)->synchronous = setup[i].synchronous;
+ HOSTDATA(shpnt)->delay = setup[i].delay;
+ HOSTDATA(shpnt)->ext_trans = setup[i].ext_trans;
+#ifdef DEBUG_AHA152X
+ HOSTDATA(shpnt)->debug = setup[i].debug;
+#endif
+
+ HOSTDATA(shpnt)->aborting = 0;
+ HOSTDATA(shpnt)->abortion_complete = 0;
+ HOSTDATA(shpnt)->abort_result = 0;
+ HOSTDATA(shpnt)->commands = 0;
+
+ HOSTDATA(shpnt)->message_len = 0;
+
+ for(j=0; j<8; j++)
+ HOSTDATA(shpnt)->syncrate[j] = 0;
+
+ SETPORT(SCSIID, setup[i].scsiid << 4);
+ shpnt->this_id=setup[i].scsiid;
+
+ if(setup[i].reconnect)
+ shpnt->can_queue=AHA152X_MAXQUEUE;
+
+ /* RESET OUT */
+ SETBITS(SCSISEQ, SCSIRSTO);
+ do_pause(30);
+ CLRBITS(SCSISEQ, SCSIRSTO);
+ do_pause(setup[i].delay);
+
+ aha152x_reset_ports(shpnt);
+
+ printk("aha152x%d: vital data: PORTBASE=0x%03x, IRQ=%d, SCSI ID=%d,"
+ " reconnect=%s, parity=%s, synchronous=%s, delay=%d, extended translation=%s\n",
+ i,
+ shpnt->io_port,
+ shpnt->irq,
+ shpnt->this_id,
+ HOSTDATA(shpnt)->reconnect ? "enabled" : "disabled",
+ HOSTDATA(shpnt)->parity ? "enabled" : "disabled",
+ HOSTDATA(shpnt)->synchronous ? "enabled" : "disabled",
+ HOSTDATA(shpnt)->delay,
+ HOSTDATA(shpnt)->ext_trans ? "enabled" : "disabled");
+
+ request_region(shpnt->io_port, IO_RANGE, "aha152x"); /* Register */
+
+ /* not expecting any interrupts */
+ SETPORT(SIMODE0, 0);
+ SETPORT(SIMODE1, 0);
+
+ SETBITS(DMACNTRL0, INTEN);
+
+ ok = request_irq(shpnt->irq, aha152x_swintr, SA_INTERRUPT, "aha152x", NULL);
+ if(ok<0) {
+ if(ok == -EINVAL)
+ printk("aha152x%d: bad IRQ %d.\n", i, shpnt->irq);
+ else if(ok == -EBUSY)
+ printk("aha152x%d: IRQ %d already in use.\n", i, shpnt->irq);
+ else
+ printk("\naha152x%d: Unexpected error code %d on requesting IRQ %d.\n", i, ok, shpnt->irq);
+ printk("aha152x: driver needs an IRQ.\n");
+
+ scsi_unregister(shpnt);
+ shpnt=aha152x_host[shpnt->irq-IRQ_MIN]=0;
+ continue;
+ }
+
+ HOSTDATA(shpnt)->swint=0;
+
+ printk("aha152x: trying software interrupt, ");
+ SETBITS(DMACNTRL0, SWINT);
+
+ the_time=jiffies+100;
+ while(!HOSTDATA(shpnt)->swint && jiffies<the_time)
+ barrier();
+
+ free_irq(shpnt->irq,0);
+
+ if(!HOSTDATA(shpnt)->swint) {
+ if(TESTHI(DMASTAT, INTSTAT)) {
+ printk("lost.\n");
+ } else {
+ printk("failed.\n");
+ }
+
+ printk("aha152x: IRQ %d possibly wrong. Please verify.\n", shpnt->irq);
+
+ scsi_unregister(shpnt);
+ shpnt=aha152x_host[shpnt->irq-IRQ_MIN]=0;
+ continue;
+ }
+
+ printk("ok.\n");
+
+ CLRBITS(DMACNTRL0, SWINT);
+
+ /* clear interrupts */
+ SETPORT(SSTAT0, 0x7f);
+ SETPORT(SSTAT1, 0xef);
+
+ if(request_irq(shpnt->irq,aha152x_intr,SA_INTERRUPT,"aha152x",NULL)<0) {
+ printk("aha152x: failed to reassign interrupt.\n");
+ }
+ }
+
+ return (setup_count>0);
+}
+
+/*
+ * Queue a command and setup interrupts for a free bus.
+ */
+int aha152x_queue(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
+{
+ struct Scsi_Host *shpnt = SCpnt->host;
+ unsigned long flags;
+
+#if defined(DEBUG_RACE)
+ enter_driver("queue");
+#else
+#if defined(DEBUG_QUEUE)
+ if(HOSTDATA(shpnt)->debug & debug_queue)
+ printk("aha152x: queue(), ");
+#endif
+#endif
+
+#if defined(DEBUG_QUEUE)
+ if(HOSTDATA(shpnt)->debug & debug_queue) {
+ printk("SCpnt (target = %d lun = %d cmnd = ",
+ SCpnt->target, SCpnt->lun);
+ print_command(SCpnt->cmnd);
+ printk(", cmd_len=%d, pieces = %d size = %u), ",
+ SCpnt->cmd_len, SCpnt->use_sg, SCpnt->request_bufflen);
+ disp_ports(shpnt);
+ }
+#endif
+
+ SCpnt->scsi_done = done;
+
+ /* setup scratch area
+ SCp.ptr : buffer pointer
+ SCp.this_residual : buffer length
+ SCp.buffer : next buffer
+ SCp.buffers_residual : left buffers in list
+ SCp.phase : current state of the command */
+ SCpnt->SCp.phase = not_issued;
+ if (SCpnt->use_sg) {
+ SCpnt->SCp.buffer = (struct scatterlist *) SCpnt->request_buffer;
+ SCpnt->SCp.ptr = SCpnt->SCp.buffer->address;
+ SCpnt->SCp.this_residual = SCpnt->SCp.buffer->length;
+ SCpnt->SCp.buffers_residual = SCpnt->use_sg - 1;
+ } else {
+ SCpnt->SCp.ptr = (char *)SCpnt->request_buffer;
+ SCpnt->SCp.this_residual = SCpnt->request_bufflen;
+ SCpnt->SCp.buffer = NULL;
+ SCpnt->SCp.buffers_residual = 0;
+ }
+
+ SCpnt->SCp.Status = CHECK_CONDITION;
+ SCpnt->SCp.Message = 0;
+ SCpnt->SCp.have_data_in = 0;
+ SCpnt->SCp.sent_command = 0;
+
+ /* Turn led on, when this is the first command. */
+ save_flags(flags);
+ cli();
+ HOSTDATA(shpnt)->commands++;
+ if(HOSTDATA(shpnt)->commands==1)
+ SETPORT(PORTA, 1);
+
+#if defined(DEBUG_QUEUES)
+ if(HOSTDATA(shpnt)->debug & debug_queues)
+ printk("i+ (%d), ", HOSTDATA(shpnt)->commands);
+#endif
+ append_SC(&ISSUE_SC, SCpnt);
+
+ /* Enable bus free interrupt, when we aren't currently on the bus */
+ if(!CURRENT_SC) {
+ SETPORT(SIMODE0, DISCONNECTED_SC ? ENSELDI : 0);
+ SETPORT(SIMODE1, ISSUE_SC ? ENBUSFREE : 0);
+ }
+ restore_flags(flags);
+
+#if defined(DEBUG_RACE)
+ leave_driver("queue");
+#endif
+
+ return 0;
+}
+
+/*
+ * We only support commands in interrupt-driven fashion
+ */
+int aha152x_command(Scsi_Cmnd *SCpnt)
+{
+ printk("aha152x: interrupt driven driver; use aha152x_queue()\n");
+ return -1;
+}
+
+/*
+ * Abort a queued command
+ * (commands that are on the bus can't be aborted easily)
+ */
+int aha152x_abort(Scsi_Cmnd *SCpnt)
+{
+ struct Scsi_Host *shpnt = SCpnt->host;
+ unsigned long flags;
+ Scsi_Cmnd *ptr, *prev;
+
+ save_flags(flags);
+ cli();
+
+#if defined(DEBUG_ABORT)
+ if(HOSTDATA(shpnt)->debug & debug_abort) {
+ printk("aha152x: abort(), SCpnt=0x%08x, ", (unsigned int) SCpnt);
+ show_queues(shpnt);
+ }
+#endif
+
+ /* look for command in issue queue */
+ for(ptr=ISSUE_SC, prev=NULL;
+ ptr && ptr!=SCpnt;
+ prev=ptr, ptr=(Scsi_Cmnd *) ptr->host_scribble)
+ ;
+
+ if(ptr) {
+ /* dequeue */
+ if(prev)
+ prev->host_scribble = ptr->host_scribble;
+ else
+ ISSUE_SC = (Scsi_Cmnd *) ptr->host_scribble;
+
+ HOSTDATA(shpnt)->commands--;
+
+ restore_flags(flags);
+
+ ptr->host_scribble = NULL;
+ ptr->result = DID_ABORT << 16;
+ ptr->scsi_done(ptr);
+
+ return SCSI_ABORT_SUCCESS;
+ }
+
+ /* if the bus is busy or a command is currently processed,
+ we can't do anything more */
+ if (TESTLO(SSTAT1, BUSFREE) || (CURRENT_SC && CURRENT_SC!=SCpnt)) {
+ /* fail abortion, if bus is busy */
+
+ if(!CURRENT_SC)
+ printk("bus busy w/o current command, ");
+
+ restore_flags(flags);
+
+ return SCSI_ABORT_BUSY;
+ }
+
+ /* bus is free */
+
+ if(CURRENT_SC) {
+ HOSTDATA(shpnt)->commands--;
+
+ /* target entered bus free before COMMAND COMPLETE, nothing to abort */
+ restore_flags(flags);
+ CURRENT_SC->result = DID_ERROR << 16;
+ CURRENT_SC->scsi_done(CURRENT_SC);
+ CURRENT_SC = (Scsi_Cmnd *) NULL;
+
+ return SCSI_ABORT_SUCCESS;
+ }
+
+ /* look for command in disconnected queue */
+ for(ptr=DISCONNECTED_SC, prev=NULL;
+ ptr && ptr!=SCpnt;
+ prev=ptr, ptr=(Scsi_Cmnd *) ptr->host_scribble)
+ ;
+
+ if(!ptr) {
+ /* command wasn't found */
+ printk("command not found\n");
+ restore_flags(flags);
+
+ return SCSI_ABORT_NOT_RUNNING;
+ }
+
+ if(!HOSTDATA(shpnt)->aborting) {
+ /* dequeue */
+ if(prev)
+ prev->host_scribble = ptr->host_scribble;
+ else
+ DISCONNECTED_SC = (Scsi_Cmnd *) ptr->host_scribble;
+
+ HOSTDATA(shpnt)->commands--;
+
+ /* set command current and initiate selection,
+ let the interrupt routine take care of the abortion */
+ CURRENT_SC = ptr;
+ ptr->SCp.phase = in_selection|aborted;
+ SETPORT(SCSIID, (shpnt->this_id << OID_) | CURRENT_SC->target);
+
+ ADDMSG(ABORT);
+
+ /* enable interrupts for SELECTION OUT DONE and SELECTION TIME OUT */
+ SETPORT(SIMODE0, ENSELDO | (DISCONNECTED_SC ? ENSELDI : 0));
+ SETPORT(SIMODE1, ENSELTIMO);
+
+ /* Enable SELECTION OUT sequence */
+ SETBITS(SCSISEQ, ENSELO | ENAUTOATNO);
+
+ SETBITS(DMACNTRL0, INTEN);
+ HOSTDATA(shpnt)->abort_result=SCSI_ABORT_SUCCESS;
+ HOSTDATA(shpnt)->aborting++;
+ HOSTDATA(shpnt)->abortion_complete=0;
+
+ sti(); /* Hi Eric, guess what ;-) */
+
+ /* sleep until the abortion is complete */
+ while(!HOSTDATA(shpnt)->abortion_complete)
+ barrier();
+ HOSTDATA(shpnt)->aborting=0;
+
+ return HOSTDATA(shpnt)->abort_result;
+ } else {
+ /* we're already aborting a command */
+ restore_flags(flags);
+
+ return SCSI_ABORT_BUSY;
+ }
+}
+
+/*
+ * Restore default values to the AIC-6260 registers and reset the fifos
+ */
+static void aha152x_reset_ports(struct Scsi_Host *shpnt)
+{
+ /* disable interrupts */
+ SETPORT(DMACNTRL0, RSTFIFO);
+
+ SETPORT(SCSISEQ, 0);
+
+ SETPORT(SXFRCTL1, 0);
+ SETPORT(SCSISIG, 0);
+ SETPORT(SCSIRATE, 0);
+
+ /* clear all interrupt conditions */
+ SETPORT(SSTAT0, 0x7f);
+ SETPORT(SSTAT1, 0xef);
+
+ SETPORT(SSTAT4, SYNCERR|FWERR|FRERR);
+
+ SETPORT(DMACNTRL0, 0);
+ SETPORT(DMACNTRL1, 0);
+
+ SETPORT(BRSTCNTRL, 0xf1);
+
+ /* clear SCSI fifo and transfer count */
+ SETPORT(SXFRCTL0, CH1|CLRCH1|CLRSTCNT);
+ SETPORT(SXFRCTL0, CH1);
+
+ /* enable interrupts */
+ SETPORT(SIMODE0, DISCONNECTED_SC ? ENSELDI : 0);
+ SETPORT(SIMODE1, ISSUE_SC ? ENBUSFREE : 0);
+}
+
+/*
+ * Reset registers, reset a hanging bus and
+ * kill active and disconnected commands for target w/o soft reset
+ */
+int aha152x_reset(Scsi_Cmnd *SCpnt, unsigned int unused)
+{
+ struct Scsi_Host *shpnt = SCpnt->host;
+ unsigned long flags;
+ Scsi_Cmnd *ptr, *prev, *next;
+
+ aha152x_reset_ports(shpnt);
+
+ /* Reset, if bus hangs */
+ if(TESTLO(SSTAT1, BUSFREE)) {
+ CLRBITS(DMACNTRL0, INTEN);
+
+#if defined(DEBUG_RESET)
+ if(HOSTDATA(shpnt)->debug & debug_reset) {
+ printk("aha152x: reset(), bus not free: SCSI RESET OUT\n");
+ show_queues(shpnt);
+ }
+#endif
+
+ ptr=CURRENT_SC;
+ if(ptr && !ptr->device->soft_reset) {
+ ptr->host_scribble = NULL;
+ ptr->result = DID_RESET << 16;
+ ptr->scsi_done(CURRENT_SC);
+ CURRENT_SC=NULL;
+ }
+
+ save_flags(flags);
+ cli();
+ prev=NULL; ptr=DISCONNECTED_SC;
+ while(ptr) {
+ if(!ptr->device->soft_reset) {
+ if(prev)
+ prev->host_scribble = ptr->host_scribble;
+ else
+ DISCONNECTED_SC = (Scsi_Cmnd *) ptr->host_scribble;
+
+ next = (Scsi_Cmnd *) ptr->host_scribble;
+
+ ptr->host_scribble = NULL;
+ ptr->result = DID_RESET << 16;
+ ptr->scsi_done(ptr);
+
+ ptr = next;
+ } else {
+ prev=ptr;
+ ptr = (Scsi_Cmnd *) ptr->host_scribble;
+ }
+ }
+ restore_flags(flags);
+
+#if defined(DEBUG_RESET)
+ if(HOSTDATA(shpnt)->debug & debug_reset) {
+ printk("commands on targets w/ soft-resets:\n");
+ show_queues(shpnt);
+ }
+#endif
+
+ /* RESET OUT */
+ SETPORT(SCSISEQ, SCSIRSTO);
+ do_pause(30);
+ SETPORT(SCSISEQ, 0);
+ do_pause(DELAY);
+
+ SETPORT(SIMODE0, DISCONNECTED_SC ? ENSELDI : 0);
+ SETPORT(SIMODE1, ISSUE_SC ? ENBUSFREE : 0);
+
+ SETPORT(DMACNTRL0, INTEN);
+ }
+
+ return SCSI_RESET_SUCCESS;
+}
+
+/*
+ * Return the "logical geometry"
+ */
+int aha152x_biosparam(Scsi_Disk * disk, kdev_t dev, int *info_array)
+{
+ struct Scsi_Host *shpnt=disk->device->host;
+
+#if defined(DEBUG_BIOSPARAM)
+ if(HOSTDATA(shpnt)->debug & debug_biosparam)
+ printk("aha152x_biosparam: dev=%s, size=%d, ",
+ kdevname(dev), disk->capacity);
+#endif
+
+ /* try default translation */
+ info_array[0]=64;
+ info_array[1]=32;
+ info_array[2]=disk->capacity / (64 * 32);
+
+ /* for disks >1GB do some guessing */
+ if(info_array[2]>=1024) {
+ int info[3];
+
+ /* try to figure out the geometry from the partition table */
+ if(scsicam_bios_param(disk, dev, info)<0 ||
+ !((info[0]==64 && info[1]==32) || (info[0]==255 && info[1]==63))) {
+ if(EXT_TRANS) {
+ printk("aha152x: unable to verify geometry for disk with >1GB.\n"
+ " using extended translation.\n");
+ info_array[0] = 255;
+ info_array[1] = 63;
+ info_array[2] = disk->capacity / (255 * 63);
+ } else {
+ printk("aha152x: unable to verify geometry for disk with >1GB.\n"
+ " Using default translation. Please verify yourself.\n"
+ " Perhaps you need to enable extended translation in the driver.\n"
+ " See /usr/src/linux/drivers/scsi/aha152x.c for details.\n");
+ }
+ } else {
+ info_array[0]=info[0];
+ info_array[1]=info[1];
+ info_array[2]=info[2];
+
+ if(info[0]==255 && !EXT_TRANS) {
+ printk("aha152x: current partition table is using extended translation.\n"
+ " using it also, although it's not explicty enabled.\n");
+ }
+ }
+ }
+
+#if defined(DEBUG_BIOSPARAM)
+ if(HOSTDATA(shpnt)->debug & debug_biosparam) {
+ printk("bios geometry: head=%d, sec=%d, cyl=%d\n",
+ info_array[0], info_array[1], info_array[2]);
+ printk("WARNING: check, if the bios geometry is correct.\n");
+ }
+#endif
+
+ return 0;
+}
+
+/*
+ * Internal done function
+ */
+void aha152x_done(struct Scsi_Host *shpnt, int error)
+{
+ unsigned long flags;
+ Scsi_Cmnd *done_SC;
+
+#if defined(DEBUG_DONE)
+ if(HOSTDATA(shpnt)->debug & debug_done) {
+ printk("\naha152x: done(), ");
+ disp_ports(shpnt);
+ }
+#endif
+
+ if(CURRENT_SC) {
+#if defined(DEBUG_DONE)
+ if(HOSTDATA(shpnt)->debug & debug_done)
+ printk("done(%x), ", error);
+#endif
+
+ save_flags(flags);
+ cli();
+
+ done_SC = CURRENT_SC;
+ CURRENT_SC = NULL;
+
+ /* turn led off, when no commands are in the driver */
+ HOSTDATA(shpnt)->commands--;
+ if(!HOSTDATA(shpnt)->commands)
+ SETPORT(PORTA, 0); /* turn led off */
+
+#if defined(DEBUG_QUEUES)
+ if(HOSTDATA(shpnt)->debug & debug_queues)
+ printk("ok (%d), ", HOSTDATA(shpnt)->commands);
+#endif
+ restore_flags(flags);
+
+ SETPORT(SIMODE0, DISCONNECTED_SC ? ENSELDI : 0);
+ SETPORT(SIMODE1, ISSUE_SC ? ENBUSFREE : 0);
+
+#if 0
+/* Why poll for the BUS FREE phase, when we have setup the interrupt!? */
+#if defined(DEBUG_PHASES)
+ if(HOSTDATA(shpnt)->debug & debug_phases)
+ printk("BUS FREE loop, ");
+#endif
+ while(TESTLO(SSTAT1, BUSFREE))
+ barrier();
+#if defined(DEBUG_PHASES)
+ if(HOSTDATA(shpnt)->debug & debug_phases)
+ printk("BUS FREE\n");
+#endif
+#endif
+
+ done_SC->result = error;
+ if(done_SC->scsi_done) {
+#if defined(DEBUG_DONE)
+ if(HOSTDATA(shpnt)->debug & debug_done)
+ printk("calling scsi_done, ");
+#endif
+ done_SC->scsi_done(done_SC);
+#if defined(DEBUG_DONE)
+ if(HOSTDATA(shpnt)->debug & debug_done)
+ printk("done returned, ");
+#endif
+ } else
+ panic("aha152x: current_SC->scsi_done() == NULL");
+ } else
+ aha152x_panic(shpnt, "done() called outside of command");
+}
+
+/*
+ * Interrupts handler (main routine of the driver)
+ */
+void aha152x_intr(int irqno, void *dev_id, struct pt_regs * regs)
+{
+ struct Scsi_Host *shpnt = aha152x_host[irqno-IRQ_MIN];
+ unsigned int flags;
+ int done=0, phase;
+
+#if defined(DEBUG_RACE)
+ enter_driver("intr");
+#else
+#if defined(DEBUG_INTR)
+ if(HOSTDATA(shpnt)->debug & debug_intr)
+ printk("\naha152x: intr(), ");
+#endif
+#endif
+
+ if(!shpnt)
+ panic("aha152x: catched interrupt for unknown controller.\n");
+
+ /* no more interrupts from the controller, while we're busy.
+ INTEN has to be restored, when we're ready to leave
+ intr(). To avoid race conditions, we have to return
+ immediately afterwards. */
+ CLRBITS(DMACNTRL0, INTEN);
+ sti(); /* Yes, sti() really needs to be here */
+
+ /* disconnected target is trying to reconnect.
+ Only possible, if we have disconnected nexuses and
+ nothing is occupying the bus.
+ */
+ if(TESTHI(SSTAT0, SELDI) &&
+ DISCONNECTED_SC &&
+ (!CURRENT_SC || (CURRENT_SC->SCp.phase & in_selection)) ) {
+ int identify_msg, target, i;
+
+ /* Avoid conflicts when a target reconnects
+ while we are trying to connect to another. */
+ if(CURRENT_SC) {
+#if defined(DEBUG_QUEUES)
+ if(HOSTDATA(shpnt)->debug & debug_queues)
+ printk("i+, ");
+#endif
+ save_flags(flags);
+ cli();
+ append_SC(&ISSUE_SC, CURRENT_SC);
+ CURRENT_SC=NULL;
+ restore_flags(flags);
+ }
+
+ /* disable sequences */
+ SETPORT(SCSISEQ, 0);
+ SETPORT(SSTAT0, CLRSELDI);
+ SETPORT(SSTAT1, CLRBUSFREE);
+
+#if defined(DEBUG_QUEUES) || defined(DEBUG_PHASES)
+ if(HOSTDATA(shpnt)->debug & (debug_queues|debug_phases))
+ printk("reselected, ");
+#endif
+
+ i = GETPORT(SELID) & ~(1 << shpnt->this_id);
+ target=0;
+
+ if(i==0)
+ aha152x_panic(shpnt, "reconnecting target unknown");
+
+ for(; (i & 1)==0; target++, i>>=1)
+ ;
+
+#if defined(DEBUG_QUEUES)
+ if(HOSTDATA(shpnt)->debug & debug_queues)
+ printk("SELID=%02x, target=%d, ", GETPORT(SELID), target);
+#endif
+ SETPORT(SCSIID, (shpnt->this_id << OID_) | target);
+ SETPORT(SCSISEQ, ENRESELI);
+
+ if(TESTLO(SSTAT0, SELDI))
+ aha152x_panic(shpnt, "RESELI failed");
+
+ SETPORT(SCSIRATE, HOSTDATA(shpnt)->syncrate[target]&0x7f);
+
+ SETPORT(SCSISIG, P_MSGI);
+
+ /* Get identify message */
+ if((i=getphase(shpnt))!=P_MSGI) {
+ printk("target doesn't enter MSGI to identify (phase=%02x)\n", i);
+ aha152x_panic(shpnt, "unknown lun");
+ }
+ SETPORT(SCSISEQ, 0);
+
+ SETPORT(SXFRCTL0, CH1);
+
+ identify_msg = GETPORT(SCSIBUS);
+
+ if(!(identify_msg & IDENTIFY_BASE)) {
+ printk("target=%d, inbound message (%02x) != IDENTIFY\n",
+ target, identify_msg);
+ aha152x_panic(shpnt, "unknown lun");
+ }
+
+
+#if defined(DEBUG_QUEUES)
+ if(HOSTDATA(shpnt)->debug & debug_queues)
+ printk("identify=%02x, lun=%d, ", identify_msg, identify_msg & 0x3f);
+#endif
+
+ save_flags(flags);
+ cli();
+
+#if defined(DEBUG_QUEUES)
+ if(HOSTDATA(shpnt)->debug & debug_queues)
+ printk("d-, ");
+#endif
+ CURRENT_SC = remove_SC(&DISCONNECTED_SC, target, identify_msg & 0x3f);
+
+ if(!CURRENT_SC) {
+ printk("lun=%d, ", identify_msg & 0x3f);
+ aha152x_panic(shpnt, "no disconnected command for that lun");
+ }
+
+ CURRENT_SC->SCp.phase &= ~disconnected;
+ restore_flags(flags);
+
+ make_acklow(shpnt);
+ if(getphase(shpnt)!=P_MSGI) {
+ SETPORT(SIMODE0, 0);
+ SETPORT(SIMODE1, ENPHASEMIS|ENBUSFREE);
+#if defined(DEBUG_RACE)
+ leave_driver("(reselected) intr");
+#endif
+ SETBITS(DMACNTRL0, INTEN);
+ return;
+ }
+ }
+
+ /* Check, if we aren't busy with a command */
+ if(!CURRENT_SC) {
+ /* bus is free to issue a queued command */
+ if(TESTHI(SSTAT1, BUSFREE) && ISSUE_SC) {
+ save_flags(flags);
+ cli();
+#if defined(DEBUG_QUEUES)
+ if(HOSTDATA(shpnt)->debug & debug_queues)
+ printk("i-, ");
+#endif
+ CURRENT_SC = remove_first_SC(&ISSUE_SC);
+ restore_flags(flags);
+
+#if defined(DEBUG_INTR) || defined(DEBUG_SELECTION) || defined(DEBUG_PHASES)
+ if(HOSTDATA(shpnt)->debug & (debug_intr|debug_selection|debug_phases))
+ printk("issuing command, ");
+#endif
+ CURRENT_SC->SCp.phase = in_selection;
+
+#if defined(DEBUG_INTR) || defined(DEBUG_SELECTION) || defined(DEBUG_PHASES)
+ if(HOSTDATA(shpnt)->debug & (debug_intr|debug_selection|debug_phases))
+ printk("selecting %d, ", CURRENT_SC->target);
+#endif
+ SETPORT(SCSIID, (shpnt->this_id << OID_) | CURRENT_SC->target);
+
+ /* Enable interrupts for SELECTION OUT DONE and SELECTION OUT INITIATED */
+ SETPORT(SXFRCTL1, HOSTDATA(shpnt)->parity ? (ENSPCHK|ENSTIMER) : ENSTIMER);
+
+ /* enable interrupts for SELECTION OUT DONE and SELECTION TIME OUT */
+ SETPORT(SIMODE0, ENSELDO | (DISCONNECTED_SC ? ENSELDI : 0));
+ SETPORT(SIMODE1, ENSELTIMO);
+
+ /* Enable SELECTION OUT sequence */
+ SETBITS(SCSISEQ, ENSELO | ENAUTOATNO);
+
+ } else {
+ /* No command we are busy with and no new to issue */
+ printk("aha152x: ignoring spurious interrupt, nothing to do\n");
+ if(TESTHI(DMACNTRL0, SWINT)) {
+ printk("aha152x: SWINT is set! Why?\n");
+ CLRBITS(DMACNTRL0, SWINT);
+ }
+ show_queues(shpnt);
+ }
+
+#if defined(DEBUG_RACE)
+ leave_driver("(selecting) intr");
+#endif
+ SETBITS(DMACNTRL0, INTEN);
+ return;
+ }
+
+ /* the bus is busy with something */
+
+#if defined(DEBUG_INTR)
+ if(HOSTDATA(shpnt)->debug & debug_intr)
+ disp_ports(shpnt);
+#endif
+
+ /* we are waiting for the result of a selection attempt */
+ if(CURRENT_SC->SCp.phase & in_selection) {
+ if(TESTLO(SSTAT1, SELTO)) {
+ /* no timeout */
+ if(TESTHI(SSTAT0, SELDO)) {
+ /* clear BUS FREE interrupt */
+ SETPORT(SSTAT1, CLRBUSFREE);
+
+ /* Disable SELECTION OUT sequence */
+ CLRBITS(SCSISEQ, ENSELO|ENAUTOATNO);
+
+ /* Disable SELECTION OUT DONE interrupt */
+ CLRBITS(SIMODE0, ENSELDO);
+ CLRBITS(SIMODE1, ENSELTIMO);
+
+ if(TESTLO(SSTAT0, SELDO)) {
+ printk("aha152x: passing bus free condition\n");
+
+#if defined(DEBUG_RACE)
+ leave_driver("(passing bus free) intr");
+#endif
+ SETBITS(DMACNTRL0, INTEN);
+
+ if(CURRENT_SC->SCp.phase & aborted) {
+ HOSTDATA(shpnt)->abort_result=SCSI_ABORT_ERROR;
+ HOSTDATA(shpnt)->abortion_complete++;
+ }
+
+ aha152x_done(shpnt, DID_NO_CONNECT << 16);
+
+ return;
+ }
+#if defined(DEBUG_SELECTION) || defined(DEBUG_PHASES)
+ if(HOSTDATA(shpnt)->debug & (debug_selection|debug_phases))
+ printk("SELDO (SELID=%x), ", GETPORT(SELID));
+#endif
+
+ /* selection was done */
+ SETPORT(SSTAT0, CLRSELDO);
+
+#if defined(DEBUG_ABORT)
+ if((HOSTDATA(shpnt)->debug & debug_abort) && (CURRENT_SC->SCp.phase & aborted))
+ printk("(ABORT) target selected, ");
+#endif
+
+ CURRENT_SC->SCp.phase &= ~in_selection;
+ CURRENT_SC->SCp.phase |= in_other;
+
+ ADDMSG(IDENTIFY(HOSTDATA(shpnt)->reconnect,CURRENT_SC->lun));
+
+ if(!(SYNCRATE&0x80) && HOSTDATA(shpnt)->synchronous) {
+ ADDMSG(EXTENDED_MESSAGE);
+ ADDMSG(3);
+ ADDMSG(EXTENDED_SDTR);
+ ADDMSG(50);
+ ADDMSG(8);
+
+ printk("outbound SDTR: ");
+ print_msg(&MSG(MSGLEN-5));
+
+ SYNCRATE=0x80;
+ CURRENT_SC->SCp.phase |= in_sync;
+ }
+
+#if defined(DEBUG_RACE)
+ leave_driver("(SELDO) intr");
+#endif
+ SETPORT(SCSIRATE, SYNCRATE&0x7f);
+
+ SETPORT(SCSISIG, P_MSGO);
+
+ SETPORT(SIMODE0, 0);
+ SETPORT(SIMODE1, ENREQINIT|ENBUSFREE);
+ SETBITS(DMACNTRL0, INTEN);
+
+ return;
+ } else
+ aha152x_panic(shpnt, "neither timeout nor selection\007");
+ } else {
+#if defined(DEBUG_SELECTION) || defined(DEBUG_PHASES)
+ if(HOSTDATA(shpnt)->debug & (debug_selection|debug_phases))
+ printk("SELTO, ");
+#endif
+ /* end selection attempt */
+ CLRBITS(SCSISEQ, ENSELO|ENAUTOATNO);
+
+ /* timeout */
+ SETPORT(SSTAT1, CLRSELTIMO);
+
+ SETPORT(SIMODE0, DISCONNECTED_SC ? ENSELDI : 0);
+ SETPORT(SIMODE1, ISSUE_SC ? ENBUSFREE : 0);
+ SETBITS(DMACNTRL0, INTEN);
+#if defined(DEBUG_RACE)
+ leave_driver("(SELTO) intr");
+#endif
+
+ if(CURRENT_SC->SCp.phase & aborted) {
+#if defined(DEBUG_ABORT)
+ if(HOSTDATA(shpnt)->debug & debug_abort)
+ printk("(ABORT) selection timeout, ");
+#endif
+ HOSTDATA(shpnt)->abort_result=SCSI_ABORT_ERROR;
+ HOSTDATA(shpnt)->abortion_complete++;
+ }
+
+ if(TESTLO(SSTAT0, SELINGO))
+ /* ARBITRATION not won */
+ aha152x_done(shpnt, DID_BUS_BUSY << 16);
+ else
+ /* ARBITRATION won, but SELECTION failed */
+ aha152x_done(shpnt, DID_NO_CONNECT << 16);
+
+ return;
+ }
+ }
+
+ /* enable interrupt, when target leaves current phase */
+ phase = getphase(shpnt);
+ if(!(phase & ~P_MASK)) /* "real" phase */
+ SETPORT(SCSISIG, phase);
+ SETPORT(SSTAT1, CLRPHASECHG);
+ CURRENT_SC->SCp.phase =
+ (CURRENT_SC->SCp.phase & ~((P_MASK|1)<<16)) | (phase << 16);
+
+ /* information transfer phase */
+ switch(phase) {
+ case P_MSGO: /* MESSAGE OUT */
+ {
+ int i, identify=0, abort=0;
+
+#if defined(DEBUG_INTR) || defined(DEBUG_MSGO) || defined(DEBUG_PHASES)
+ if(HOSTDATA(shpnt)->debug & (debug_intr|debug_msgo|debug_phases))
+ printk("MESSAGE OUT, ");
+#endif
+ if(MSGLEN==0) {
+ ADDMSG(MESSAGE_REJECT);
+#if defined(DEBUG_MSGO)
+ if(HOSTDATA(shpnt)->debug & debug_msgo)
+ printk("unexpected MESSAGE OUT phase; rejecting, ");
+#endif
+ }
+
+ CLRBITS(SXFRCTL0, ENDMA);
+
+ SETPORT(SIMODE0, 0);
+ SETPORT(SIMODE1, ENPHASEMIS|ENREQINIT|ENBUSFREE);
+
+ /* wait for data latch to become ready or a phase change */
+ while(TESTLO(DMASTAT, INTSTAT))
+ barrier();
+
+#if defined(DEBUG_MSGO)
+ if(HOSTDATA(shpnt)->debug & debug_msgo) {
+ int i;
+
+ printk("messages (");
+ for(i=0; i<MSGLEN; i+=print_msg(&MSG(i)), printk(" "))
+ ;
+ printk("), ");
+ }
+#endif
+
+ for(i=0; i<MSGLEN && TESTLO(SSTAT1, PHASEMIS); i++) {
+#if defined(DEBUG_MSGO)
+ if(HOSTDATA(shpnt)->debug & debug_msgo)
+ printk("%x ", MSG(i));
+#endif
+ if(i==MSGLEN-1) {
+ /* Leave MESSAGE OUT after transfer */
+ SETPORT(SSTAT1, CLRATNO);
+ }
+
+ SETPORT(SCSIDAT, MSG(i));
+
+ make_acklow(shpnt);
+ getphase(shpnt);
+
+ if(MSG(i)==IDENTIFY(HOSTDATA(shpnt)->reconnect,CURRENT_SC->lun))
+ identify++;
+
+ if(MSG(i)==ABORT)
+ abort++;
+
+ }
+
+ MSGLEN=0;
+
+ if(identify)
+ CURRENT_SC->SCp.phase |= sent_ident;
+
+ if(abort) {
+ /* revive abort(); abort() enables interrupts */
+ HOSTDATA(shpnt)->abort_result=SCSI_ABORT_SUCCESS;
+ HOSTDATA(shpnt)->abortion_complete++;
+
+ CURRENT_SC->SCp.phase &= ~(P_MASK<<16);
+
+ /* exit */
+ SETBITS(DMACNTRL0, INTEN);
+#if defined(DEBUG_RACE)
+ leave_driver("(ABORT) intr");
+#endif
+ aha152x_done(shpnt, DID_ABORT<<16);
+
+ return;
+ }
+ }
+ break;
+
+ case P_CMD: /* COMMAND phase */
+#if defined(DEBUG_INTR) || defined(DEBUG_CMD) || defined(DEBUG_PHASES)
+ if(HOSTDATA(shpnt)->debug & (debug_intr|debug_cmd|debug_phases))
+ printk("COMMAND, ");
+#endif
+ if(!(CURRENT_SC->SCp.sent_command)) {
+ int i;
+
+ CLRBITS(SXFRCTL0, ENDMA);
+
+ SETPORT(SIMODE0, 0);
+ SETPORT(SIMODE1, ENPHASEMIS|ENREQINIT|ENBUSFREE);
+
+ /* wait for data latch to become ready or a phase change */
+ while(TESTLO(DMASTAT, INTSTAT))
+ barrier();
+
+ for(i=0; i<CURRENT_SC->cmd_len && TESTLO(SSTAT1, PHASEMIS); i++) {
+ SETPORT(SCSIDAT, CURRENT_SC->cmnd[i]);
+
+ make_acklow(shpnt);
+ getphase(shpnt);
+ }
+
+ if(i<CURRENT_SC->cmd_len && TESTHI(SSTAT1, PHASEMIS))
+ aha152x_panic(shpnt, "target left COMMAND");
+
+ CURRENT_SC->SCp.sent_command++;
+ } else
+ aha152x_panic(shpnt, "Nothing to send while in COMMAND");
+ break;
+
+ case P_MSGI: /* MESSAGE IN phase */
+ {
+ int start_sync=0;
+
+#if defined(DEBUG_INTR) || defined(DEBUG_MSGI) || defined(DEBUG_PHASES)
+ if(HOSTDATA(shpnt)->debug & (debug_intr|debug_msgi|debug_phases))
+ printk("MESSAGE IN, ");
+#endif
+ SETPORT(SXFRCTL0, CH1);
+
+ SETPORT(SIMODE0, 0);
+ SETPORT(SIMODE1, ENBUSFREE);
+
+ while(phase == P_MSGI) {
+ CURRENT_SC->SCp.Message = GETPORT(SCSIDAT);
+ switch(CURRENT_SC->SCp.Message) {
+ case DISCONNECT:
+#if defined(DEBUG_MSGI) || defined(DEBUG_PHASES)
+ if(HOSTDATA(shpnt)->debug & (debug_msgi|debug_phases))
+ printk("target disconnected, ");
+#endif
+ CURRENT_SC->SCp.Message = 0;
+ CURRENT_SC->SCp.phase |= disconnected;
+ if(!HOSTDATA(shpnt)->reconnect)
+ aha152x_panic(shpnt, "target was not allowed to disconnect");
+
+ break;
+
+ case COMMAND_COMPLETE:
+#if defined(DEBUG_MSGI) || defined(DEBUG_PHASES)
+ if(HOSTDATA(shpnt)->debug & (debug_msgi|debug_phases))
+ printk("inbound message (COMMAND COMPLETE), ");
+#endif
+ done++;
+ break;
+
+ case MESSAGE_REJECT:
+ if(CURRENT_SC->SCp.phase & in_sync) {
+ CURRENT_SC->SCp.phase &= ~in_sync;
+ SYNCRATE=0x80;
+ printk("synchronous rejected, ");
+ } else
+ printk("inbound message (MESSAGE REJECT), ");
+#if defined(DEBUG_MSGI)
+ if(HOSTDATA(shpnt)->debug & debug_msgi)
+ printk("inbound message (MESSAGE REJECT), ");
+#endif
+ break;
+
+ case SAVE_POINTERS:
+#if defined(DEBUG_MSGI)
+ if(HOSTDATA(shpnt)->debug & debug_msgi)
+ printk("inbound message (SAVE DATA POINTERS), ");
+#endif
+ break;
+
+ case RESTORE_POINTERS:
+#if defined(DEBUG_MSGI)
+ if(HOSTDATA(shpnt)->debug & debug_msgi)
+ printk("inbound message (RESTORE DATA POINTERS), ");
+#endif
+ break;
+
+ case EXTENDED_MESSAGE:
+ {
+ char buffer[16];
+ int i;
+
+#if defined(DEBUG_MSGI)
+ if(HOSTDATA(shpnt)->debug & debug_msgi)
+ printk("inbound message (EXTENDED MESSAGE), ");
+#endif
+ make_acklow(shpnt);
+ if(getphase(shpnt)!=P_MSGI)
+ break;
+
+ buffer[0]=EXTENDED_MESSAGE;
+ buffer[1]=GETPORT(SCSIDAT);
+
+ for(i=0; i<buffer[1] &&
+ (make_acklow(shpnt), getphase(shpnt)==P_MSGI); i++)
+ buffer[2+i]=GETPORT(SCSIDAT);
+
+#if defined(DEBUG_MSGI)
+ if(HOSTDATA(shpnt)->debug & debug_msgi)
+ print_msg(buffer);
+#endif
+
+ switch(buffer [2]) {
+ case EXTENDED_SDTR:
+ {
+ long ticks;
+
+ if(buffer[1]!=3)
+ aha152x_panic(shpnt, "SDTR message length != 3");
+
+ if(!HOSTDATA(shpnt)->synchronous)
+ break;
+
+ printk("inbound SDTR: "); print_msg(buffer);
+
+ ticks=(buffer[3]*4+49)/50;
+
+ if(CURRENT_SC->SCp.phase & in_sync) {
+ /* we initiated SDTR */
+ if(ticks>9 || buffer[4]<1 || buffer[4]>8)
+ aha152x_panic(shpnt, "received SDTR invalid");
+
+ SYNCRATE |= ((ticks-2)<<4) + buffer[4];
+ } else if(ticks<=9 && buffer[4]>=1) {
+ if(buffer[4]>8)
+ buffer[4]=8;
+
+ ADDMSG(EXTENDED_MESSAGE);
+ ADDMSG(3);
+ ADDMSG(EXTENDED_SDTR);
+ if(ticks<4) {
+ ticks=4;
+ ADDMSG(50);
+ } else
+ ADDMSG(buffer[3]);
+
+ ADDMSG(buffer[4]);
+
+ printk("outbound SDTR: ");
+ print_msg(&MSG(MSGLEN-5));
+
+ CURRENT_SC->SCp.phase |= in_sync;
+
+ SYNCRATE |= ((ticks-2)<<4) + buffer[4];
+
+ start_sync++;
+ } else {
+ /* requested SDTR is too slow, do it asynchronously */
+ ADDMSG(MESSAGE_REJECT);
+ SYNCRATE = 0;
+ }
+
+ SETPORT(SCSIRATE, SYNCRATE&0x7f);
+ }
+ break;
+
+ case EXTENDED_MODIFY_DATA_POINTER:
+ case EXTENDED_EXTENDED_IDENTIFY:
+ case EXTENDED_WDTR:
+ default:
+ ADDMSG(MESSAGE_REJECT);
+ break;
+ }
+ }
+ break;
+
+ default:
+ printk("unsupported inbound message %x, ", CURRENT_SC->SCp.Message);
+ break;
+
+ }
+
+ make_acklow(shpnt);
+ phase=getphase(shpnt);
+ }
+
+ if(start_sync)
+ CURRENT_SC->SCp.phase |= in_sync;
+ else
+ CURRENT_SC->SCp.phase &= ~in_sync;
+
+ if(MSGLEN>0)
+ SETPORT(SCSISIG, P_MSGI|ATNO);
+
+ /* clear SCSI fifo on BUSFREE */
+ if(phase==P_BUSFREE)
+ SETPORT(SXFRCTL0, CH1|CLRCH1);
+
+ if(CURRENT_SC->SCp.phase & disconnected) {
+ save_flags(flags);
+ cli();
+#if defined(DEBUG_QUEUES)
+ if(HOSTDATA(shpnt)->debug & debug_queues)
+ printk("d+, ");
+#endif
+ append_SC(&DISCONNECTED_SC, CURRENT_SC);
+ CURRENT_SC->SCp.phase |= 1<<16;
+ CURRENT_SC = NULL;
+ restore_flags(flags);
+
+ SETBITS(SCSISEQ, ENRESELI);
+
+ SETPORT(SIMODE0, DISCONNECTED_SC ? ENSELDI : 0);
+ SETPORT(SIMODE1, ISSUE_SC ? ENBUSFREE : 0);
+
+ SETBITS(DMACNTRL0, INTEN);
+
+ return;
+ }
+ }
+ break;
+
+ case P_STATUS: /* STATUS IN phase */
+#if defined(DEBUG_STATUS) || defined(DEBUG_INTR) || defined(DEBUG_PHASES)
+ if(HOSTDATA(shpnt)->debug & (debug_status|debug_intr|debug_phases))
+ printk("STATUS, ");
+#endif
+ SETPORT(SXFRCTL0, CH1);
+
+ SETPORT(SIMODE0, 0);
+ SETPORT(SIMODE1, ENREQINIT|ENBUSFREE);
+
+ if(TESTHI(SSTAT1, PHASEMIS))
+ printk("aha152x: passing STATUS phase");
+
+ CURRENT_SC->SCp.Status = GETPORT(SCSIBUS);
+ make_acklow(shpnt);
+ getphase(shpnt);
+
+#if defined(DEBUG_STATUS)
+ if(HOSTDATA(shpnt)->debug & debug_status) {
+ printk("inbound status ");
+ print_status(CURRENT_SC->SCp.Status);
+ printk(", ");
+ }
+#endif
+ break;
+
+ case P_DATAI: /* DATA IN phase */
+ {
+ int fifodata, data_count, done;
+
+#if defined(DEBUG_DATAI) || defined(DEBUG_INTR) || defined(DEBUG_PHASES)
+ if(HOSTDATA(shpnt)->debug & (debug_datai|debug_intr|debug_phases))
+ printk("DATA IN, ");
+#endif
+
+#if 0
+ if(GETPORT(FIFOSTAT) || GETPORT(SSTAT2) & (SFULL|SFCNT))
+ printk("aha152x: P_DATAI: %d(%d) bytes left in FIFO, resetting\n",
+ GETPORT(FIFOSTAT), GETPORT(SSTAT2) & (SFULL|SFCNT));
+#endif
+
+ /* reset host fifo */
+ SETPORT(DMACNTRL0, RSTFIFO);
+ SETPORT(DMACNTRL0, RSTFIFO|ENDMA);
+
+ SETPORT(SXFRCTL0, CH1|SCSIEN|DMAEN);
+
+ SETPORT(SIMODE0, 0);
+ SETPORT(SIMODE1, ENPHASEMIS|ENBUSFREE);
+
+ /* done is set when the FIFO is empty after the target left DATA IN */
+ done=0;
+
+ /* while the target stays in DATA to transfer data */
+ while (!done) {
+#if defined(DEBUG_DATAI)
+ if(HOSTDATA(shpnt)->debug & debug_datai)
+ printk("expecting data, ");
+#endif
+ /* wait for PHASEMIS or full FIFO */
+ while(TESTLO(DMASTAT, DFIFOFULL|INTSTAT))
+ barrier();
+
+#if defined(DEBUG_DATAI)
+ if(HOSTDATA(shpnt)->debug & debug_datai)
+ printk("ok, ");
+#endif
+
+ if(TESTHI(DMASTAT, DFIFOFULL))
+ fifodata=GETPORT(FIFOSTAT);
+ else {
+ /* wait for SCSI fifo to get empty */
+ while(TESTLO(SSTAT2, SEMPTY))
+ barrier();
+
+ /* rest of data in FIFO */
+ fifodata=GETPORT(FIFOSTAT);
+#if defined(DEBUG_DATAI)
+ if(HOSTDATA(shpnt)->debug & debug_datai)
+ printk("last transfer, ");
+#endif
+ done=1;
+ }
+
+#if defined(DEBUG_DATAI)
+ if(HOSTDATA(shpnt)->debug & debug_datai)
+ printk("fifodata=%d, ", fifodata);
+#endif
+
+ while(fifodata && CURRENT_SC->SCp.this_residual) {
+ data_count=fifodata;
+
+ /* limit data transfer to size of first sg buffer */
+ if(data_count > CURRENT_SC->SCp.this_residual)
+ data_count = CURRENT_SC->SCp.this_residual;
+
+ fifodata -= data_count;
+
+#if defined(DEBUG_DATAI)
+ if(HOSTDATA(shpnt)->debug & debug_datai)
+ printk("data_count=%d, ", data_count);
+#endif
+
+ if(data_count&1) {
+ /* get a single byte in byte mode */
+ SETBITS(DMACNTRL0, _8BIT);
+ *CURRENT_SC->SCp.ptr++ = GETPORT(DATAPORT);
+ CURRENT_SC->SCp.this_residual--;
+ }
+
+ if(data_count>1) {
+ CLRBITS(DMACNTRL0, _8BIT);
+ data_count >>= 1; /* Number of words */
+ insw(DATAPORT, CURRENT_SC->SCp.ptr, data_count);
+#if defined(DEBUG_DATAI)
+ if(HOSTDATA(shpnt)->debug & debug_datai)
+ /* show what comes with the last transfer */
+ if(done) {
+#if 0
+ int i;
+ unsigned char *data;
+#endif
+
+ printk("data on last transfer (%d bytes) ",
+ 2*data_count);
+#if 0
+ printk("data on last transfer (%d bytes: ",
+ 2*data_count);
+ data = (unsigned char *) CURRENT_SC->SCp.ptr;
+ for(i=0; i<2*data_count; i++)
+ printk("%2x ", *data++);
+ printk("), ");
+#endif
+ }
+#endif
+ CURRENT_SC->SCp.ptr += 2 * data_count;
+ CURRENT_SC->SCp.this_residual -= 2 * data_count;
+ }
+
+ /* if this buffer is full and there are more buffers left */
+ if(!CURRENT_SC->SCp.this_residual &&
+ CURRENT_SC->SCp.buffers_residual) {
+ /* advance to next buffer */
+ 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;
+ }
+ }
+
+ /*
+ * FIFO should be empty
+ */
+ if(fifodata>0) {
+ printk("aha152x: more data than expected (%d bytes)\n",
+ GETPORT(FIFOSTAT));
+ SETBITS(DMACNTRL0, _8BIT);
+ printk("aha152x: data (");
+ while(fifodata--)
+ printk("%2x ", GETPORT(DATAPORT));
+ printk(")\n");
+ }
+
+#if defined(DEBUG_DATAI)
+ if(HOSTDATA(shpnt)->debug & debug_datai)
+ if(!fifodata)
+ printk("fifo empty, ");
+ else
+ printk("something left in fifo, ");
+#endif
+ }
+
+#if defined(DEBUG_DATAI)
+ if((HOSTDATA(shpnt)->debug & debug_datai) &&
+ (CURRENT_SC->SCp.buffers_residual ||
+ CURRENT_SC->SCp.this_residual))
+ printk("left buffers (buffers=%d, bytes=%d), ",
+ CURRENT_SC->SCp.buffers_residual, CURRENT_SC->SCp.this_residual);
+#endif
+ /* transfer can be considered ended, when SCSIEN reads back zero */
+ CLRBITS(SXFRCTL0, SCSIEN|DMAEN);
+ while(TESTHI(SXFRCTL0, SCSIEN))
+ barrier();
+ CLRBITS(DMACNTRL0, ENDMA);
+
+#if defined(DEBUG_DATAI) || defined(DEBUG_INTR)
+ if(HOSTDATA(shpnt)->debug & (debug_datai|debug_intr))
+ printk("got %d bytes, ", GETSTCNT());
+#endif
+
+ CURRENT_SC->SCp.have_data_in++;
+ }
+ break;
+
+ case P_DATAO: /* DATA OUT phase */
+ {
+ int data_count;
+
+#if defined(DEBUG_DATAO) || defined(DEBUG_INTR) || defined(DEBUG_PHASES)
+ if(HOSTDATA(shpnt)->debug & (debug_datao|debug_intr|debug_phases))
+ printk("DATA OUT, ");
+#endif
+#if defined(DEBUG_DATAO)
+ if(HOSTDATA(shpnt)->debug & debug_datao)
+ printk("got data to send (bytes=%d, buffers=%d), ",
+ CURRENT_SC->SCp.this_residual,
+ CURRENT_SC->SCp.buffers_residual);
+#endif
+
+ if(GETPORT(FIFOSTAT) || GETPORT(SSTAT2) & (SFULL|SFCNT)) {
+ printk("%d(%d) left in FIFO, ",
+ GETPORT(FIFOSTAT), GETPORT(SSTAT2) & (SFULL|SFCNT));
+ aha152x_panic(shpnt, "FIFO should be empty");
+ }
+
+ SETPORT(SXFRCTL0, CH1|CLRSTCNT|CLRCH1);
+ SETPORT(SXFRCTL0, SCSIEN|DMAEN|CH1);
+
+ SETPORT(DMACNTRL0, WRITE_READ|RSTFIFO);
+ SETPORT(DMACNTRL0, ENDMA|WRITE_READ);
+
+ SETPORT(SIMODE0, 0);
+ SETPORT(SIMODE1, ENPHASEMIS|ENBUSFREE);
+
+ /* while current buffer is not empty or
+ there are more buffers to transfer */
+ while(TESTLO(SSTAT1, PHASEMIS) &&
+ (CURRENT_SC->SCp.this_residual ||
+ CURRENT_SC->SCp.buffers_residual)) {
+#if defined(DEBUG_DATAO)
+ if(HOSTDATA(shpnt)->debug & debug_datao)
+ printk("sending data (left: bytes=%d, buffers=%d), waiting, ",
+ CURRENT_SC->SCp.this_residual,
+ CURRENT_SC->SCp.buffers_residual);
+#endif
+ /* transfer rest of buffer, but max. 128 byte */
+ data_count =
+ CURRENT_SC->SCp.this_residual > 128 ?
+ 128 : CURRENT_SC->SCp.this_residual ;
+
+#if defined(DEBUG_DATAO)
+ if(HOSTDATA(shpnt)->debug & debug_datao)
+ printk("data_count=%d, ", data_count);
+#endif
+
+ if(data_count&1) {
+ /* put a single byte in byte mode */
+ SETBITS(DMACNTRL0, _8BIT);
+ SETPORT(DATAPORT, *CURRENT_SC->SCp.ptr++);
+ CURRENT_SC->SCp.this_residual--;
+ }
+ if(data_count>1) {
+ CLRBITS(DMACNTRL0, _8BIT);
+ data_count >>= 1; /* number of words */
+ outsw(DATAPORT, CURRENT_SC->SCp.ptr, data_count);
+ CURRENT_SC->SCp.ptr += 2 * data_count;
+ CURRENT_SC->SCp.this_residual -= 2 * data_count;
+ }
+
+ /* wait for FIFO to get empty */
+ while(TESTLO(DMASTAT, DFIFOEMP|INTSTAT))
+ barrier();
+
+#if defined(DEBUG_DATAO)
+ if(HOSTDATA(shpnt)->debug & debug_datao)
+ printk("fifo (%d bytes), transfered (%d bytes), ",
+ GETPORT(FIFOSTAT), GETSTCNT());
+#endif
+
+ /* if this buffer is empty and there are more buffers left */
+ if(TESTLO(SSTAT1, PHASEMIS) &&
+ !CURRENT_SC->SCp.this_residual &&
+ CURRENT_SC->SCp.buffers_residual) {
+ /* advance to next buffer */
+ 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(CURRENT_SC->SCp.this_residual || CURRENT_SC->SCp.buffers_residual) {
+ /* target leaves DATA OUT for an other phase (perhaps disconnect) */
+
+ /* data in fifos has to be resend */
+ data_count = GETPORT(SSTAT2) & (SFULL|SFCNT);
+
+ data_count += GETPORT(FIFOSTAT) ;
+ CURRENT_SC->SCp.ptr -= data_count;
+ CURRENT_SC->SCp.this_residual += data_count;
+#if defined(DEBUG_DATAO)
+ if(HOSTDATA(shpnt)->debug & debug_datao)
+ printk("left data (bytes=%d, buffers=%d), fifos (bytes=%d), "
+ "transfer incomplete, resetting fifo, ",
+ CURRENT_SC->SCp.this_residual,
+ CURRENT_SC->SCp.buffers_residual,
+ data_count);
+#endif
+ SETPORT(DMACNTRL0, WRITE_READ|RSTFIFO);
+ CLRBITS(SXFRCTL0, SCSIEN|DMAEN);
+ CLRBITS(DMACNTRL0, ENDMA);
+ } else {
+#if defined(DEBUG_DATAO)
+ if(HOSTDATA(shpnt)->debug & debug_datao)
+ printk("waiting for SCSI fifo to get empty, ");
+#endif
+ /* wait for SCSI fifo to get empty */
+ while(TESTLO(SSTAT2, SEMPTY))
+ barrier();
+#if defined(DEBUG_DATAO)
+ if(HOSTDATA(shpnt)->debug & debug_datao)
+ printk("ok, left data (bytes=%d, buffers=%d) ",
+ CURRENT_SC->SCp.this_residual,
+ CURRENT_SC->SCp.buffers_residual);
+#endif
+ CLRBITS(SXFRCTL0, SCSIEN|DMAEN);
+
+ /* transfer can be considered ended, when SCSIEN reads back zero */
+ while(TESTHI(SXFRCTL0, SCSIEN))
+ barrier();
+
+ CLRBITS(DMACNTRL0, ENDMA);
+ }
+
+#if defined(DEBUG_DATAO) || defined(DEBUG_INTR)
+ if(HOSTDATA(shpnt)->debug & (debug_datao|debug_intr))
+ printk("sent %d data bytes, ", GETSTCNT());
+#endif
+ }
+ break;
+
+ case P_BUSFREE: /* BUSFREE */
+#if defined(DEBUG_RACE)
+ leave_driver("(BUSFREE) intr");
+#endif
+#if defined(DEBUG_PHASES)
+ if(HOSTDATA(shpnt)->debug & debug_phases)
+ printk("unexpected BUS FREE, ");
+#endif
+ CURRENT_SC->SCp.phase &= ~(P_MASK<<16);
+
+ aha152x_done(shpnt, DID_ERROR << 16); /* Don't know any better */
+ return;
+ break;
+
+ case P_PARITY: /* parity error in DATA phase */
+#if defined(DEBUG_RACE)
+ leave_driver("(DID_PARITY) intr");
+#endif
+ printk("PARITY error in DATA phase, ");
+
+ CURRENT_SC->SCp.phase &= ~(P_MASK<<16);
+
+ SETBITS(DMACNTRL0, INTEN);
+ aha152x_done(shpnt, DID_PARITY << 16);
+ return;
+ break;
+
+ default:
+ printk("aha152x: unexpected phase\n");
+ break;
+ }
+
+ if(done) {
+#if defined(DEBUG_INTR)
+ if(HOSTDATA(shpnt)->debug & debug_intr)
+ printk("command done.\n");
+#endif
+#if defined(DEBUG_RACE)
+ leave_driver("(done) intr");
+#endif
+
+ SETPORT(SIMODE0, DISCONNECTED_SC ? ENSELDI : 0);
+ SETPORT(SIMODE1, ISSUE_SC ? ENBUSFREE : 0);
+ SETPORT(SCSISEQ, DISCONNECTED_SC ? ENRESELI : 0);
+
+ SETBITS(DMACNTRL0, INTEN);
+
+ aha152x_done(shpnt,
+ (CURRENT_SC->SCp.Status & 0xff)
+ | ((CURRENT_SC->SCp.Message & 0xff) << 8)
+ | (DID_OK << 16));
+
+#if defined(DEBUG_RACE)
+ printk("done returned (DID_OK: Status=%x; Message=%x).\n",
+ CURRENT_SC->SCp.Status, CURRENT_SC->SCp.Message);
+#endif
+ return;
+ }
+
+ if(CURRENT_SC)
+ CURRENT_SC->SCp.phase |= 1<<16;
+
+ SETPORT(SIMODE0, 0);
+ SETPORT(SIMODE1, ENPHASEMIS|ENBUSFREE);
+#if defined(DEBUG_INTR)
+ if(HOSTDATA(shpnt)->debug & debug_intr)
+ disp_enintr(shpnt);
+#endif
+#if defined(DEBUG_RACE)
+ leave_driver("(PHASEEND) intr");
+#endif
+
+ SETBITS(DMACNTRL0, INTEN);
+ return;
+}
+
+/*
+ * Dump the current driver status and panic...
+ */
+static void aha152x_panic(struct Scsi_Host *shpnt, char *msg)
+{
+ printk("\naha152x: %s\n", msg);
+ show_queues(shpnt);
+ panic("aha152x panic");
+}
+
+/*
+ * Display registers of AIC-6260
+ */
+static void disp_ports(struct Scsi_Host *shpnt)
+{
+#ifdef DEBUG_AHA152X
+ int s;
+
+#ifdef SKIP_PORTS
+ if(HOSTDATA(shpnt)->debug & debug_skipports)
+ return;
+#endif
+
+ printk("\n%s: ", CURRENT_SC ? "on bus" : "waiting");
+
+ s=GETPORT(SCSISEQ);
+ printk("SCSISEQ (");
+ if(s & TEMODEO) printk("TARGET MODE ");
+ if(s & ENSELO) printk("SELO ");
+ if(s & ENSELI) printk("SELI ");
+ if(s & ENRESELI) printk("RESELI ");
+ if(s & ENAUTOATNO) printk("AUTOATNO ");
+ if(s & ENAUTOATNI) printk("AUTOATNI ");
+ if(s & ENAUTOATNP) printk("AUTOATNP ");
+ if(s & SCSIRSTO) printk("SCSIRSTO ");
+ printk(");");
+
+ printk(" SCSISIG (");
+ s=GETPORT(SCSISIG);
+ switch(s & P_MASK) {
+ case P_DATAO:
+ printk("DATA OUT");
+ break;
+ case P_DATAI:
+ printk("DATA IN");
+ break;
+ case P_CMD:
+ printk("COMMAND");
+ break;
+ case P_STATUS:
+ printk("STATUS");
+ break;
+ case P_MSGO:
+ printk("MESSAGE OUT");
+ break;
+ case P_MSGI:
+ printk("MESSAGE IN");
+ break;
+ default:
+ printk("*illegal*");
+ break;
+ }
+
+ printk("); ");
+
+ printk("INTSTAT (%s); ", TESTHI(DMASTAT, INTSTAT) ? "hi" : "lo");
+
+ printk("SSTAT (");
+ s=GETPORT(SSTAT0);
+ if(s & TARGET) printk("TARGET ");
+ if(s & SELDO) printk("SELDO ");
+ if(s & SELDI) printk("SELDI ");
+ if(s & SELINGO) printk("SELINGO ");
+ if(s & SWRAP) printk("SWRAP ");
+ if(s & SDONE) printk("SDONE ");
+ if(s & SPIORDY) printk("SPIORDY ");
+ if(s & DMADONE) printk("DMADONE ");
+
+ s=GETPORT(SSTAT1);
+ if(s & SELTO) printk("SELTO ");
+ if(s & ATNTARG) printk("ATNTARG ");
+ if(s & SCSIRSTI) printk("SCSIRSTI ");
+ if(s & PHASEMIS) printk("PHASEMIS ");
+ if(s & BUSFREE) printk("BUSFREE ");
+ if(s & SCSIPERR) printk("SCSIPERR ");
+ if(s & PHASECHG) printk("PHASECHG ");
+ if(s & REQINIT) printk("REQINIT ");
+ printk("); ");
+
+
+ printk("SSTAT (");
+
+ s=GETPORT(SSTAT0) & GETPORT(SIMODE0);
+
+ if(s & TARGET) printk("TARGET ");
+ if(s & SELDO) printk("SELDO ");
+ if(s & SELDI) printk("SELDI ");
+ if(s & SELINGO) printk("SELINGO ");
+ if(s & SWRAP) printk("SWRAP ");
+ if(s & SDONE) printk("SDONE ");
+ if(s & SPIORDY) printk("SPIORDY ");
+ if(s & DMADONE) printk("DMADONE ");
+
+ s=GETPORT(SSTAT1) & GETPORT(SIMODE1);
+
+ if(s & SELTO) printk("SELTO ");
+ if(s & ATNTARG) printk("ATNTARG ");
+ if(s & SCSIRSTI) printk("SCSIRSTI ");
+ if(s & PHASEMIS) printk("PHASEMIS ");
+ if(s & BUSFREE) printk("BUSFREE ");
+ if(s & SCSIPERR) printk("SCSIPERR ");
+ if(s & PHASECHG) printk("PHASECHG ");
+ if(s & REQINIT) printk("REQINIT ");
+ printk("); ");
+
+ printk("SXFRCTL0 (");
+
+ s=GETPORT(SXFRCTL0);
+ if(s & SCSIEN) printk("SCSIEN ");
+ if(s & DMAEN) printk("DMAEN ");
+ if(s & CH1) printk("CH1 ");
+ if(s & CLRSTCNT) printk("CLRSTCNT ");
+ if(s & SPIOEN) printk("SPIOEN ");
+ if(s & CLRCH1) printk("CLRCH1 ");
+ printk("); ");
+
+ printk("SIGNAL (");
+
+ s=GETPORT(SCSISIG);
+ if(s & ATNI) printk("ATNI ");
+ if(s & SELI) printk("SELI ");
+ if(s & BSYI) printk("BSYI ");
+ if(s & REQI) printk("REQI ");
+ if(s & ACKI) printk("ACKI ");
+ printk("); ");
+
+ printk("SELID (%02x), ", GETPORT(SELID));
+
+ printk("SSTAT2 (");
+
+ s=GETPORT(SSTAT2);
+ if(s & SOFFSET) printk("SOFFSET ");
+ if(s & SEMPTY) printk("SEMPTY ");
+ if(s & SFULL) printk("SFULL ");
+ printk("); SFCNT (%d); ", s & (SFULL|SFCNT));
+
+ s=GETPORT(SSTAT3);
+ printk("SCSICNT (%d), OFFCNT(%d), ", (s&0xf0)>>4, s&0x0f);
+
+ printk("SSTAT4 (");
+ s=GETPORT(SSTAT4);
+ if(s & SYNCERR) printk("SYNCERR ");
+ if(s & FWERR) printk("FWERR ");
+ if(s & FRERR) printk("FRERR ");
+ printk("); ");
+
+ printk("DMACNTRL0 (");
+ s=GETPORT(DMACNTRL0);
+ printk("%s ", s & _8BIT ? "8BIT" : "16BIT");
+ printk("%s ", s & DMA ? "DMA" : "PIO" );
+ printk("%s ", s & WRITE_READ ? "WRITE" : "READ" );
+ if(s & ENDMA) printk("ENDMA ");
+ if(s & INTEN) printk("INTEN ");
+ if(s & RSTFIFO) printk("RSTFIFO ");
+ if(s & SWINT) printk("SWINT ");
+ printk("); ");
+
+ printk("DMASTAT (");
+ s=GETPORT(DMASTAT);
+ if(s & ATDONE) printk("ATDONE ");
+ if(s & WORDRDY) printk("WORDRDY ");
+ if(s & DFIFOFULL) printk("DFIFOFULL ");
+ if(s & DFIFOEMP) printk("DFIFOEMP ");
+ printk(")");
+
+ printk("\n");
+#endif
+}
+
+/*
+ * display enabled interrupts
+ */
+static void disp_enintr(struct Scsi_Host *shpnt)
+{
+ int s;
+
+ printk("enabled interrupts (");
+
+ s=GETPORT(SIMODE0);
+ if(s & ENSELDO) printk("ENSELDO ");
+ if(s & ENSELDI) printk("ENSELDI ");
+ if(s & ENSELINGO) printk("ENSELINGO ");
+ if(s & ENSWRAP) printk("ENSWRAP ");
+ if(s & ENSDONE) printk("ENSDONE ");
+ if(s & ENSPIORDY) printk("ENSPIORDY ");
+ if(s & ENDMADONE) printk("ENDMADONE ");
+
+ s=GETPORT(SIMODE1);
+ if(s & ENSELTIMO) printk("ENSELTIMO ");
+ if(s & ENATNTARG) printk("ENATNTARG ");
+ if(s & ENPHASEMIS) printk("ENPHASEMIS ");
+ if(s & ENBUSFREE) printk("ENBUSFREE ");
+ if(s & ENSCSIPERR) printk("ENSCSIPERR ");
+ if(s & ENPHASECHG) printk("ENPHASECHG ");
+ if(s & ENREQINIT) printk("ENREQINIT ");
+ printk(")\n");
+}
+
+#if defined(DEBUG_RACE)
+
+static const char *should_leave;
+static int in_driver=0;
+
+/*
+ * Only one routine can be in the driver at once.
+ */
+static void enter_driver(const char *func)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ printk("aha152x: entering %s() (%x)\n", func, jiffies);
+ if(in_driver) {
+ printk("%s should leave first.\n", should_leave);
+ panic("aha152x: already in driver\n");
+ }
+
+ in_driver++;
+ should_leave=func;
+ restore_flags(flags);
+}
+
+static void leave_driver(const char *func)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ printk("\naha152x: leaving %s() (%x)\n", func, jiffies);
+ if(!in_driver) {
+ printk("aha152x: %s already left.\n", should_leave);
+ panic("aha152x: %s already left driver.\n");
+ }
+
+ in_driver--;
+ should_leave=func;
+ restore_flags(flags);
+}
+#endif
+
+/*
+ * Show the command data of a command
+ */
+static void show_command(Scsi_Cmnd *ptr)
+{
+ printk("0x%08x: target=%d; lun=%d; cmnd=(",
+ (unsigned int) ptr, ptr->target, ptr->lun);
+
+ print_command(ptr->cmnd);
+
+ printk("); residual=%d; buffers=%d; phase |",
+ ptr->SCp.this_residual, ptr->SCp.buffers_residual);
+
+ if(ptr->SCp.phase & not_issued ) printk("not issued|");
+ if(ptr->SCp.phase & in_selection) printk("in selection|");
+ if(ptr->SCp.phase & disconnected) printk("disconnected|");
+ if(ptr->SCp.phase & aborted ) printk("aborted|");
+ if(ptr->SCp.phase & sent_ident ) printk("send_ident|");
+ if(ptr->SCp.phase & in_other) {
+ printk("; in other(");
+ switch((ptr->SCp.phase >> 16) & P_MASK) {
+ case P_DATAO:
+ printk("DATA OUT");
+ break;
+ case P_DATAI:
+ printk("DATA IN");
+ break;
+ case P_CMD:
+ printk("COMMAND");
+ break;
+ case P_STATUS:
+ printk("STATUS");
+ break;
+ case P_MSGO:
+ printk("MESSAGE OUT");
+ break;
+ case P_MSGI:
+ printk("MESSAGE IN");
+ break;
+ default:
+ printk("*illegal*");
+ break;
+ }
+ printk(")");
+ if(ptr->SCp.phase & (1<<16))
+ printk("; phaseend");
+ }
+ printk("; next=0x%08x\n", (unsigned int) ptr->host_scribble);
+}
+
+/*
+ * Dump the queued data
+ */
+static void show_queues(struct Scsi_Host *shpnt)
+{
+ unsigned long flags;
+ Scsi_Cmnd *ptr;
+
+ save_flags(flags);
+ cli();
+ printk("QUEUE STATUS:\nissue_SC:\n");
+ for(ptr=ISSUE_SC; ptr; ptr = (Scsi_Cmnd *) ptr->host_scribble)
+ show_command(ptr);
+
+ printk("current_SC:\n");
+ if(CURRENT_SC)
+ show_command(CURRENT_SC);
+ else
+ printk("none\n");
+
+ printk("disconnected_SC:\n");
+ for(ptr=DISCONNECTED_SC; ptr; ptr = (Scsi_Cmnd *) ptr->host_scribble)
+ show_command(ptr);
+
+ disp_ports(shpnt);
+ disp_enintr(shpnt);
+ restore_flags(flags);
+}
+
+int aha152x_set_info(char *buffer, int length, struct Scsi_Host *shpnt)
+{
+ return(-ENOSYS); /* Currently this is a no-op */
+}
+
+#undef SPRINTF
+#define SPRINTF(args...) pos += sprintf(pos, ## args)
+
+static int get_command(char *pos, Scsi_Cmnd *ptr)
+{
+ char *start = pos;
+ int i;
+
+ SPRINTF("0x%08x: target=%d; lun=%d; cmnd=( ",
+ (unsigned int) ptr, ptr->target, ptr->lun);
+
+ for(i=0; i<COMMAND_SIZE(ptr->cmnd[0]); i++)
+ SPRINTF("0x%02x ", ptr->cmnd[i]);
+
+ SPRINTF("); residual=%d; buffers=%d; phase |",
+ ptr->SCp.this_residual, ptr->SCp.buffers_residual);
+
+ if(ptr->SCp.phase & not_issued ) SPRINTF("not issued|");
+ if(ptr->SCp.phase & in_selection) SPRINTF("in selection|");
+ if(ptr->SCp.phase & disconnected) SPRINTF("disconnected|");
+ if(ptr->SCp.phase & aborted ) SPRINTF("aborted|");
+ if(ptr->SCp.phase & sent_ident ) SPRINTF("send_ident|");
+ if(ptr->SCp.phase & in_other) {
+ SPRINTF("; in other(");
+ switch((ptr->SCp.phase >> 16) & P_MASK) {
+ case P_DATAO:
+ SPRINTF("DATA OUT");
+ break;
+ case P_DATAI:
+ SPRINTF("DATA IN");
+ break;
+ case P_CMD:
+ SPRINTF("COMMAND");
+ break;
+ case P_STATUS:
+ SPRINTF("STATUS");
+ break;
+ case P_MSGO:
+ SPRINTF("MESSAGE OUT");
+ break;
+ case P_MSGI:
+ SPRINTF("MESSAGE IN");
+ break;
+ default:
+ SPRINTF("*illegal*");
+ break;
+ }
+ SPRINTF(")");
+ if(ptr->SCp.phase & (1<<16))
+ SPRINTF("; phaseend");
+ }
+ SPRINTF("; next=0x%08x\n", (unsigned int) ptr->host_scribble);
+
+ return(pos-start);
+}
+
+static int get_ports(struct Scsi_Host *shpnt, char *pos)
+{
+ char *start = pos;
+ int s;
+
+#ifdef SKIP_PORTS
+ if(HOSTDATA(shpnt)->debug & debug_skipports)
+ return;
+#endif
+
+ SPRINTF("\n%s: ", CURRENT_SC ? "on bus" : "waiting");
+
+ s=GETPORT(SCSISEQ);
+ SPRINTF("SCSISEQ (");
+ if(s & TEMODEO) SPRINTF("TARGET MODE ");
+ if(s & ENSELO) SPRINTF("SELO ");
+ if(s & ENSELI) SPRINTF("SELI ");
+ if(s & ENRESELI) SPRINTF("RESELI ");
+ if(s & ENAUTOATNO) SPRINTF("AUTOATNO ");
+ if(s & ENAUTOATNI) SPRINTF("AUTOATNI ");
+ if(s & ENAUTOATNP) SPRINTF("AUTOATNP ");
+ if(s & SCSIRSTO) SPRINTF("SCSIRSTO ");
+ SPRINTF(");");
+
+ SPRINTF(" SCSISIG (");
+ s=GETPORT(SCSISIG);
+ switch(s & P_MASK) {
+ case P_DATAO:
+ SPRINTF("DATA OUT");
+ break;
+ case P_DATAI:
+ SPRINTF("DATA IN");
+ break;
+ case P_CMD:
+ SPRINTF("COMMAND");
+ break;
+ case P_STATUS:
+ SPRINTF("STATUS");
+ break;
+ case P_MSGO:
+ SPRINTF("MESSAGE OUT");
+ break;
+ case P_MSGI:
+ SPRINTF("MESSAGE IN");
+ break;
+ default:
+ SPRINTF("*illegal*");
+ break;
+ }
+
+ SPRINTF("); ");
+
+ SPRINTF("INTSTAT (%s); ", TESTHI(DMASTAT, INTSTAT) ? "hi" : "lo");
+
+ SPRINTF("SSTAT (");
+ s=GETPORT(SSTAT0);
+ if(s & TARGET) SPRINTF("TARGET ");
+ if(s & SELDO) SPRINTF("SELDO ");
+ if(s & SELDI) SPRINTF("SELDI ");
+ if(s & SELINGO) SPRINTF("SELINGO ");
+ if(s & SWRAP) SPRINTF("SWRAP ");
+ if(s & SDONE) SPRINTF("SDONE ");
+ if(s & SPIORDY) SPRINTF("SPIORDY ");
+ if(s & DMADONE) SPRINTF("DMADONE ");
+
+ s=GETPORT(SSTAT1);
+ if(s & SELTO) SPRINTF("SELTO ");
+ if(s & ATNTARG) SPRINTF("ATNTARG ");
+ if(s & SCSIRSTI) SPRINTF("SCSIRSTI ");
+ if(s & PHASEMIS) SPRINTF("PHASEMIS ");
+ if(s & BUSFREE) SPRINTF("BUSFREE ");
+ if(s & SCSIPERR) SPRINTF("SCSIPERR ");
+ if(s & PHASECHG) SPRINTF("PHASECHG ");
+ if(s & REQINIT) SPRINTF("REQINIT ");
+ SPRINTF("); ");
+
+
+ SPRINTF("SSTAT (");
+
+ s=GETPORT(SSTAT0) & GETPORT(SIMODE0);
+
+ if(s & TARGET) SPRINTF("TARGET ");
+ if(s & SELDO) SPRINTF("SELDO ");
+ if(s & SELDI) SPRINTF("SELDI ");
+ if(s & SELINGO) SPRINTF("SELINGO ");
+ if(s & SWRAP) SPRINTF("SWRAP ");
+ if(s & SDONE) SPRINTF("SDONE ");
+ if(s & SPIORDY) SPRINTF("SPIORDY ");
+ if(s & DMADONE) SPRINTF("DMADONE ");
+
+ s=GETPORT(SSTAT1) & GETPORT(SIMODE1);
+
+ if(s & SELTO) SPRINTF("SELTO ");
+ if(s & ATNTARG) SPRINTF("ATNTARG ");
+ if(s & SCSIRSTI) SPRINTF("SCSIRSTI ");
+ if(s & PHASEMIS) SPRINTF("PHASEMIS ");
+ if(s & BUSFREE) SPRINTF("BUSFREE ");
+ if(s & SCSIPERR) SPRINTF("SCSIPERR ");
+ if(s & PHASECHG) SPRINTF("PHASECHG ");
+ if(s & REQINIT) SPRINTF("REQINIT ");
+ SPRINTF("); ");
+
+ SPRINTF("SXFRCTL0 (");
+
+ s=GETPORT(SXFRCTL0);
+ if(s & SCSIEN) SPRINTF("SCSIEN ");
+ if(s & DMAEN) SPRINTF("DMAEN ");
+ if(s & CH1) SPRINTF("CH1 ");
+ if(s & CLRSTCNT) SPRINTF("CLRSTCNT ");
+ if(s & SPIOEN) SPRINTF("SPIOEN ");
+ if(s & CLRCH1) SPRINTF("CLRCH1 ");
+ SPRINTF("); ");
+
+ SPRINTF("SIGNAL (");
+
+ s=GETPORT(SCSISIG);
+ if(s & ATNI) SPRINTF("ATNI ");
+ if(s & SELI) SPRINTF("SELI ");
+ if(s & BSYI) SPRINTF("BSYI ");
+ if(s & REQI) SPRINTF("REQI ");
+ if(s & ACKI) SPRINTF("ACKI ");
+ SPRINTF("); ");
+
+ SPRINTF("SELID (%02x), ", GETPORT(SELID));
+
+ SPRINTF("SSTAT2 (");
+
+ s=GETPORT(SSTAT2);
+ if(s & SOFFSET) SPRINTF("SOFFSET ");
+ if(s & SEMPTY) SPRINTF("SEMPTY ");
+ if(s & SFULL) SPRINTF("SFULL ");
+ SPRINTF("); SFCNT (%d); ", s & (SFULL|SFCNT));
+
+ s=GETPORT(SSTAT3);
+ SPRINTF("SCSICNT (%d), OFFCNT(%d), ", (s&0xf0)>>4, s&0x0f);
+
+ SPRINTF("SSTAT4 (");
+ s=GETPORT(SSTAT4);
+ if(s & SYNCERR) SPRINTF("SYNCERR ");
+ if(s & FWERR) SPRINTF("FWERR ");
+ if(s & FRERR) SPRINTF("FRERR ");
+ SPRINTF("); ");
+
+ SPRINTF("DMACNTRL0 (");
+ s=GETPORT(DMACNTRL0);
+ SPRINTF("%s ", s & _8BIT ? "8BIT" : "16BIT");
+ SPRINTF("%s ", s & DMA ? "DMA" : "PIO" );
+ SPRINTF("%s ", s & WRITE_READ ? "WRITE" : "READ" );
+ if(s & ENDMA) SPRINTF("ENDMA ");
+ if(s & INTEN) SPRINTF("INTEN ");
+ if(s & RSTFIFO) SPRINTF("RSTFIFO ");
+ if(s & SWINT) SPRINTF("SWINT ");
+ SPRINTF("); ");
+
+ SPRINTF("DMASTAT (");
+ s=GETPORT(DMASTAT);
+ if(s & ATDONE) SPRINTF("ATDONE ");
+ if(s & WORDRDY) SPRINTF("WORDRDY ");
+ if(s & DFIFOFULL) SPRINTF("DFIFOFULL ");
+ if(s & DFIFOEMP) SPRINTF("DFIFOEMP ");
+ SPRINTF(")\n\n");
+
+ SPRINTF("enabled interrupts (");
+
+ s=GETPORT(SIMODE0);
+ if(s & ENSELDO) SPRINTF("ENSELDO ");
+ if(s & ENSELDI) SPRINTF("ENSELDI ");
+ if(s & ENSELINGO) SPRINTF("ENSELINGO ");
+ if(s & ENSWRAP) SPRINTF("ENSWRAP ");
+ if(s & ENSDONE) SPRINTF("ENSDONE ");
+ if(s & ENSPIORDY) SPRINTF("ENSPIORDY ");
+ if(s & ENDMADONE) SPRINTF("ENDMADONE ");
+
+ s=GETPORT(SIMODE1);
+ if(s & ENSELTIMO) SPRINTF("ENSELTIMO ");
+ if(s & ENATNTARG) SPRINTF("ENATNTARG ");
+ if(s & ENPHASEMIS) SPRINTF("ENPHASEMIS ");
+ if(s & ENBUSFREE) SPRINTF("ENBUSFREE ");
+ if(s & ENSCSIPERR) SPRINTF("ENSCSIPERR ");
+ if(s & ENPHASECHG) SPRINTF("ENPHASECHG ");
+ if(s & ENREQINIT) SPRINTF("ENREQINIT ");
+ SPRINTF(")\n");
+
+ return (pos-start);
+}
+
+#undef SPRINTF
+#define SPRINTF(args...) do { if(pos < buffer + length) pos += sprintf(pos, ## args); } while(0)
+
+int aha152x_proc_info(char *buffer, char **start,
+ off_t offset, int length, int hostno, int inout)
+{
+ int i;
+ char *pos = buffer;
+ struct Scsi_Host *shpnt;
+ unsigned long flags;
+ Scsi_Cmnd *ptr;
+
+ for(i=0, shpnt= (struct Scsi_Host *) NULL; i<IRQS; i++)
+ if(aha152x_host[i] && aha152x_host[i]->host_no == hostno)
+ shpnt=aha152x_host[i];
+
+ if(!shpnt)
+ return(-ESRCH);
+
+ if(inout) /* Has data been written to the file ? */
+ return(aha152x_set_info(buffer, length, shpnt));
+
+ SPRINTF(AHA152X_REVID "\n");
+
+ save_flags(flags);
+ cli();
+
+ SPRINTF("ioports 0x%04x to 0x%04x\n",
+ shpnt->io_port, shpnt->io_port+shpnt->n_io_port-1);
+ SPRINTF("interrupt 0x%02x\n", shpnt->irq);
+ SPRINTF("disconnection/reconnection %s\n",
+ HOSTDATA(shpnt)->reconnect ? "enabled" : "disabled");
+ SPRINTF("parity checking %s\n",
+ HOSTDATA(shpnt)->parity ? "enabled" : "disabled");
+ SPRINTF("synchronous transfers %s\n",
+ HOSTDATA(shpnt)->synchronous ? "enabled" : "disabled");
+ SPRINTF("%d commands currently queued\n", HOSTDATA(shpnt)->commands);
+
+ if(HOSTDATA(shpnt)->synchronous) {
+#if 0
+ SPRINTF("synchronously operating targets (tick=%ld ns):\n",
+ 250000000/loops_per_sec);
+ for(i=0; i<8; i++)
+ if(HOSTDATA(shpnt)->syncrate[i]&0x7f)
+ SPRINTF("target %d: period %dT/%ldns; req/ack offset %d\n",
+ i,
+ (((HOSTDATA(shpnt)->syncrate[i]&0x70)>>4)+2),
+ (((HOSTDATA(shpnt)->syncrate[i]&0x70)>>4)+2)*
+ 250000000/loops_per_sec,
+ HOSTDATA(shpnt)->syncrate[i]&0x0f);
+#else
+ SPRINTF("synchronously operating targets (tick=50 ns):\n");
+ for(i=0; i<8; i++)
+ if(HOSTDATA(shpnt)->syncrate[i]&0x7f)
+ SPRINTF("target %d: period %dT/%dns; req/ack offset %d\n",
+ i,
+ (((HOSTDATA(shpnt)->syncrate[i]&0x70)>>4)+2),
+ (((HOSTDATA(shpnt)->syncrate[i]&0x70)>>4)+2)*50,
+ HOSTDATA(shpnt)->syncrate[i]&0x0f);
+#endif
+ }
+
+#ifdef DEBUG_AHA152X
+#define PDEBUG(flags,txt) if(HOSTDATA(shpnt)->debug & flags) SPRINTF("(%s) ", txt);
+
+ SPRINTF("enabled debugging options: ");
+
+ PDEBUG(debug_skipports, "skip ports");
+ PDEBUG(debug_queue, "queue");
+ PDEBUG(debug_intr, "interrupt");
+ PDEBUG(debug_selection, "selection");
+ PDEBUG(debug_msgo, "message out");
+ PDEBUG(debug_msgi, "message in");
+ PDEBUG(debug_status, "status");
+ PDEBUG(debug_cmd, "command");
+ PDEBUG(debug_datai, "data in");
+ PDEBUG(debug_datao, "data out");
+ PDEBUG(debug_abort, "abort");
+ PDEBUG(debug_done, "done");
+ PDEBUG(debug_biosparam, "bios parameters");
+ PDEBUG(debug_phases, "phases");
+ PDEBUG(debug_queues, "queues");
+ PDEBUG(debug_reset, "reset");
+
+ SPRINTF("\n");
+#endif
+
+ SPRINTF("\nqueue status:\n");
+ if(ISSUE_SC) {
+ SPRINTF("not yet issued commands:\n");
+ for(ptr=ISSUE_SC; ptr; ptr = (Scsi_Cmnd *) ptr->host_scribble)
+ pos += get_command(pos, ptr);
+ } else
+ SPRINTF("no not yet issued commands\n");
+
+ if(CURRENT_SC) {
+ SPRINTF("current command:\n");
+ pos += get_command(pos, CURRENT_SC);
+ } else
+ SPRINTF("no current command\n");
+
+ if(DISCONNECTED_SC) {
+ SPRINTF("disconnected commands:\n");
+ for(ptr=DISCONNECTED_SC; ptr; ptr = (Scsi_Cmnd *) ptr->host_scribble)
+ pos += get_command(pos, ptr);
+ } else
+ SPRINTF("no disconnected commands\n");
+
+ restore_flags(flags);
+
+ pos += get_ports(shpnt, pos);
+
+ *start=buffer+offset;
+ if (pos - buffer < offset)
+ return 0;
+ else if (pos - buffer - offset < length)
+ return pos - buffer - offset;
+ else
+ return length;
+}
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = AHA152X;
+
+#include "scsi_module.c"
+#endif
diff --git a/linux/dev/drivers/scsi/eata_dma.c b/linux/dev/drivers/scsi/eata_dma.c
new file mode 100644
index 0000000..b561208
--- /dev/null
+++ b/linux/dev/drivers/scsi/eata_dma.c
@@ -0,0 +1,1607 @@
+/************************************************************
+ * *
+ * Linux EATA SCSI driver *
+ * *
+ * based on the CAM document CAM/89-004 rev. 2.0c, *
+ * DPT's driver kit, some internal documents and source, *
+ * and several other Linux scsi drivers and kernel docs. *
+ * *
+ * The driver currently: *
+ * -supports all ISA based EATA-DMA boards *
+ * like PM2011, PM2021, PM2041, PM3021 *
+ * -supports all EISA based EATA-DMA boards *
+ * like PM2012B, PM2022, PM2122, PM2322, PM2042, *
+ * PM3122, PM3222, PM3332 *
+ * -supports all PCI based EATA-DMA boards *
+ * like PM2024, PM2124, PM2044, PM2144, PM3224, *
+ * PM3334 *
+ * -supports the Wide, Ultra Wide and Differential *
+ * versions of the boards *
+ * -supports multiple HBAs with & without IRQ sharing *
+ * -supports all SCSI channels on multi channel boards *
+ * -supports ix86 and MIPS, untested on ALPHA *
+ * -needs identical IDs on all channels of a HBA *
+ * -can be loaded as module *
+ * -displays statistical and hardware information *
+ * in /proc/scsi/eata_dma *
+ * -provides rudimentary latency measurement *
+ * possibilities via /proc/scsi/eata_dma/<hostnum> *
+ * *
+ * (c)1993-96 Michael Neuffer *
+ * mike@i-Connect.Net *
+ * neuffer@mail.uni-mainz.de *
+ * *
+ * 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 of the License, 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 kernel; if not, write to *
+ * the Free Software Foundation, Inc., 675 Mass Ave, *
+ * Cambridge, MA 02139, USA. *
+ * *
+ * I have to thank DPT for their excellent support. I took *
+ * me almost a year and a stopover at their HQ, on my first *
+ * trip to the USA, to get it, but since then they've been *
+ * very helpful and tried to give me all the infos and *
+ * support I need. *
+ * *
+ * Thanks also to Simon Shapiro, Greg Hosler and Mike *
+ * Jagdis who did a lot of testing and found quite a number *
+ * of bugs during the development. *
+ ************************************************************
+ * last change: 96/10/21 OS: Linux 2.0.23 *
+ ************************************************************/
+
+/* Look in eata_dma.h for configuration and revision information */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/in.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#include <linux/delay.h>
+#include <asm/byteorder.h>
+#include <asm/types.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#ifdef MACH
+#define flush_cache_all()
+#else
+#include <asm/pgtable.h>
+#endif
+#ifdef __mips__
+#include <asm/cachectl.h>
+#endif
+#include <linux/blk.h>
+#include "scsi.h"
+#include "sd.h"
+#include "hosts.h"
+#include "eata_dma.h"
+#include "eata_dma_proc.h"
+
+#include <linux/stat.h>
+#include <linux/config.h> /* for CONFIG_PCI */
+
+struct proc_dir_entry proc_scsi_eata_dma = {
+ PROC_SCSI_EATA, 8, "eata_dma",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+static u32 ISAbases[] =
+{0x1F0, 0x170, 0x330, 0x230};
+static unchar EISAbases[] =
+{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
+static uint registered_HBAs = 0;
+static struct Scsi_Host *last_HBA = NULL;
+static struct Scsi_Host *first_HBA = NULL;
+static unchar reg_IRQ[] =
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+static unchar reg_IRQL[] =
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+static struct eata_sp *status = 0; /* Statuspacket array */
+static void *dma_scratch = 0;
+
+static struct eata_register *fake_int_base;
+static int fake_int_result;
+static int fake_int_happened;
+
+static ulong int_counter = 0;
+static ulong queue_counter = 0;
+
+void eata_scsi_done (Scsi_Cmnd * scmd)
+{
+ scmd->request.rq_status = RQ_SCSI_DONE;
+
+ if (scmd->request.sem != NULL)
+ up(scmd->request.sem);
+
+ return;
+}
+
+void eata_fake_int_handler(s32 irq, void *dev_id, struct pt_regs * regs)
+{
+ fake_int_result = inb((ulong)fake_int_base + HA_RSTATUS);
+ fake_int_happened = TRUE;
+ DBG(DBG_INTR3, printk("eata_fake_int_handler called irq%d base %p"
+ " res %#x\n", irq, fake_int_base, fake_int_result));
+ return;
+}
+
+#include "eata_dma_proc.c"
+
+#ifdef MODULE
+int eata_release(struct Scsi_Host *sh)
+{
+ uint i;
+ if (sh->irq && reg_IRQ[sh->irq] == 1) free_irq(sh->irq, NULL);
+ else reg_IRQ[sh->irq]--;
+
+ scsi_init_free((void *)status, 512);
+ scsi_init_free((void *)dma_scratch - 4, 1024);
+ for (i = 0; i < sh->can_queue; i++){ /* Free all SG arrays */
+ if(SD(sh)->ccb[i].sg_list != NULL)
+ scsi_init_free((void *) SD(sh)->ccb[i].sg_list,
+ sh->sg_tablesize * sizeof(struct eata_sg_list));
+ }
+
+ if (SD(sh)->channel == 0) {
+ if (sh->dma_channel != BUSMASTER) free_dma(sh->dma_channel);
+ if (sh->io_port && sh->n_io_port)
+ release_region(sh->io_port, sh->n_io_port);
+ }
+ return(TRUE);
+}
+#endif
+
+
+inline void eata_latency_in(struct eata_ccb *cp, hostdata *hd)
+{
+ uint time;
+ time = jiffies - cp->timestamp;
+ if(hd->all_lat[1] > time)
+ hd->all_lat[1] = time;
+ if(hd->all_lat[2] < time)
+ hd->all_lat[2] = time;
+ hd->all_lat[3] += time;
+ hd->all_lat[0]++;
+ if((cp->rw_latency) == WRITE) { /* was WRITE */
+ if(hd->writes_lat[cp->sizeindex][1] > time)
+ hd->writes_lat[cp->sizeindex][1] = time;
+ if(hd->writes_lat[cp->sizeindex][2] < time)
+ hd->writes_lat[cp->sizeindex][2] = time;
+ hd->writes_lat[cp->sizeindex][3] += time;
+ hd->writes_lat[cp->sizeindex][0]++;
+ } else if((cp->rw_latency) == READ) {
+ if(hd->reads_lat[cp->sizeindex][1] > time)
+ hd->reads_lat[cp->sizeindex][1] = time;
+ if(hd->reads_lat[cp->sizeindex][2] < time)
+ hd->reads_lat[cp->sizeindex][2] = time;
+ hd->reads_lat[cp->sizeindex][3] += time;
+ hd->reads_lat[cp->sizeindex][0]++;
+ }
+}
+
+inline void eata_latency_out(struct eata_ccb *cp, Scsi_Cmnd *cmd)
+{
+ int x, z;
+ short *sho;
+ long *lon;
+ x = 0; /* just to keep GCC quiet */
+ cp->timestamp = jiffies; /* For latency measurements */
+ switch(cmd->cmnd[0]) {
+ case WRITE_6:
+ x = cmd->cmnd[4]/2;
+ cp->rw_latency = WRITE;
+ break;
+ case READ_6:
+ x = cmd->cmnd[4]/2;
+ cp->rw_latency = READ;
+ break;
+ case WRITE_10:
+ sho = (short *) &cmd->cmnd[7];
+ x = ntohs(*sho)/2;
+ cp->rw_latency = WRITE;
+ break;
+ case READ_10:
+ sho = (short *) &cmd->cmnd[7];
+ x = ntohs(*sho)/2;
+ cp->rw_latency = READ;
+ break;
+ case WRITE_12:
+ lon = (long *) &cmd->cmnd[6];
+ x = ntohl(*lon)/2;
+ cp->rw_latency = WRITE;
+ break;
+ case READ_12:
+ lon = (long *) &cmd->cmnd[6];
+ x = ntohl(*lon)/2;
+ cp->rw_latency = READ;
+ break;
+ default:
+ cp->rw_latency = OTHER;
+ break;
+ }
+ if (cmd->cmnd[0] == WRITE_6 || cmd->cmnd[0] == WRITE_10 ||
+ cmd->cmnd[0] == WRITE_12 || cmd->cmnd[0] == READ_6 ||
+ cmd->cmnd[0] == READ_10 || cmd->cmnd[0] == READ_12) {
+ for(z = 0; (x > (1 << z)) && (z <= 11); z++)
+ /* nothing */;
+ cp->sizeindex = z;
+ }
+}
+
+
+void eata_int_handler(int irq, void *dev_id, struct pt_regs * regs)
+{
+ uint i, result = 0;
+ uint hba_stat, scsi_stat, eata_stat;
+ Scsi_Cmnd *cmd;
+ struct eata_ccb *ccb;
+ struct eata_sp *sp;
+ uint base;
+ uint x;
+ struct Scsi_Host *sh;
+
+ for (x = 1, sh = first_HBA; x <= registered_HBAs; x++, sh = SD(sh)->next) {
+ if (sh->irq != irq)
+ continue;
+
+ while(inb((uint)sh->base + HA_RAUXSTAT) & HA_AIRQ) {
+
+ int_counter++;
+
+ sp = &SD(sh)->sp;
+#ifdef __mips__
+ sys_cacheflush(sp, sizeof(struct eata_sp), 2);
+#endif
+ ccb = sp->ccb;
+
+ if(ccb == NULL) {
+ eata_stat = inb((uint)sh->base + HA_RSTATUS);
+ printk("eata_dma: int_handler, Spurious IRQ %d "
+ "received. CCB pointer not set.\n", irq);
+ break;
+ }
+
+ cmd = ccb->cmd;
+ base = (uint) cmd->host->base;
+ hba_stat = sp->hba_stat;
+
+ scsi_stat = (sp->scsi_stat >> 1) & 0x1f;
+
+ if (sp->EOC == FALSE) {
+ eata_stat = inb(base + HA_RSTATUS);
+ printk(KERN_WARNING "eata_dma: int_handler, board: %x cmd %lx "
+ "returned unfinished.\n"
+ "EATA: %x HBA: %x SCSI: %x spadr %lx spadrirq %lx, "
+ "irq%d\n", base, (long)ccb, eata_stat, hba_stat,
+ scsi_stat,(long)&status, (long)&status[irq], irq);
+ cmd->result = DID_ERROR << 16;
+ ccb->status = FREE;
+ cmd->scsi_done(cmd);
+ break;
+ }
+
+ sp->EOC = FALSE; /* Clean out this flag */
+
+ if (ccb->status == LOCKED || ccb->status == RESET) {
+ printk("eata_dma: int_handler, reseted command pid %ld returned"
+ "\n", cmd->pid);
+ DBG(DBG_INTR && DBG_DELAY, DELAY(1));
+ }
+
+ eata_stat = inb(base + HA_RSTATUS);
+ DBG(DBG_INTR, printk("IRQ %d received, base %#.4x, pid %ld, "
+ "target: %x, lun: %x, ea_s: %#.2x, hba_s: "
+ "%#.2x \n", irq, base, cmd->pid, cmd->target,
+ cmd->lun, eata_stat, hba_stat));
+
+ switch (hba_stat) {
+ case HA_NO_ERROR: /* NO Error */
+ if(HD(cmd)->do_latency == TRUE && ccb->timestamp)
+ eata_latency_in(ccb, HD(cmd));
+ result = DID_OK << 16;
+ break;
+ case HA_ERR_SEL_TO: /* Selection Timeout */
+ case HA_ERR_CMD_TO: /* Command Timeout */
+ result = DID_TIME_OUT << 16;
+ break;
+ case HA_BUS_RESET: /* SCSI Bus Reset Received */
+ result = DID_RESET << 16;
+ DBG(DBG_STATUS, printk(KERN_WARNING "scsi%d: BUS RESET "
+ "received on cmd %ld\n",
+ HD(cmd)->HBA_number, cmd->pid));
+ break;
+ case HA_INIT_POWERUP: /* Initial Controller Power-up */
+ if (cmd->device->type != TYPE_TAPE)
+ result = DID_BUS_BUSY << 16;
+ else
+ result = DID_ERROR << 16;
+
+ for (i = 0; i < MAXTARGET; i++)
+ DBG(DBG_STATUS, printk(KERN_DEBUG "scsi%d: cmd pid %ld "
+ "returned with INIT_POWERUP\n",
+ HD(cmd)->HBA_number, cmd->pid));
+ break;
+ case HA_CP_ABORT_NA:
+ case HA_CP_ABORTED:
+ result = DID_ABORT << 16;
+ DBG(DBG_STATUS, printk(KERN_WARNING "scsi%d: aborted cmd "
+ "returned\n", HD(cmd)->HBA_number));
+ break;
+ case HA_CP_RESET_NA:
+ case HA_CP_RESET:
+ HD(cmd)->resetlevel[cmd->channel] = 0;
+ result = DID_RESET << 16;
+ DBG(DBG_STATUS, printk(KERN_WARNING "scsi%d: reseted cmd "
+ "pid %ldreturned\n",
+ HD(cmd)->HBA_number, cmd->pid));
+ case HA_SCSI_HUNG: /* SCSI Hung */
+ printk(KERN_ERR "scsi%d: SCSI hung\n", HD(cmd)->HBA_number);
+ result = DID_ERROR << 16;
+ break;
+ case HA_RSENSE_FAIL: /* Auto Request-Sense Failed */
+ DBG(DBG_STATUS, printk(KERN_ERR "scsi%d: Auto Request Sense "
+ "Failed\n", HD(cmd)->HBA_number));
+ result = DID_ERROR << 16;
+ break;
+ case HA_UNX_BUSPHASE: /* Unexpected Bus Phase */
+ case HA_UNX_BUS_FREE: /* Unexpected Bus Free */
+ case HA_BUS_PARITY: /* Bus Parity Error */
+ case HA_UNX_MSGRJCT: /* Unexpected Message Reject */
+ case HA_RESET_STUCK: /* SCSI Bus Reset Stuck */
+ case HA_PARITY_ERR: /* Controller Ram Parity */
+ default:
+ result = DID_ERROR << 16;
+ break;
+ }
+ cmd->result = result | (scsi_stat << 1);
+
+#if DBG_INTR2
+ if (scsi_stat || result || hba_stat || eata_stat != 0x50
+ || cmd->scsi_done == NULL || cmd->device->id == 7)
+ printk("HBA: %d, channel %d, id: %d, lun %d, pid %ld:\n"
+ "eata_stat %#x, hba_stat %#.2x, scsi_stat %#.2x, "
+ "sense_key: %#x, result: %#.8x\n", x,
+ cmd->device->channel, cmd->device->id, cmd->device->lun,
+ cmd->pid, eata_stat, hba_stat, scsi_stat,
+ cmd->sense_buffer[2] & 0xf, cmd->result);
+ DBG(DBG_INTR&&DBG_DELAY,DELAY(1));
+#endif
+
+ ccb->status = FREE; /* now we can release the slot */
+ cmd->scsi_done(cmd);
+ }
+ }
+
+ return;
+}
+
+inline int eata_send_command(u32 addr, u32 base, u8 command)
+{
+ long loop = R_LIMIT;
+
+ while (inb(base + HA_RAUXSTAT) & HA_ABUSY)
+ if (--loop == 0)
+ return(FALSE);
+
+ if(addr != (u32) NULL)
+ addr = virt_to_bus((void *)addr);
+
+ /*
+ * This is overkill.....but the MIPSen seem to need this
+ * and it will be optimized away for i86 and ALPHA machines.
+ */
+ flush_cache_all();
+
+ /* And now the address in nice little byte chunks */
+#ifdef __LITTLE_ENDIAN
+ outb(addr, base + HA_WDMAADDR);
+ outb(addr >> 8, base + HA_WDMAADDR + 1);
+ outb(addr >> 16, base + HA_WDMAADDR + 2);
+ outb(addr >> 24, base + HA_WDMAADDR + 3);
+#else
+ outb(addr >> 24, base + HA_WDMAADDR);
+ outb(addr >> 16, base + HA_WDMAADDR + 1);
+ outb(addr >> 8, base + HA_WDMAADDR + 2);
+ outb(addr, base + HA_WDMAADDR + 3);
+#endif
+ outb(command, base + HA_WCOMMAND);
+ return(TRUE);
+}
+
+inline int eata_send_immediate(u32 base, u32 addr, u8 ifc, u8 code, u8 code2)
+{
+ if(addr != (u32) NULL)
+ addr = virt_to_bus((void *)addr);
+
+ /*
+ * This is overkill.....but the MIPSen seem to need this
+ * and it will be optimized away for i86 and ALPHA machines.
+ */
+ flush_cache_all();
+
+ outb(0x0, base + HA_WDMAADDR - 1);
+ if(addr){
+#ifdef __LITTLE_ENDIAN
+ outb(addr, base + HA_WDMAADDR);
+ outb(addr >> 8, base + HA_WDMAADDR + 1);
+ outb(addr >> 16, base + HA_WDMAADDR + 2);
+ outb(addr >> 24, base + HA_WDMAADDR + 3);
+#else
+ outb(addr >> 24, base + HA_WDMAADDR);
+ outb(addr >> 16, base + HA_WDMAADDR + 1);
+ outb(addr >> 8, base + HA_WDMAADDR + 2);
+ outb(addr, base + HA_WDMAADDR + 3);
+#endif
+ } else {
+ outb(0x0, base + HA_WDMAADDR);
+ outb(0x0, base + HA_WDMAADDR + 1);
+ outb(code2, base + HA_WCODE2);
+ outb(code, base + HA_WCODE);
+ }
+
+ outb(ifc, base + HA_WIFC);
+ outb(EATA_CMD_IMMEDIATE, base + HA_WCOMMAND);
+ return(TRUE);
+}
+
+int eata_queue(Scsi_Cmnd * cmd, void (* done) (Scsi_Cmnd *))
+{
+ unsigned int i, x, y;
+ ulong flags;
+ hostdata *hd;
+ struct Scsi_Host *sh;
+ struct eata_ccb *ccb;
+ struct scatterlist *sl;
+
+
+ save_flags(flags);
+ cli();
+
+#if 0
+ for (x = 1, sh = first_HBA; x <= registered_HBAs; x++, sh = SD(sh)->next) {
+ if(inb((uint)sh->base + HA_RAUXSTAT) & HA_AIRQ) {
+ printk("eata_dma: scsi%d interrupt pending in eata_queue.\n"
+ " Calling interrupt handler.\n", sh->host_no);
+ eata_int_handler(sh->irq, 0, 0);
+ }
+ }
+#endif
+
+ queue_counter++;
+
+ hd = HD(cmd);
+ sh = cmd->host;
+
+ if (cmd->cmnd[0] == REQUEST_SENSE && cmd->sense_buffer[0] != 0) {
+ DBG(DBG_REQSENSE, printk(KERN_DEBUG "Tried to REQUEST SENSE\n"));
+ cmd->result = DID_OK << 16;
+ done(cmd);
+
+ return(0);
+ }
+
+ /* check for free slot */
+ for (y = hd->last_ccb + 1, x = 0; x < sh->can_queue; x++, y++) {
+ if (y >= sh->can_queue)
+ y = 0;
+ if (hd->ccb[y].status == FREE)
+ break;
+ }
+
+ hd->last_ccb = y;
+
+ if (x >= sh->can_queue) {
+ cmd->result = DID_BUS_BUSY << 16;
+ DBG(DBG_QUEUE && DBG_ABNORM,
+ printk(KERN_CRIT "eata_queue pid %ld, HBA QUEUE FULL..., "
+ "returning DID_BUS_BUSY\n", cmd->pid));
+ done(cmd);
+ restore_flags(flags);
+ return(0);
+ }
+ ccb = &hd->ccb[y];
+
+ memset(ccb, 0, sizeof(struct eata_ccb) - sizeof(struct eata_sg_list *));
+
+ ccb->status = USED; /* claim free slot */
+
+ restore_flags(flags);
+
+ DBG(DBG_QUEUE, printk("eata_queue pid %ld, target: %x, lun: %x, y %d\n",
+ cmd->pid, cmd->target, cmd->lun, y));
+ DBG(DBG_QUEUE && DBG_DELAY, DELAY(1));
+
+ if(hd->do_latency == TRUE)
+ eata_latency_out(ccb, cmd);
+
+ cmd->scsi_done = (void *)done;
+
+ switch (cmd->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 UPDATE_BLOCK: case WRITE_LONG: case WRITE_SAME:
+ case SEARCH_HIGH_12: case SEARCH_EQUAL_12: case SEARCH_LOW_12:
+ case WRITE_12: case WRITE_VERIFY_12: case SET_WINDOW:
+ case MEDIUM_SCAN: case SEND_VOLUME_TAG:
+ case 0xea: /* alternate number for WRITE LONG */
+ ccb->DataOut = TRUE; /* Output mode */
+ break;
+ case TEST_UNIT_READY:
+ default:
+ ccb->DataIn = TRUE; /* Input mode */
+ }
+
+ /* FIXME: This will have to be changed once the midlevel driver
+ * allows different HBA IDs on every channel.
+ */
+ if (cmd->target == sh->this_id)
+ ccb->Interpret = TRUE; /* Interpret command */
+
+ if (cmd->use_sg) {
+ ccb->scatter = TRUE; /* SG mode */
+ if (ccb->sg_list == NULL) {
+ ccb->sg_list = kmalloc(sh->sg_tablesize * sizeof(struct eata_sg_list),
+ GFP_ATOMIC | GFP_DMA);
+ }
+ if (ccb->sg_list == NULL)
+ panic("eata_dma: Run out of DMA memory for SG lists !\n");
+ ccb->cp_dataDMA = htonl(virt_to_bus(ccb->sg_list));
+
+ ccb->cp_datalen = htonl(cmd->use_sg * sizeof(struct eata_sg_list));
+ sl=(struct scatterlist *)cmd->request_buffer;
+ for(i = 0; i < cmd->use_sg; i++, sl++){
+ ccb->sg_list[i].data = htonl(virt_to_bus(sl->address));
+ ccb->sg_list[i].len = htonl((u32) sl->length);
+ }
+ } else {
+ ccb->scatter = FALSE;
+ ccb->cp_datalen = htonl(cmd->request_bufflen);
+ ccb->cp_dataDMA = htonl(virt_to_bus(cmd->request_buffer));
+ }
+
+ ccb->Auto_Req_Sen = TRUE;
+ ccb->cp_reqDMA = htonl(virt_to_bus(cmd->sense_buffer));
+ ccb->reqlen = sizeof(cmd->sense_buffer);
+
+ ccb->cp_id = cmd->target;
+ ccb->cp_channel = cmd->channel;
+ ccb->cp_lun = cmd->lun;
+ ccb->cp_dispri = TRUE;
+ ccb->cp_identify = TRUE;
+ memcpy(ccb->cp_cdb, cmd->cmnd, cmd->cmd_len);
+
+ ccb->cp_statDMA = htonl(virt_to_bus(&(hd->sp)));
+
+ ccb->cp_viraddr = ccb; /* This will be passed thru, so we don't need to
+ * convert it */
+ ccb->cmd = cmd;
+ cmd->host_scribble = (char *)&hd->ccb[y];
+
+ if(eata_send_command((u32) ccb, (u32) sh->base, EATA_CMD_DMA_SEND_CP) == FALSE) {
+ cmd->result = DID_BUS_BUSY << 16;
+ DBG(DBG_QUEUE && DBG_ABNORM,
+ printk("eata_queue target %d, pid %ld, HBA busy, "
+ "returning DID_BUS_BUSY\n",cmd->target, cmd->pid));
+ ccb->status = FREE;
+ done(cmd);
+ return(0);
+ }
+ DBG(DBG_QUEUE, printk("Queued base %#.4x pid: %ld target: %x lun: %x "
+ "slot %d irq %d\n", (s32)sh->base, cmd->pid,
+ cmd->target, cmd->lun, y, sh->irq));
+ DBG(DBG_QUEUE && DBG_DELAY, DELAY(1));
+
+ return(0);
+}
+
+
+int eata_abort(Scsi_Cmnd * cmd)
+{
+ ulong loop = HZ / 2;
+ ulong flags;
+ int x;
+ struct Scsi_Host *sh;
+
+ save_flags(flags);
+ cli();
+
+ DBG(DBG_ABNORM, printk("eata_abort called pid: %ld target: %x lun: %x"
+ " reason %x\n", cmd->pid, cmd->target, cmd->lun,
+ cmd->abort_reason));
+ DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
+
+ /* Some interrupt controllers seem to loose interrupts */
+ for (x = 1, sh = first_HBA; x <= registered_HBAs; x++, sh = SD(sh)->next) {
+ if(inb((uint)sh->base + HA_RAUXSTAT) & HA_AIRQ) {
+ printk("eata_dma: scsi%d interrupt pending in eata_abort.\n"
+ " Calling interrupt handler.\n", sh->host_no);
+ eata_int_handler(sh->irq, 0, 0);
+ }
+ }
+
+ while (inb((u32)(cmd->host->base) + HA_RAUXSTAT) & HA_ABUSY) {
+ if (--loop == 0) {
+ printk("eata_dma: abort, timeout error.\n");
+ DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
+ restore_flags(flags);
+ return (SCSI_ABORT_ERROR);
+ }
+ }
+ if (CD(cmd)->status == RESET) {
+ printk("eata_dma: abort, command reset error.\n");
+ DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
+ restore_flags(flags);
+ return (SCSI_ABORT_ERROR);
+ }
+ if (CD(cmd)->status == LOCKED) {
+ DBG(DBG_ABNORM, printk("eata_dma: abort, queue slot locked.\n"));
+ DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
+ restore_flags(flags);
+ return (SCSI_ABORT_NOT_RUNNING);
+ }
+ if (CD(cmd)->status == USED) {
+ DBG(DBG_ABNORM, printk("Returning: SCSI_ABORT_BUSY\n"));
+ restore_flags(flags);
+ return (SCSI_ABORT_BUSY); /* SNOOZE */
+ }
+ if (CD(cmd)->status == FREE) {
+ DBG(DBG_ABNORM, printk("Returning: SCSI_ABORT_NOT_RUNNING\n"));
+ restore_flags(flags);
+ return (SCSI_ABORT_NOT_RUNNING);
+ }
+ restore_flags(flags);
+ panic("eata_dma: abort: invalid slot status\n");
+}
+
+int eata_reset(Scsi_Cmnd * cmd, unsigned int resetflags)
+{
+ uint x;
+ ulong loop = loops_per_sec / 3;
+ ulong flags;
+ unchar success = FALSE;
+ Scsi_Cmnd *sp;
+ struct Scsi_Host *sh;
+
+ save_flags(flags);
+ cli();
+
+ DBG(DBG_ABNORM, printk("eata_reset called pid:%ld target: %x lun: %x"
+ " reason %x\n", cmd->pid, cmd->target, cmd->lun,
+ cmd->abort_reason));
+
+ for (x = 1, sh = first_HBA; x <= registered_HBAs; x++, sh = SD(sh)->next) {
+ if(inb((uint)sh->base + HA_RAUXSTAT) & HA_AIRQ) {
+ printk("eata_dma: scsi%d interrupt pending in eata_reset.\n"
+ " Calling interrupt handler.\n", sh->host_no);
+ eata_int_handler(sh->irq, 0, 0);
+ }
+ }
+
+ if (HD(cmd)->state == RESET) {
+ printk("eata_reset: exit, already in reset.\n");
+ restore_flags(flags);
+ DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
+ return (SCSI_RESET_ERROR);
+ }
+
+ while (inb((u32)(cmd->host->base) + HA_RAUXSTAT) & HA_ABUSY)
+ if (--loop == 0) {
+ printk("eata_reset: exit, timeout error.\n");
+ restore_flags(flags);
+ DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
+ return (SCSI_RESET_ERROR);
+ }
+
+ for (x = 0; x < cmd->host->can_queue; x++) {
+ if (HD(cmd)->ccb[x].status == FREE)
+ continue;
+
+ if (HD(cmd)->ccb[x].status == LOCKED) {
+ HD(cmd)->ccb[x].status = FREE;
+ printk("eata_reset: locked slot %d forced free.\n", x);
+ DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
+ continue;
+ }
+
+
+ sp = HD(cmd)->ccb[x].cmd;
+ HD(cmd)->ccb[x].status = RESET;
+
+ if (sp == NULL)
+ panic("eata_reset: slot %d, sp==NULL.\n", x);
+
+ printk("eata_reset: slot %d in reset, pid %ld.\n", x, sp->pid);
+
+ DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
+
+ if (sp == cmd)
+ success = TRUE;
+ }
+
+ /* hard reset the HBA */
+ inb((u32) (cmd->host->base) + HA_RSTATUS); /* This might cause trouble */
+ eata_send_command(0, (u32) cmd->host->base, EATA_CMD_RESET);
+
+ HD(cmd)->state = RESET;
+
+ DBG(DBG_ABNORM, printk("eata_reset: board reset done, enabling "
+ "interrupts.\n"));
+
+ DELAY(2); /* In theorie we should get interrupts and set free all
+ * used queueslots */
+
+ DBG(DBG_ABNORM, printk("eata_reset: interrupts disabled again.\n"));
+ DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
+
+ for (x = 0; x < cmd->host->can_queue; x++) {
+
+ /* Skip slots already set free by interrupt and those that
+ * are still LOCKED from the last reset */
+ if (HD(cmd)->ccb[x].status != RESET)
+ continue;
+
+ sp = HD(cmd)->ccb[x].cmd;
+ sp->result = DID_RESET << 16;
+
+ /* This mailbox is still waiting for its interrupt */
+ HD(cmd)->ccb[x].status = LOCKED;
+
+ printk("eata_reset: slot %d locked, DID_RESET, pid %ld done.\n",
+ x, sp->pid);
+ DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
+
+ sp->scsi_done(sp);
+ }
+
+ HD(cmd)->state = FALSE;
+ restore_flags(flags);
+
+ if (success) {
+ DBG(DBG_ABNORM, printk("eata_reset: exit, pending.\n"));
+ DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
+ return (SCSI_RESET_PENDING);
+ } else {
+ DBG(DBG_ABNORM, printk("eata_reset: exit, wakeup.\n"));
+ DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
+ return (SCSI_RESET_PUNT);
+ }
+}
+
+/* Here we try to determine the optimum queue depth for
+ * each attached device.
+ *
+ * At the moment the algorithm is rather simple
+ */
+static void eata_select_queue_depths(struct Scsi_Host *host,
+ Scsi_Device *devicelist)
+{
+ Scsi_Device *device;
+ int devcount = 0;
+ int factor = 0;
+
+#if CRIPPLE_QUEUE
+ for(device = devicelist; device != NULL; device = device->next) {
+ if(device->host == host)
+ device->queue_depth = 2;
+ }
+#else
+ /* First we do a sample run go find out what we have */
+ for(device = devicelist; device != NULL; device = device->next) {
+ if (device->host == host) {
+ devcount++;
+ switch(device->type) {
+ case TYPE_DISK:
+ case TYPE_MOD:
+ factor += TYPE_DISK_QUEUE;
+ break;
+ case TYPE_TAPE:
+ factor += TYPE_TAPE_QUEUE;
+ break;
+ case TYPE_WORM:
+ case TYPE_ROM:
+ factor += TYPE_ROM_QUEUE;
+ break;
+ case TYPE_PROCESSOR:
+ case TYPE_SCANNER:
+ default:
+ factor += TYPE_OTHER_QUEUE;
+ break;
+ }
+ }
+ }
+
+ DBG(DBG_REGISTER, printk(KERN_DEBUG "scsi%d: needed queueslots %d\n",
+ host->host_no, factor));
+
+ if(factor == 0) /* We don't want to get a DIV BY ZERO error */
+ factor = 1;
+
+ factor = (SD(host)->queuesize * 10) / factor;
+
+ DBG(DBG_REGISTER, printk(KERN_DEBUG "scsi%d: using factor %dE-1\n",
+ host->host_no, factor));
+
+ /* Now that have the factor we can set the individual queuesizes */
+ for(device = devicelist; device != NULL; device = device->next) {
+ if(device->host == host) {
+ if(SD(device->host)->bustype != IS_ISA){
+ switch(device->type) {
+ case TYPE_DISK:
+ case TYPE_MOD:
+ device->queue_depth = (TYPE_DISK_QUEUE * factor) / 10;
+ break;
+ case TYPE_TAPE:
+ device->queue_depth = (TYPE_TAPE_QUEUE * factor) / 10;
+ break;
+ case TYPE_WORM:
+ case TYPE_ROM:
+ device->queue_depth = (TYPE_ROM_QUEUE * factor) / 10;
+ break;
+ case TYPE_PROCESSOR:
+ case TYPE_SCANNER:
+ default:
+ device->queue_depth = (TYPE_OTHER_QUEUE * factor) / 10;
+ break;
+ }
+ } else /* ISA forces us to limit the queue depth because of the
+ * bounce buffer memory overhead. I know this is cruel */
+ device->queue_depth = 2;
+
+ /*
+ * It showed that we need to set an upper limit of commands
+ * we can allow to queue for a single device on the bus.
+ * If we get above that limit, the broken midlevel SCSI code
+ * will produce bogus timeouts and aborts en masse. :-(
+ */
+ if(device->queue_depth > UPPER_DEVICE_QUEUE_LIMIT)
+ device->queue_depth = UPPER_DEVICE_QUEUE_LIMIT;
+ if(device->queue_depth == 0)
+ device->queue_depth = 1;
+
+ printk(KERN_INFO "scsi%d: queue depth for target %d on channel %d "
+ "set to %d\n", host->host_no, device->id, device->channel,
+ device->queue_depth);
+ }
+ }
+#endif
+}
+
+#if CHECK_BLINK
+int check_blink_state(long base)
+{
+ ushort loops = 10;
+ u32 blinkindicator;
+ u32 state = 0x12345678;
+ u32 oldstate = 0;
+
+ blinkindicator = htonl(0x54504442);
+ while ((loops--) && (state != oldstate)) {
+ oldstate = state;
+ state = inl((uint) base + 1);
+ }
+
+ DBG(DBG_BLINK, printk("Did Blink check. Status: %d\n",
+ (state == oldstate) && (state == blinkindicator)));
+
+ if ((state == oldstate) && (state == blinkindicator))
+ return(TRUE);
+ else
+ return (FALSE);
+}
+#endif
+
+char * get_board_data(u32 base, u32 irq, u32 id)
+{
+ struct eata_ccb *cp;
+ struct eata_sp *sp;
+ static char *buff;
+ ulong i;
+
+ cp = (struct eata_ccb *) scsi_init_malloc(sizeof(struct eata_ccb),
+ GFP_ATOMIC | GFP_DMA);
+ sp = (struct eata_sp *) scsi_init_malloc(sizeof(struct eata_sp),
+ GFP_ATOMIC | GFP_DMA);
+
+ buff = dma_scratch;
+
+ memset(cp, 0, sizeof(struct eata_ccb));
+ memset(sp, 0, sizeof(struct eata_sp));
+ memset(buff, 0, 256);
+
+ cp->DataIn = TRUE;
+ cp->Interpret = TRUE; /* Interpret command */
+ cp->cp_dispri = TRUE;
+ cp->cp_identify = TRUE;
+
+ cp->cp_datalen = htonl(56);
+ cp->cp_dataDMA = htonl(virt_to_bus(buff));
+ cp->cp_statDMA = htonl(virt_to_bus(sp));
+ cp->cp_viraddr = cp;
+
+ cp->cp_id = id;
+ cp->cp_lun = 0;
+
+ cp->cp_cdb[0] = INQUIRY;
+ cp->cp_cdb[1] = 0;
+ cp->cp_cdb[2] = 0;
+ cp->cp_cdb[3] = 0;
+ cp->cp_cdb[4] = 56;
+ cp->cp_cdb[5] = 0;
+
+ fake_int_base = (struct eata_register *) base;
+ fake_int_result = FALSE;
+ fake_int_happened = FALSE;
+
+ eata_send_command((u32) cp, (u32) base, EATA_CMD_DMA_SEND_CP);
+
+ i = jiffies + (3 * HZ);
+ while (fake_int_happened == FALSE && jiffies <= i)
+ barrier();
+
+ DBG(DBG_INTR3, printk(KERN_DEBUG "fake_int_result: %#x hbastat %#x "
+ "scsistat %#x, buff %p sp %p\n",
+ fake_int_result, (u32) (sp->hba_stat /*& 0x7f*/),
+ (u32) sp->scsi_stat, buff, sp));
+
+ scsi_init_free((void *)cp, sizeof(struct eata_ccb));
+ scsi_init_free((void *)sp, sizeof(struct eata_sp));
+
+ if ((fake_int_result & HA_SERROR) || jiffies > i){
+ printk(KERN_WARNING "eata_dma: trying to reset HBA at %x to clear "
+ "possible blink state\n", base);
+ /* hard reset the HBA */
+ inb((u32) (base) + HA_RSTATUS);
+ eata_send_command(0, base, EATA_CMD_RESET);
+ DELAY(1);
+ return (NULL);
+ } else
+ return (buff);
+}
+
+
+int get_conf_PIO(u32 base, struct get_conf *buf)
+{
+ ulong loop = R_LIMIT;
+ u16 *p;
+
+ if(check_region(base, 9))
+ return (FALSE);
+
+ memset(buf, 0, sizeof(struct get_conf));
+
+ while (inb(base + HA_RSTATUS) & HA_SBUSY)
+ if (--loop == 0)
+ return (FALSE);
+
+ fake_int_base = (struct eata_register *) base;
+ fake_int_result = FALSE;
+ fake_int_happened = FALSE;
+
+ DBG(DBG_PIO && DBG_PROBE,
+ printk("Issuing PIO READ CONFIG to HBA at %#x\n", base));
+ eata_send_command(0, base, EATA_CMD_PIO_READ_CONFIG);
+
+ loop = R_LIMIT;
+ for (p = (u16 *) buf;
+ (long)p <= ((long)buf + (sizeof(struct get_conf) / 2)); p++) {
+ while (!(inb(base + HA_RSTATUS) & HA_SDRQ))
+ if (--loop == 0)
+ return (FALSE);
+
+ loop = R_LIMIT;
+ *p = inw(base + HA_RDATA);
+ }
+
+ if (!(inb(base + HA_RSTATUS) & HA_SERROR)) { /* Error ? */
+ if (htonl(EATA_SIGNATURE) == buf->signature) {
+ DBG(DBG_PIO&&DBG_PROBE, printk("EATA Controller found at %x "
+ "EATA Level: %x\n", (uint) base,
+ (uint) (buf->version)));
+
+ while (inb(base + HA_RSTATUS) & HA_SDRQ)
+ inw(base + HA_RDATA);
+ return (TRUE);
+ }
+ } else {
+ DBG(DBG_PROBE, printk("eata_dma: get_conf_PIO, error during transfer "
+ "for HBA at %lx\n", (long)base));
+ }
+ return (FALSE);
+}
+
+
+void print_config(struct get_conf *gc)
+{
+ printk("LEN: %d ver:%d OCS:%d TAR:%d TRNXFR:%d MORES:%d DMAS:%d\n",
+ (u32) ntohl(gc->len), gc->version,
+ gc->OCS_enabled, gc->TAR_support, gc->TRNXFR, gc->MORE_support,
+ gc->DMA_support);
+ printk("DMAV:%d HAAV:%d SCSIID0:%d ID1:%d ID2:%d QUEUE:%d SG:%d SEC:%d\n",
+ gc->DMA_valid, gc->HAA_valid, gc->scsi_id[3], gc->scsi_id[2],
+ gc->scsi_id[1], ntohs(gc->queuesiz), ntohs(gc->SGsiz), gc->SECOND);
+ printk("IRQ:%d IRQT:%d DMAC:%d FORCADR:%d SG_64K:%d SG_UAE:%d MID:%d "
+ "MCH:%d MLUN:%d\n",
+ gc->IRQ, gc->IRQ_TR, (8 - gc->DMA_channel) & 7, gc->FORCADR,
+ gc->SG_64K, gc->SG_UAE, gc->MAX_ID, gc->MAX_CHAN, gc->MAX_LUN);
+ printk("RIDQ:%d PCI:%d EISA:%d\n",
+ gc->ID_qest, gc->is_PCI, gc->is_EISA);
+ DBG(DPT_DEBUG, DELAY(14));
+}
+
+short register_HBA(u32 base, struct get_conf *gc, Scsi_Host_Template * tpnt,
+ u8 bustype)
+{
+ ulong size = 0;
+ unchar dma_channel = 0;
+ char *buff = 0;
+ unchar bugs = 0;
+ struct Scsi_Host *sh;
+ hostdata *hd;
+ int x;
+
+
+ DBG(DBG_REGISTER, print_config(gc));
+
+ if (gc->DMA_support == FALSE) {
+ printk("The EATA HBA at %#.4x does not support DMA.\n"
+ "Please use the EATA-PIO driver.\n", base);
+ return (FALSE);
+ }
+ if(gc->HAA_valid == FALSE || ntohl(gc->len) < 0x22)
+ gc->MAX_CHAN = 0;
+
+ if (reg_IRQ[gc->IRQ] == FALSE) { /* Interrupt already registered ? */
+ if (!request_irq(gc->IRQ, (void *) eata_fake_int_handler, SA_INTERRUPT,
+ "eata_dma", NULL)){
+ reg_IRQ[gc->IRQ]++;
+ if (!gc->IRQ_TR)
+ reg_IRQL[gc->IRQ] = TRUE; /* IRQ is edge triggered */
+ } else {
+ printk("Couldn't allocate IRQ %d, Sorry.", gc->IRQ);
+ return (FALSE);
+ }
+ } else { /* More than one HBA on this IRQ */
+ if (reg_IRQL[gc->IRQ] == TRUE) {
+ printk("Can't support more than one HBA on this IRQ,\n"
+ " if the IRQ is edge triggered. Sorry.\n");
+ return (FALSE);
+ } else
+ reg_IRQ[gc->IRQ]++;
+ }
+
+
+ /* If DMA is supported but DMA_valid isn't set to indicate that
+ * the channel number is given we must have pre 2.0 firmware (1.7?)
+ * which leaves us to guess since the "newer ones" also don't set the
+ * DMA_valid bit.
+ */
+ if (gc->DMA_support && !gc->DMA_valid && gc->DMA_channel) {
+ printk(KERN_WARNING "eata_dma: If you are using a pre 2.0 firmware "
+ "please update it !\n"
+ " You can get new firmware releases from ftp.dpt.com\n");
+ gc->DMA_channel = (base == 0x1f0 ? 3 /* DMA=5 */ : 2 /* DMA=6 */);
+ gc->DMA_valid = TRUE;
+ }
+
+ /* if gc->DMA_valid it must be an ISA HBA and we have to register it */
+ dma_channel = BUSMASTER;
+ if (gc->DMA_valid) {
+ if (request_dma(dma_channel = (8 - gc->DMA_channel) & 7, "eata_dma")) {
+ printk(KERN_WARNING "Unable to allocate DMA channel %d for ISA HBA"
+ " at %#.4x.\n", dma_channel, base);
+ reg_IRQ[gc->IRQ]--;
+ if (reg_IRQ[gc->IRQ] == 0)
+ free_irq(gc->IRQ, NULL);
+ if (gc->IRQ_TR == FALSE)
+ reg_IRQL[gc->IRQ] = FALSE;
+ return (FALSE);
+ }
+ }
+
+ if (dma_channel != BUSMASTER) {
+ disable_dma(dma_channel);
+ clear_dma_ff(dma_channel);
+ set_dma_mode(dma_channel, DMA_MODE_CASCADE);
+ enable_dma(dma_channel);
+ }
+
+ if (bustype != IS_EISA && bustype != IS_ISA)
+ buff = get_board_data(base, gc->IRQ, gc->scsi_id[3]);
+
+ if (buff == NULL) {
+ if (bustype == IS_EISA || bustype == IS_ISA) {
+ bugs = bugs || BROKEN_INQUIRY;
+ } else {
+ if (gc->DMA_support == FALSE)
+ printk(KERN_WARNING "HBA at %#.4x doesn't support DMA. "
+ "Sorry\n", base);
+ else
+ printk(KERN_WARNING "HBA at %#.4x does not react on INQUIRY. "
+ "Sorry.\n", base);
+ if (gc->DMA_valid)
+ free_dma(dma_channel);
+ reg_IRQ[gc->IRQ]--;
+ if (reg_IRQ[gc->IRQ] == 0)
+ free_irq(gc->IRQ, NULL);
+ if (gc->IRQ_TR == FALSE)
+ reg_IRQL[gc->IRQ] = FALSE;
+ return (FALSE);
+ }
+ }
+
+ if (gc->DMA_support == FALSE && buff != NULL)
+ printk(KERN_WARNING "HBA %.12sat %#.4x doesn't set the DMA_support "
+ "flag correctly.\n", &buff[16], base);
+
+ request_region(base, 9, "eata_dma"); /* We already checked the
+ * availability, so this
+ * should not fail.
+ */
+
+ if(ntohs(gc->queuesiz) == 0) {
+ gc->queuesiz = ntohs(64);
+ printk(KERN_WARNING "Warning: Queue size has to be corrected. Assuming"
+ " 64 queueslots\n"
+ " This might be a PM2012B with a defective Firmware\n"
+ " Contact DPT support@dpt.com for an upgrade\n");
+ }
+
+ size = sizeof(hostdata) + ((sizeof(struct eata_ccb) + sizeof(long))
+ * ntohs(gc->queuesiz));
+
+ DBG(DBG_REGISTER, printk("scsi_register size: %ld\n", size));
+
+ sh = scsi_register(tpnt, size);
+
+ if(sh != NULL) {
+
+ hd = SD(sh);
+
+ memset(hd->reads, 0, sizeof(u32) * 26);
+
+ sh->select_queue_depths = eata_select_queue_depths;
+
+ hd->bustype = bustype;
+
+ /*
+ * If we are using a ISA board, we can't use extended SG,
+ * because we would need excessive amounts of memory for
+ * bounce buffers.
+ */
+ if (gc->SG_64K==TRUE && ntohs(gc->SGsiz)==64 && hd->bustype!=IS_ISA){
+ sh->sg_tablesize = SG_SIZE_BIG;
+ } else {
+ sh->sg_tablesize = ntohs(gc->SGsiz);
+ if (sh->sg_tablesize > SG_SIZE || sh->sg_tablesize == 0) {
+ if (sh->sg_tablesize == 0)
+ printk(KERN_WARNING "Warning: SG size had to be fixed.\n"
+ "This might be a PM2012 with a defective Firmware"
+ "\nContact DPT support@dpt.com for an upgrade\n");
+ sh->sg_tablesize = SG_SIZE;
+ }
+ }
+ hd->sgsize = sh->sg_tablesize;
+ }
+
+ if(sh != NULL) {
+ sh->can_queue = hd->queuesize = ntohs(gc->queuesiz);
+ sh->cmd_per_lun = 0;
+ }
+
+ if(sh == NULL) {
+ DBG(DBG_REGISTER, printk(KERN_NOTICE "eata_dma: couldn't register HBA"
+ " at%x \n", base));
+ scsi_unregister(sh);
+ if (gc->DMA_valid)
+ free_dma(dma_channel);
+
+ reg_IRQ[gc->IRQ]--;
+ if (reg_IRQ[gc->IRQ] == 0)
+ free_irq(gc->IRQ, NULL);
+ if (gc->IRQ_TR == FALSE)
+ reg_IRQL[gc->IRQ] = FALSE;
+ return (FALSE);
+ }
+
+
+ hd->broken_INQUIRY = (bugs & BROKEN_INQUIRY);
+
+ if(hd->broken_INQUIRY == TRUE) {
+ strcpy(hd->vendor, "DPT");
+ strcpy(hd->name, "??????????");
+ strcpy(hd->revision, "???.?");
+ hd->firmware_revision = 0;
+ } else {
+ strncpy(hd->vendor, &buff[8], 8);
+ hd->vendor[8] = 0;
+ strncpy(hd->name, &buff[16], 17);
+ hd->name[17] = 0;
+ hd->revision[0] = buff[32];
+ hd->revision[1] = buff[33];
+ hd->revision[2] = buff[34];
+ hd->revision[3] = '.';
+ hd->revision[4] = buff[35];
+ hd->revision[5] = 0;
+ hd->firmware_revision = (buff[32] << 24) + (buff[33] << 16)
+ + (buff[34] << 8) + buff[35];
+ }
+
+ if (hd->firmware_revision >= (('0'<<24) + ('7'<<16) + ('G'<< 8) + '0'))
+ hd->immediate_support = 1;
+ else
+ hd->immediate_support = 0;
+
+ switch (ntohl(gc->len)) {
+ case 0x1c:
+ hd->EATA_revision = 'a';
+ break;
+ case 0x1e:
+ hd->EATA_revision = 'b';
+ break;
+ case 0x22:
+ hd->EATA_revision = 'c';
+ break;
+ case 0x24:
+ hd->EATA_revision = 'z';
+ default:
+ hd->EATA_revision = '?';
+ }
+
+
+ if(ntohl(gc->len) >= 0x22) {
+ sh->max_id = gc->MAX_ID + 1;
+ sh->max_lun = gc->MAX_LUN + 1;
+ } else {
+ sh->max_id = 8;
+ sh->max_lun = 8;
+ }
+
+ hd->HBA_number = sh->host_no;
+ hd->channel = gc->MAX_CHAN;
+ sh->max_channel = gc->MAX_CHAN;
+ sh->unique_id = base;
+ sh->base = (char *) base;
+ sh->io_port = base;
+ sh->n_io_port = 9;
+ sh->irq = gc->IRQ;
+ sh->dma_channel = dma_channel;
+
+ /* FIXME:
+ * SCSI midlevel code should support different HBA ids on every channel
+ */
+ sh->this_id = gc->scsi_id[3];
+
+ if (gc->SECOND)
+ hd->primary = FALSE;
+ else
+ hd->primary = TRUE;
+
+ sh->wish_block = FALSE;
+
+ if (hd->bustype != IS_ISA) {
+ sh->unchecked_isa_dma = FALSE;
+ } else {
+ sh->unchecked_isa_dma = TRUE; /* We're doing ISA DMA */
+ }
+
+ for(x = 0; x <= 11; x++){ /* Initialize min. latency */
+ hd->writes_lat[x][1] = 0xffffffff;
+ hd->reads_lat[x][1] = 0xffffffff;
+ }
+ hd->all_lat[1] = 0xffffffff;
+
+ hd->next = NULL; /* build a linked list of all HBAs */
+ hd->prev = last_HBA;
+ if(hd->prev != NULL)
+ SD(hd->prev)->next = sh;
+ last_HBA = sh;
+ if (first_HBA == NULL)
+ first_HBA = sh;
+ registered_HBAs++;
+
+ return (TRUE);
+}
+
+
+
+void find_EISA(struct get_conf *buf, Scsi_Host_Template * tpnt)
+{
+ u32 base;
+ int i;
+
+#if CHECKPAL
+ u8 pal1, pal2, pal3;
+#endif
+
+ for (i = 0; i < MAXEISA; i++) {
+ if (EISAbases[i] == TRUE) { /* Still a possibility ? */
+
+ base = 0x1c88 + (i * 0x1000);
+#if CHECKPAL
+ pal1 = inb((u16)base - 8);
+ pal2 = inb((u16)base - 7);
+ pal3 = inb((u16)base - 6);
+
+ if (((pal1 == DPT_ID1) && (pal2 == DPT_ID2)) ||
+ ((pal1 == NEC_ID1) && (pal2 == NEC_ID2) && (pal3 == NEC_ID3))||
+ ((pal1 == ATT_ID1) && (pal2 == ATT_ID2) && (pal3 == ATT_ID3))){
+ DBG(DBG_PROBE, printk("EISA EATA id tags found: %x %x %x \n",
+ (int)pal1, (int)pal2, (int)pal3));
+#endif
+ if (get_conf_PIO(base, buf) == TRUE) {
+ if (buf->IRQ) {
+ DBG(DBG_EISA, printk("Registering EISA HBA\n"));
+ register_HBA(base, buf, tpnt, IS_EISA);
+ } else
+ printk("eata_dma: No valid IRQ. HBA removed from list\n");
+ }
+#if CHECK_BLINK
+ else {
+ if (check_blink_state(base))
+ printk("HBA is in BLINK state. Consult your HBAs "
+ "Manual to correct this.\n");
+ }
+#endif
+ /* Nothing found here so we take it from the list */
+ EISAbases[i] = 0;
+#if CHECKPAL
+ }
+#endif
+ }
+ }
+ return;
+}
+
+void find_ISA(struct get_conf *buf, Scsi_Host_Template * tpnt)
+{
+ int i;
+
+ for (i = 0; i < MAXISA; i++) {
+ if (ISAbases[i]) {
+ if (get_conf_PIO(ISAbases[i],buf) == TRUE){
+ DBG(DBG_ISA, printk("Registering ISA HBA\n"));
+ register_HBA(ISAbases[i], buf, tpnt, IS_ISA);
+ }
+#if CHECK_BLINK
+ else {
+ if (check_blink_state(ISAbases[i]))
+ printk("HBA is in BLINK state. Consult your HBAs "
+ "Manual to correct this.\n");
+ }
+#endif
+ ISAbases[i] = 0;
+ }
+ }
+ return;
+}
+
+void find_PCI(struct get_conf *buf, Scsi_Host_Template * tpnt)
+{
+
+#ifndef CONFIG_PCI
+ printk("eata_dma: kernel PCI support not enabled. Skipping scan for PCI HBAs.\n");
+#else
+
+ u8 pci_bus, pci_device_fn;
+ static s16 pci_index = 0; /* Device index to PCI BIOS calls */
+ u32 base = 0;
+ u16 com_adr;
+ u16 rev_device;
+ u32 error, i, x;
+ u8 pal1, pal2, pal3;
+
+ if (pcibios_present()) {
+ for (i = 0; i <= MAXPCI; ++i, ++pci_index) {
+ if (pcibios_find_device(PCI_VENDOR_ID_DPT, PCI_DEVICE_ID_DPT,
+ pci_index, &pci_bus, &pci_device_fn))
+ break;
+ DBG(DBG_PROBE && DBG_PCI,
+ printk("eata_dma: find_PCI, HBA at bus %d, device %d,"
+ " function %d, index %d\n", (s32)pci_bus,
+ (s32)((pci_device_fn & 0xf8) >> 3),
+ (s32)(pci_device_fn & 7), pci_index));
+
+ if (!(error = pcibios_read_config_word(pci_bus, pci_device_fn,
+ PCI_CLASS_DEVICE, &rev_device))) {
+ if (rev_device == PCI_CLASS_STORAGE_SCSI) {
+ if (!(error = pcibios_read_config_word(pci_bus,
+ pci_device_fn, PCI_COMMAND,
+ (u16 *) & com_adr))) {
+ if (!((com_adr & PCI_COMMAND_IO) &&
+ (com_adr & PCI_COMMAND_MASTER))) {
+ printk("eata_dma: find_PCI, HBA has IO or"
+ " BUSMASTER mode disabled\n");
+ continue;
+ }
+ } else
+ printk("eata_dma: find_PCI, error %x while reading "
+ "PCI_COMMAND\n", error);
+ } else
+ printk("eata_dma: find_PCI, DEVICECLASSID %x didn't match\n",
+ rev_device);
+ } else {
+ printk("eata_dma: find_PCI, error %x while reading "
+ "PCI_CLASS_BASE\n",
+ error);
+ continue;
+ }
+
+ if (!(error = pcibios_read_config_dword(pci_bus, pci_device_fn,
+ PCI_BASE_ADDRESS_0, (int *) &base))){
+
+ /* Check if the address is valid */
+ if (base & 0x01) {
+ base &= 0xfffffffe;
+ /* EISA tag there ? */
+ pal1 = inb(base);
+ pal2 = inb(base + 1);
+ pal3 = inb(base + 2);
+ if (((pal1 == DPT_ID1) && (pal2 == DPT_ID2)) ||
+ ((pal1 == NEC_ID1) && (pal2 == NEC_ID2) &&
+ (pal3 == NEC_ID3)) ||
+ ((pal1 == ATT_ID1) && (pal2 == ATT_ID2) &&
+ (pal3 == ATT_ID3)))
+ base += 0x08;
+ else
+ base += 0x10; /* Now, THIS is the real address */
+
+ if (base != 0x1f8) {
+ /* We didn't find it in the primary search */
+ if (get_conf_PIO(base, buf) == TRUE) {
+
+ /* OK. We made it till here, so we can go now
+ * and register it. We only have to check and
+ * eventually remove it from the EISA and ISA list
+ */
+ DBG(DBG_PCI, printk("Registering PCI HBA\n"));
+ register_HBA(base, buf, tpnt, IS_PCI);
+
+ if (base < 0x1000) {
+ for (x = 0; x < MAXISA; ++x) {
+ if (ISAbases[x] == base) {
+ ISAbases[x] = 0;
+ break;
+ }
+ }
+ } else if ((base & 0x0fff) == 0x0c88)
+ EISAbases[(base >> 12) & 0x0f] = 0;
+ continue; /* break; */
+ }
+#if CHECK_BLINK
+ else if (check_blink_state(base) == TRUE) {
+ printk("eata_dma: HBA is in BLINK state.\n"
+ "Consult your HBAs manual to correct this.\n");
+ }
+#endif
+ }
+ }
+ } else {
+ printk("eata_dma: error %x while reading "
+ "PCI_BASE_ADDRESS_0\n", error);
+ }
+ }
+ } else {
+ printk("eata_dma: No BIOS32 extensions present. This driver release "
+ "still depends on it.\n"
+ " Skipping scan for PCI HBAs. \n");
+ }
+#endif /* #ifndef CONFIG_PCI */
+ return;
+}
+
+int eata_detect(Scsi_Host_Template * tpnt)
+{
+ struct Scsi_Host *HBA_ptr;
+ struct get_conf gc;
+ int i;
+
+ DBG((DBG_PROBE && DBG_DELAY) || DPT_DEBUG,
+ printk("Using lots of delays to let you read the debugging output\n"));
+
+ tpnt->proc_dir = &proc_scsi_eata_dma;
+
+ status = scsi_init_malloc(512, GFP_ATOMIC | GFP_DMA);
+ dma_scratch = scsi_init_malloc(1024, GFP_ATOMIC | GFP_DMA);
+
+ if(status == NULL || dma_scratch == NULL) {
+ printk("eata_dma: can't allocate enough memory to probe for hosts !\n");
+ return(0);
+ }
+
+ dma_scratch += 4;
+
+ find_PCI(&gc, tpnt);
+
+ find_EISA(&gc, tpnt);
+
+ find_ISA(&gc, tpnt);
+
+ for (i = 0; i <= MAXIRQ; i++) { /* Now that we know what we have, we */
+ if (reg_IRQ[i] >= 1){ /* exchange the interrupt handler which */
+ free_irq(i, NULL); /* we used for probing with the real one */
+ request_irq(i, (void *)(eata_int_handler), SA_INTERRUPT|SA_SHIRQ,
+ "eata_dma", NULL);
+ }
+ }
+
+ HBA_ptr = first_HBA;
+
+ if (registered_HBAs != 0) {
+ printk("EATA (Extended Attachment) driver version: %d.%d%s"
+ "\ndeveloped in co-operation with DPT\n"
+ "(c) 1993-96 Michael Neuffer, mike@i-Connect.Net\n",
+ VER_MAJOR, VER_MINOR, VER_SUB);
+ printk("Registered HBAs:");
+ printk("\nHBA no. Boardtype Revis EATA Bus BaseIO IRQ"
+ " DMA Ch ID Pr QS S/G IS\n");
+ for (i = 1; i <= registered_HBAs; i++) {
+ printk("scsi%-2d: %.12s v%s 2.0%c %s %#.4x %2d",
+ HBA_ptr->host_no, SD(HBA_ptr)->name, SD(HBA_ptr)->revision,
+ SD(HBA_ptr)->EATA_revision, (SD(HBA_ptr)->bustype == 'P')?
+ "PCI ":(SD(HBA_ptr)->bustype == 'E')?"EISA":"ISA ",
+ (u32) HBA_ptr->base, HBA_ptr->irq);
+ if(HBA_ptr->dma_channel != BUSMASTER)
+ printk(" %2x ", HBA_ptr->dma_channel);
+ else
+ printk(" %s", "BMST");
+ printk(" %d %d %c %3d %3d %c\n",
+ SD(HBA_ptr)->channel+1, HBA_ptr->this_id,
+ (SD(HBA_ptr)->primary == TRUE)?'Y':'N',
+ HBA_ptr->can_queue, HBA_ptr->sg_tablesize,
+ (SD(HBA_ptr)->immediate_support == TRUE)?'Y':'N');
+ HBA_ptr = SD(HBA_ptr)->next;
+ }
+ } else {
+ scsi_init_free((void *)status, 512);
+ }
+
+ scsi_init_free((void *)dma_scratch - 4, 1024);
+
+ DBG(DPT_DEBUG, DELAY(12));
+
+ return(registered_HBAs);
+}
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = EATA_DMA;
+#include "scsi_module.c"
+#endif
+
+/*
+ * Overrides for Emacs so that we almost follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 4
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: 0
+ * tab-width: 8
+ * End:
+ */
diff --git a/linux/dev/drivers/scsi/g_NCR5380.c b/linux/dev/drivers/scsi/g_NCR5380.c
new file mode 100644
index 0000000..e2d64a1
--- /dev/null
+++ b/linux/dev/drivers/scsi/g_NCR5380.c
@@ -0,0 +1,739 @@
+/*
+ * Generic Generic NCR5380 driver
+ *
+ * Copyright 1993, Drew Eckhardt
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * drew@colorado.edu
+ * +1 (303) 440-4894
+ *
+ * NCR53C400 extensions (c) 1994,1995,1996, Kevin Lentin
+ * K.Lentin@cs.monash.edu.au
+ *
+ * ALPHA RELEASE 1.
+ *
+ * For more information, please consult
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+/*
+ * TODO : flesh out DMA support, find some one actually using this (I have
+ * a memory mapped Trantor board that works fine)
+ */
+
+/*
+ * Options :
+ *
+ * PARITY - enable parity checking. Not supported.
+ *
+ * SCSI2 - enable support for SCSI-II tagged queueing. Untested.
+ *
+ * USLEEP - enable support for devices that don't disconnect. Untested.
+ *
+ * The card is detected and initialized in one of several ways :
+ * 1. With command line overrides - NCR5380=port,irq may be
+ * used on the LILO command line to override the defaults.
+ *
+ * 2. With the GENERIC_NCR5380_OVERRIDE compile time define. This is
+ * specified as an array of address, irq, dma, board tuples. Ie, for
+ * one board at 0x350, IRQ5, no dma, I could say
+ * -DGENERIC_NCR5380_OVERRIDE={{0xcc000, 5, DMA_NONE, BOARD_NCR5380}}
+ *
+ * -1 should be specified for no or DMA interrupt, -2 to autoprobe for an
+ * IRQ line if overridden on the command line.
+ *
+ * 3. When included as a module, with arguments passed on the command line:
+ * ncr_irq=xx the interrupt
+ * ncr_addr=xx the port or base address (for port or memory
+ * mapped, resp.)
+ * ncr_dma=xx the DMA
+ * ncr_5380=1 to set up for a NCR5380 board
+ * ncr_53c400=1 to set up for a NCR53C400 board
+ * e.g.
+ * modprobe g_NCR5380 ncr_irq=5 ncr_addr=0x350 ncr_5380=1
+ * for a port mapped NCR5380 board or
+ * modprobe g_NCR5380 ncr_irq=255 ncr_addr=0xc8000 ncr_53c400=1
+ * for a memory mapped NCR53C400 board with interrupts disabled.
+ *
+ * 255 should be specified for no or DMA interrupt, 254 to autoprobe for an
+ * IRQ line if overridden on the command line.
+ *
+ */
+
+/*
+ * $Log: generic_NCR5380.c,v $
+ */
+
+#ifdef MACH
+#define GENERIC_NCR5380_OVERRIDE {{(NCR5380_map_type)0x350,5,0,BOARD_NCR53C400}};
+#define CONFIG_SCSI_GENERIC_NCR53C400
+#define CONFIG_SCSI_G_NCR5380_MEM
+#endif
+
+#define AUTOPROBE_IRQ
+#define AUTOSENSE
+
+#include <linux/config.h>
+
+#ifdef CONFIG_SCSI_GENERIC_NCR53C400
+#define NCR53C400_PSEUDO_DMA 1
+#define PSEUDO_DMA
+#define NCR53C400
+#define NCR5380_STATS
+#undef NCR5380_STAT_LIMIT
+#endif
+#if defined(CONFIG_SCSI_G_NCR5380_PORT) && defined(CONFIG_SCSI_G_NCR5380_MEM)
+#error You can not configure the Generic NCR 5380 SCSI Driver for memory mapped I/O and port mapped I/O at the same time (yet)
+#endif
+#if !defined(CONFIG_SCSI_G_NCR5380_PORT) && !defined(CONFIG_SCSI_G_NCR5380_MEM)
+#error You must configure the Generic NCR 5380 SCSI Driver for one of memory mapped I/O and port mapped I/O.
+#endif
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+#include "g_NCR5380.h"
+#include "NCR5380.h"
+#include "constants.h"
+#include "sd.h"
+#include<linux/stat.h>
+
+struct proc_dir_entry proc_scsi_g_ncr5380 = {
+ PROC_SCSI_GENERIC_NCR5380, 9, "g_NCR5380",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+#define NCR_NOT_SET 0
+static int ncr_irq=NCR_NOT_SET;
+static int ncr_dma=NCR_NOT_SET;
+static int ncr_addr=NCR_NOT_SET;
+static int ncr_5380=NCR_NOT_SET;
+static int ncr_53c400=NCR_NOT_SET;
+
+static struct override {
+ NCR5380_implementation_fields;
+ int irq;
+ int dma;
+ int board; /* Use NCR53c400, Ricoh, etc. extensions ? */
+} overrides
+#ifdef GENERIC_NCR5380_OVERRIDE
+ [] = GENERIC_NCR5380_OVERRIDE
+#else
+ [1] = {{0,},};
+#endif
+
+#define NO_OVERRIDES (sizeof(overrides) / sizeof(struct override))
+
+/*
+ * Function : static internal_setup(int board, char *str, int *ints)
+ *
+ * Purpose : LILO command line initialization of the overrides array,
+ *
+ * Inputs : board - either BOARD_NCR5380 for a normal NCR5380 board,
+ * or BOARD_NCR53C400 for a NCR53C400 board. str - unused, ints -
+ * array of integer parameters with ints[0] equal to the number of ints.
+ *
+ */
+
+static void internal_setup(int board, char *str, int *ints) {
+ static int commandline_current = 0;
+ switch (board) {
+ case BOARD_NCR5380:
+ if (ints[0] != 2 && ints[0] != 3) {
+ printk("generic_NCR5380_setup : usage ncr5380=" STRVAL(NCR5380_map_name) ",irq,dma\n");
+ return;
+ }
+ case BOARD_NCR53C400:
+ if (ints[0] != 2) {
+ printk("generic_NCR53C400_setup : usage ncr53c400=" STRVAL(NCR5380_map_name) ",irq\n");
+ return;
+ }
+ }
+
+ if (commandline_current < NO_OVERRIDES) {
+ overrides[commandline_current].NCR5380_map_name = (NCR5380_map_type)ints[1];
+ overrides[commandline_current].irq = ints[2];
+ if (ints[0] == 3)
+ overrides[commandline_current].dma = ints[3];
+ else
+ overrides[commandline_current].dma = DMA_NONE;
+ overrides[commandline_current].board = board;
+ ++commandline_current;
+ }
+}
+
+/*
+ * Function : generic_NCR5380_setup (char *str, int *ints)
+ *
+ * Purpose : LILO command line initialization of the overrides array,
+ *
+ * Inputs : str - unused, ints - array of integer parameters with ints[0]
+ * equal to the number of ints.
+ */
+
+void generic_NCR5380_setup (char *str, int *ints) {
+ internal_setup (BOARD_NCR5380, str, ints);
+}
+
+/*
+ * Function : generic_NCR53C400_setup (char *str, int *ints)
+ *
+ * Purpose : LILO command line initialization of the overrides array,
+ *
+ * Inputs : str - unused, ints - array of integer parameters with ints[0]
+ * equal to the number of ints.
+ */
+
+void generic_NCR53C400_setup (char *str, int *ints) {
+ internal_setup (BOARD_NCR53C400, str, ints);
+}
+
+/*
+ * Function : int generic_NCR5380_detect(Scsi_Host_Template * tpnt)
+ *
+ * Purpose : initializes generic NCR5380 driver based on the
+ * command line / compile time port and irq definitions.
+ *
+ * Inputs : tpnt - template for this SCSI adapter.
+ *
+ * Returns : 1 if a host adapter was found, 0 if not.
+ *
+ */
+
+int generic_NCR5380_detect(Scsi_Host_Template * tpnt) {
+ static int current_override = 0;
+ int count;
+ int flags = 0;
+ struct Scsi_Host *instance;
+
+ if (ncr_irq != NCR_NOT_SET)
+ overrides[0].irq=ncr_irq;
+ if (ncr_dma != NCR_NOT_SET)
+ overrides[0].dma=ncr_dma;
+ if (ncr_addr != NCR_NOT_SET)
+ overrides[0].NCR5380_map_name=(NCR5380_map_type)ncr_addr;
+ if (ncr_5380 != NCR_NOT_SET)
+ overrides[0].board=BOARD_NCR5380;
+ else if (ncr_53c400 != NCR_NOT_SET)
+ overrides[0].board=BOARD_NCR53C400;
+
+ tpnt->proc_dir = &proc_scsi_g_ncr5380;
+
+ for (count = 0; current_override < NO_OVERRIDES; ++current_override) {
+ if (!(overrides[current_override].NCR5380_map_name))
+ continue;
+
+ switch (overrides[current_override].board) {
+ case BOARD_NCR5380:
+ flags = FLAG_NO_PSEUDO_DMA;
+ break;
+ case BOARD_NCR53C400:
+ flags = FLAG_NCR53C400;
+ break;
+ }
+
+ instance = scsi_register (tpnt, sizeof(struct NCR5380_hostdata));
+ instance->NCR5380_instance_name = overrides[current_override].NCR5380_map_name;
+
+ NCR5380_init(instance, flags);
+
+ if (overrides[current_override].irq != IRQ_AUTO)
+ instance->irq = overrides[current_override].irq;
+ else
+ instance->irq = NCR5380_probe_irq(instance, 0xffff);
+
+ if (instance->irq != IRQ_NONE)
+ if (request_irq(instance->irq, generic_NCR5380_intr, SA_INTERRUPT, "NCR5380", NULL)) {
+ printk("scsi%d : IRQ%d not free, interrupts disabled\n",
+ instance->host_no, instance->irq);
+ instance->irq = IRQ_NONE;
+ }
+
+ if (instance->irq == IRQ_NONE) {
+ printk("scsi%d : interrupts not enabled. for better interactive performance,\n", instance->host_no);
+ printk("scsi%d : please jumper the board for a free IRQ.\n", instance->host_no);
+ }
+
+ printk("scsi%d : at " STRVAL(NCR5380_map_name) " 0x%x", instance->host_no, (unsigned int)instance->NCR5380_instance_name);
+ if (instance->irq == IRQ_NONE)
+ printk (" interrupts disabled");
+ else
+ printk (" irq %d", instance->irq);
+ printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d",
+ CAN_QUEUE, CMD_PER_LUN, GENERIC_NCR5380_PUBLIC_RELEASE);
+ NCR5380_print_options(instance);
+ printk("\n");
+
+ ++current_override;
+ ++count;
+ }
+ return count;
+}
+
+const char * generic_NCR5380_info (struct Scsi_Host* host) {
+ static const char string[]="Generic NCR5380/53C400 Driver";
+ return string;
+}
+
+int generic_NCR5380_release_resources(struct Scsi_Host * instance)
+{
+ NCR5380_local_declare();
+
+ NCR5380_setup(instance);
+
+ if (instance->irq != IRQ_NONE)
+ free_irq(instance->irq, NULL);
+
+ return 0;
+}
+
+#ifdef BIOSPARAM
+/*
+ * Function : int generic_NCR5380_biosparam(Disk * disk, kdev_t dev, int *ip)
+ *
+ * Purpose : Generates a BIOS / DOS compatible H-C-S mapping for
+ * the specified device / size.
+ *
+ * Inputs : size = size of device in sectors (512 bytes), dev = block device
+ * major / minor, ip[] = {heads, sectors, cylinders}
+ *
+ * Returns : always 0 (success), initializes ip
+ *
+ */
+
+/*
+ * XXX Most SCSI boards use this mapping, I could be incorrect. Some one
+ * using hard disks on a trantor should verify that this mapping corresponds
+ * to that used by the BIOS / ASPI driver by running the linux fdisk program
+ * and matching the H_C_S coordinates to what DOS uses.
+ */
+
+int generic_NCR5380_biosparam(Disk * disk, kdev_t dev, int *ip)
+{
+ int size = disk->capacity;
+ ip[0] = 64;
+ ip[1] = 32;
+ ip[2] = size >> 11;
+ return 0;
+}
+#endif
+
+#if NCR53C400_PSEUDO_DMA
+static inline int NCR5380_pread (struct Scsi_Host *instance, unsigned char *dst, int len)
+{
+ int blocks = len / 128;
+ int start = 0;
+ int i;
+ int bl;
+ NCR5380_local_declare();
+
+ NCR5380_setup(instance);
+
+#if (NDEBUG & NDEBUG_C400_PREAD)
+ printk("53C400r: About to read %d blocks for %d bytes\n", blocks, len);
+#endif
+
+ NCR5380_write(C400_CONTROL_STATUS_REG, CSR_BASE | CSR_TRANS_DIR);
+ NCR5380_write(C400_BLOCK_COUNTER_REG, blocks);
+ while (1) {
+
+#if (NDEBUG & NDEBUG_C400_PREAD)
+ printk("53C400r: %d blocks left\n", blocks);
+#endif
+
+ if ((bl=NCR5380_read(C400_BLOCK_COUNTER_REG)) == 0) {
+#if (NDEBUG & NDEBUG_C400_PREAD)
+ if (blocks)
+ printk("53C400r: blocks still == %d\n", blocks);
+ else
+ printk("53C400r: Exiting loop\n");
+#endif
+ break;
+ }
+
+#if 1
+ if (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_GATED_53C80_IRQ) {
+ printk("53C400r: Got 53C80_IRQ start=%d, blocks=%d\n", start, blocks);
+ return -1;
+ }
+#endif
+
+#if (NDEBUG & NDEBUG_C400_PREAD)
+ printk("53C400r: Waiting for buffer, bl=%d\n", bl);
+#endif
+
+ while (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_HOST_BUF_NOT_RDY)
+ ;
+#if (NDEBUG & NDEBUG_C400_PREAD)
+ printk("53C400r: Transferring 128 bytes\n");
+#endif
+
+#ifdef CONFIG_SCSI_G_NCR5380_PORT
+ for (i=0; i<128; i++)
+ dst[start+i] = NCR5380_read(C400_HOST_BUFFER);
+#else
+ /* implies CONFIG_SCSI_G_NCR5380_MEM */
+ memmove(dst+start,NCR53C400_host_buffer+NCR5380_map_name,128);
+#endif
+ start+=128;
+ blocks--;
+ }
+
+ if (blocks) {
+#if (NDEBUG & NDEBUG_C400_PREAD)
+ printk("53C400r: EXTRA: Waiting for buffer\n");
+#endif
+ while (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_HOST_BUF_NOT_RDY)
+ ;
+
+#if (NDEBUG & NDEBUG_C400_PREAD)
+ printk("53C400r: Transferring EXTRA 128 bytes\n");
+#endif
+#ifdef CONFIG_SCSI_G_NCR5380_PORT
+ for (i=0; i<128; i++)
+ dst[start+i] = NCR5380_read(C400_HOST_BUFFER);
+#else
+ /* implies CONFIG_SCSI_G_NCR5380_MEM */
+ memmove(dst+start,NCR53C400_host_buffer+NCR5380_map_name,128);
+#endif
+ start+=128;
+ blocks--;
+ }
+#if (NDEBUG & NDEBUG_C400_PREAD)
+ else
+ printk("53C400r: No EXTRA required\n");
+#endif
+
+#if (NDEBUG & NDEBUG_C400_PREAD)
+ printk("53C400r: Final values: blocks=%d start=%d\n", blocks, start);
+#endif
+
+ if (!(NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_GATED_53C80_IRQ))
+ printk("53C400r: no 53C80 gated irq after transfer");
+#if (NDEBUG & NDEBUG_C400_PREAD)
+ else
+ printk("53C400r: Got 53C80 interrupt and tried to clear it\n");
+#endif
+
+/* DON'T DO THIS - THEY NEVER ARRIVE!
+ printk("53C400r: Waiting for 53C80 registers\n");
+ while (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_53C80_REG)
+ ;
+*/
+
+ if (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_END_DMA_TRANSFER))
+ printk("53C400r: no end dma signal\n");
+#if (NDEBUG & NDEBUG_C400_PREAD)
+ else
+ printk("53C400r: end dma as expected\n");
+#endif
+
+ NCR5380_write(MODE_REG, MR_BASE);
+ NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+ return 0;
+}
+
+static inline int NCR5380_pwrite (struct Scsi_Host *instance, unsigned char *src, int len)
+{
+ int blocks = len / 128;
+ int start = 0;
+ int i;
+ int bl;
+ NCR5380_local_declare();
+
+ NCR5380_setup(instance);
+
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+ printk("53C400w: About to write %d blocks for %d bytes\n", blocks, len);
+#endif
+
+ NCR5380_write(C400_CONTROL_STATUS_REG, CSR_BASE);
+ NCR5380_write(C400_BLOCK_COUNTER_REG, blocks);
+ while (1) {
+ if (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_GATED_53C80_IRQ) {
+ printk("53C400w: Got 53C80_IRQ start=%d, blocks=%d\n", start, blocks);
+ return -1;
+ }
+
+ if ((bl=NCR5380_read(C400_BLOCK_COUNTER_REG)) == 0) {
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+ if (blocks)
+ printk("53C400w: exiting loop, blocks still == %d\n", blocks);
+ else
+ printk("53C400w: exiting loop\n");
+#endif
+ break;
+ }
+
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+ printk("53C400w: %d blocks left\n", blocks);
+
+ printk("53C400w: waiting for buffer, bl=%d\n", bl);
+#endif
+ while (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_HOST_BUF_NOT_RDY)
+ ;
+
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+ printk("53C400w: transferring 128 bytes\n");
+#endif
+#ifdef CONFIG_SCSI_G_NCR5380_PORT
+ for (i=0; i<128; i++)
+ NCR5380_write(C400_HOST_BUFFER, src[start+i]);
+#else
+ /* implies CONFIG_SCSI_G_NCR5380_MEM */
+ memmove(NCR53C400_host_buffer+NCR5380_map_name,src+start,128);
+#endif
+ start+=128;
+ blocks--;
+ }
+ if (blocks) {
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+ printk("53C400w: EXTRA waiting for buffer\n");
+#endif
+ while (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_HOST_BUF_NOT_RDY)
+ ;
+
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+ printk("53C400w: transferring EXTRA 128 bytes\n");
+#endif
+#ifdef CONFIG_SCSI_G_NCR5380_PORT
+ for (i=0; i<128; i++)
+ NCR5380_write(C400_HOST_BUFFER, src[start+i]);
+#else
+ /* implies CONFIG_SCSI_G_NCR5380_MEM */
+ memmove(NCR53C400_host_buffer+NCR5380_map_name,src+start,128);
+#endif
+ start+=128;
+ blocks--;
+ }
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+ else
+ printk("53C400w: No EXTRA required\n");
+#endif
+
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+ printk("53C400w: Final values: blocks=%d start=%d\n", blocks, start);
+#endif
+
+#if 0
+ printk("53C400w: waiting for registers to be available\n");
+ THEY NEVER DO!
+ while (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_53C80_REG)
+ ;
+ printk("53C400w: Got em\n");
+#endif
+
+ /* Let's wait for this instead - could be ugly */
+ /* All documentation says to check for this. Maybe my hardware is too
+ * fast. Waiting for it seems to work fine! KLL
+ */
+ while (!(i = NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_GATED_53C80_IRQ))
+ ;
+
+ /*
+ * I know. i is certainly != 0 here but the loop is new. See previous
+ * comment.
+ */
+ if (i) {
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+ printk("53C400w: got 53C80 gated irq (last block)\n");
+#endif
+ if (!((i=NCR5380_read(BUS_AND_STATUS_REG)) & BASR_END_DMA_TRANSFER))
+ printk("53C400w: No END OF DMA bit - WHOOPS! BASR=%0x\n",i);
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+ else
+ printk("53C400w: Got END OF DMA\n");
+#endif
+ }
+ else
+ printk("53C400w: no 53C80 gated irq after transfer (last block)\n");
+
+#if 0
+ if (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_END_DMA_TRANSFER)) {
+ printk("53C400w: no end dma signal\n");
+ }
+#endif
+
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+ printk("53C400w: waiting for last byte...\n");
+#endif
+ while (!(NCR5380_read(TARGET_COMMAND_REG) & TCR_LAST_BYTE_SENT))
+ ;
+
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+ printk("53C400w: got last byte.\n");
+ printk("53C400w: pwrite exiting with status 0, whoopee!\n");
+#endif
+ return 0;
+}
+#endif /* PSEUDO_DMA */
+
+#include "NCR5380.c"
+
+#define PRINTP(x) len += sprintf(buffer+len, x)
+#define ANDP ,
+
+static int sprint_opcode(char* buffer, int len, int opcode) {
+ int start = len;
+ PRINTP("0x%02x " ANDP opcode);
+ return len-start;
+}
+
+static int sprint_command (char* buffer, int len, unsigned char *command) {
+ int i,s,start=len;
+ len += sprint_opcode(buffer, len, command[0]);
+ for ( i = 1, s = COMMAND_SIZE(command[0]); i < s; ++i)
+ PRINTP("%02x " ANDP command[i]);
+ PRINTP("\n");
+ return len-start;
+}
+
+static int sprint_Scsi_Cmnd (char* buffer, int len, Scsi_Cmnd *cmd) {
+ int start = len;
+ PRINTP("host number %d destination target %d, lun %d\n" ANDP
+ cmd->host->host_no ANDP
+ cmd->target ANDP
+ cmd->lun);
+ PRINTP(" command = ");
+ len += sprint_command (buffer, len, cmd->cmnd);
+ return len-start;
+}
+
+int generic_NCR5380_proc_info(char* buffer, char** start, off_t offset, int length, int hostno, int inout)
+{
+ int len = 0;
+ NCR5380_local_declare();
+ unsigned char status;
+ int i;
+ struct Scsi_Host *scsi_ptr;
+ Scsi_Cmnd *ptr;
+ Scsi_Device *dev;
+ struct NCR5380_hostdata *hostdata;
+
+ cli();
+
+ for (scsi_ptr = first_instance; scsi_ptr; scsi_ptr=scsi_ptr->next)
+ if (scsi_ptr->host_no == hostno)
+ break;
+ NCR5380_setup(scsi_ptr);
+ hostdata = (struct NCR5380_hostdata *)scsi_ptr->hostdata;
+
+ PRINTP("SCSI host number %d : %s\n" ANDP scsi_ptr->host_no ANDP scsi_ptr->hostt->name);
+ PRINTP("Generic NCR5380 driver version %d\n" ANDP GENERIC_NCR5380_PUBLIC_RELEASE);
+ PRINTP("NCR5380 core version %d\n" ANDP NCR5380_PUBLIC_RELEASE);
+#ifdef NCR53C400
+ PRINTP("NCR53C400 extension version %d\n" ANDP NCR53C400_PUBLIC_RELEASE);
+ PRINTP("NCR53C400 card%s detected\n" ANDP (((struct NCR5380_hostdata *)scsi_ptr->hostdata)->flags & FLAG_NCR53C400)?"":" not");
+# if NCR53C400_PSEUDO_DMA
+ PRINTP("NCR53C400 pseudo DMA used\n");
+# endif
+#else
+ PRINTP("NO NCR53C400 driver extensions\n");
+#endif
+ PRINTP("Using %s mapping at %s 0x%x, " ANDP STRVAL(NCR5380_map_config) ANDP STRVAL(NCR5380_map_name) ANDP scsi_ptr->NCR5380_instance_name);
+ if (scsi_ptr->irq == IRQ_NONE)
+ PRINTP("no interrupt\n");
+ else
+ PRINTP("on interrupt %d\n" ANDP scsi_ptr->irq);
+
+#ifdef NCR5380_STATS
+ if (hostdata->connected || hostdata->issue_queue || hostdata->disconnected_queue)
+ PRINTP("There are commands pending, transfer rates may be crud\n");
+ if (hostdata->pendingr)
+ PRINTP(" %d pending reads" ANDP hostdata->pendingr);
+ if (hostdata->pendingw)
+ PRINTP(" %d pending writes" ANDP hostdata->pendingw);
+ if (hostdata->pendingr || hostdata->pendingw)
+ PRINTP("\n");
+ for (dev = scsi_devices; dev; dev=dev->next) {
+ if (dev->host == scsi_ptr) {
+ unsigned long br = hostdata->bytes_read[dev->id];
+ unsigned long bw = hostdata->bytes_write[dev->id];
+ long tr = hostdata->time_read[dev->id] / HZ;
+ long tw = hostdata->time_write[dev->id] / HZ;
+
+ PRINTP(" T:%d %s " ANDP dev->id ANDP (dev->type < MAX_SCSI_DEVICE_CODE) ? scsi_device_types[(int)dev->type] : "Unknown");
+ for (i=0; i<8; i++)
+ if (dev->vendor[i] >= 0x20)
+ *(buffer+(len++)) = dev->vendor[i];
+ *(buffer+(len++)) = ' ';
+ for (i=0; i<16; i++)
+ if (dev->model[i] >= 0x20)
+ *(buffer+(len++)) = dev->model[i];
+ *(buffer+(len++)) = ' ';
+ for (i=0; i<4; i++)
+ if (dev->rev[i] >= 0x20)
+ *(buffer+(len++)) = dev->rev[i];
+ *(buffer+(len++)) = ' ';
+
+ PRINTP("\n%10ld kb read in %5ld secs" ANDP br/1024 ANDP tr);
+ if (tr)
+ PRINTP(" @ %5ld bps" ANDP br / tr);
+
+ PRINTP("\n%10ld kb written in %5ld secs" ANDP bw/1024 ANDP tw);
+ if (tw)
+ PRINTP(" @ %5ld bps" ANDP bw / tw);
+ PRINTP("\n");
+ }
+ }
+#endif
+
+ status = NCR5380_read(STATUS_REG);
+ if (!(status & SR_REQ))
+ PRINTP("REQ not asserted, phase unknown.\n");
+ else {
+ for (i = 0; (phases[i].value != PHASE_UNKNOWN) &&
+ (phases[i].value != (status & PHASE_MASK)); ++i)
+ ;
+ PRINTP("Phase %s\n" ANDP phases[i].name);
+ }
+
+ if (!hostdata->connected) {
+ PRINTP("No currently connected command\n");
+ } else {
+ len += sprint_Scsi_Cmnd (buffer, len, (Scsi_Cmnd *) hostdata->connected);
+ }
+
+ PRINTP("issue_queue\n");
+
+ for (ptr = (Scsi_Cmnd *) hostdata->issue_queue; ptr;
+ ptr = (Scsi_Cmnd *) ptr->host_scribble)
+ len += sprint_Scsi_Cmnd (buffer, len, ptr);
+
+ PRINTP("disconnected_queue\n");
+
+ for (ptr = (Scsi_Cmnd *) hostdata->disconnected_queue; ptr;
+ ptr = (Scsi_Cmnd *) ptr->host_scribble)
+ len += sprint_Scsi_Cmnd (buffer, len, ptr);
+
+ *start = buffer + offset;
+ len -= offset;
+ if (len > length)
+ len = length;
+ sti();
+ return len;
+}
+
+#undef PRINTP
+#undef ANDP
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = GENERIC_NCR5380;
+
+#include <linux/module.h>
+#include "scsi_module.c"
+#endif
diff --git a/linux/dev/drivers/scsi/hosts.c b/linux/dev/drivers/scsi/hosts.c
new file mode 100644
index 0000000..d5086ef
--- /dev/null
+++ b/linux/dev/drivers/scsi/hosts.c
@@ -0,0 +1,549 @@
+/*
+ * hosts.c Copyright (C) 1992 Drew Eckhardt
+ * Copyright (C) 1993, 1994, 1995 Eric Youngdale
+ *
+ * mid to lowlevel SCSI driver interface
+ * Initial versions: Drew Eckhardt
+ * Subsequent revisions: Eric Youngdale
+ *
+ * <drew@colorado.edu>
+ */
+
+
+/*
+ * This file contains the medium level SCSI
+ * host interface initialization, as well as the scsi_hosts array of SCSI
+ * hosts currently present in the system.
+ */
+
+/*
+ * Don't import our own symbols, as this would severely mess up our
+ * symbol tables.
+ */
+#define _SCSI_SYMS_VER_
+#define __NO_VERSION__
+#include <linux/module.h>
+
+#include <linux/config.h>
+#include <linux/blk.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/proc_fs.h>
+
+#include "scsi.h"
+
+#ifndef NULL
+#define NULL 0L
+#endif
+
+#define HOSTS_C
+
+#include "hosts.h"
+
+#ifdef CONFIG_A3000_SCSI
+#include "a3000.h"
+#endif
+
+#ifdef CONFIG_A2091_SCSI
+#include "a2091.h"
+#endif
+
+#ifdef CONFIG_GVP11_SCSI
+#include "gvp11.h"
+#endif
+
+#ifdef CONFIG_ATARI_SCSI
+#include "atari_scsi.h"
+#endif
+
+#ifdef CONFIG_SCSI_ADVANSYS
+#include "advansys.h"
+#endif
+
+#ifdef CONFIG_SCSI_AHA152X
+#include "aha152x.h"
+#endif
+
+#ifdef CONFIG_SCSI_AHA1542
+#include "aha1542.h"
+#endif
+
+#ifdef CONFIG_SCSI_AHA1740
+#include "aha1740.h"
+#endif
+
+#ifdef CONFIG_SCSI_AIC7XXX
+#include "aic7xxx.h"
+#endif
+
+#ifdef CONFIG_SCSI_BUSLOGIC
+#include "BusLogic.h"
+#endif
+
+#ifdef CONFIG_SCSI_EATA_DMA
+#include "eata_dma.h"
+#endif
+
+#ifdef CONFIG_SCSI_EATA_PIO
+#include "eata_pio.h"
+#endif
+
+#ifdef CONFIG_SCSI_U14_34F
+#include "u14-34f.h"
+#endif
+
+#ifdef CONFIG_SCSI_FUTURE_DOMAIN
+#include "fdomain.h"
+#endif
+
+#ifdef CONFIG_SCSI_GENERIC_NCR5380
+#include "g_NCR5380.h"
+#endif
+
+#ifdef CONFIG_SCSI_IN2000
+#include "in2000.h"
+#endif
+
+#ifdef CONFIG_SCSI_PAS16
+#include "pas16.h"
+#endif
+
+#ifdef CONFIG_SCSI_QLOGIC_FAS
+#include "qlogicfas.h"
+#endif
+
+#ifdef CONFIG_SCSI_QLOGIC_ISP
+#include "qlogicisp.h"
+#endif
+
+#ifdef CONFIG_SCSI_SEAGATE
+#include "seagate.h"
+#endif
+
+#ifdef CONFIG_SCSI_T128
+#include "t128.h"
+#endif
+
+#ifdef CONFIG_SCSI_DTC3280
+#include "dtc.h"
+#endif
+
+#ifdef CONFIG_SCSI_NCR53C7xx
+#include "53c7,8xx.h"
+#endif
+
+#ifdef CONFIG_SCSI_NCR53C8XX
+#include "ncr53c8xx.h"
+#endif
+
+#ifdef CONFIG_SCSI_ULTRASTOR
+#include "ultrastor.h"
+#endif
+
+#ifdef CONFIG_SCSI_7000FASST
+#include "wd7000.h"
+#endif
+
+#ifdef CONFIG_SCSI_EATA
+#include "eata.h"
+#endif
+
+#ifdef CONFIG_SCSI_NCR53C406A
+#include "NCR53c406a.h"
+#endif
+
+#ifdef CONFIG_SCSI_DC390W
+#include "dc390w.h"
+#endif
+
+#ifdef CONFIG_SCSI_DC390T
+#include "dc390.h"
+#endif
+
+#ifdef CONFIG_SCSI_AM53C974
+#include "AM53C974.h"
+#endif
+
+#ifdef CONFIG_SCSI_MEGARAID
+#include "megaraid.h"
+#endif
+
+#ifdef CONFIG_SCSI_PPA
+#include "ppa.h"
+#endif
+
+#ifdef CONFIG_SCSI_SUNESP
+#include "esp.h"
+#endif
+
+#ifdef CONFIG_BLK_DEV_IDESCSI
+#include "ide-scsi.h"
+#endif
+
+#ifdef CONFIG_SCSI_GDTH
+#include "gdth.h"
+#endif
+
+#ifdef CONFIG_SCSI_DEBUG
+#include "scsi_debug.h"
+#endif
+
+
+/*
+static const char RCSid[] = "$Header: cvs/gnumach/linux/dev/drivers/scsi/Attic/hosts.c,v 1.1 1999/04/26 05:44:25 tb Exp $";
+*/
+
+/*
+ * The scsi host entries should be in the order you wish the
+ * cards to be detected. A driver may appear more than once IFF
+ * it can deal with being detected (and therefore initialized)
+ * with more than one simultaneous host number, can handle being
+ * reentrant, etc.
+ *
+ * They may appear in any order, as each SCSI host is told which host
+ * number it is during detection.
+ */
+
+/* This is a placeholder for controllers that are not configured into
+ * the system - we do this to ensure that the controller numbering is
+ * always consistent, no matter how the kernel is configured. */
+
+#define NO_CONTROLLER {NULL, NULL, NULL, NULL, NULL, NULL, NULL, \
+ NULL, NULL, 0, 0, 0, 0, 0, 0}
+
+/*
+ * When figure is run, we don't want to link to any object code. Since
+ * the macro for each host will contain function pointers, we cannot
+ * use it and instead must use a "blank" that does no such
+ * idiocy.
+ */
+
+Scsi_Host_Template * scsi_hosts = NULL;
+
+static Scsi_Host_Template builtin_scsi_hosts[] =
+{
+#ifdef CONFIG_AMIGA
+#ifdef CONFIG_A3000_SCSI
+ A3000_SCSI,
+#endif
+#ifdef CONFIG_A2091_SCSI
+ A2091_SCSI,
+#endif
+#ifdef CONFIG_GVP11_SCSI
+ GVP11_SCSI,
+#endif
+#endif
+
+#ifdef CONFIG_ATARI
+#ifdef CONFIG_ATARI_SCSI
+ ATARI_SCSI,
+#endif
+#endif
+
+#ifdef CONFIG_SCSI_ADVANSYS
+ ADVANSYS,
+#endif
+/* BusLogic must come before aha1542.c */
+#ifdef CONFIG_SCSI_BUSLOGIC
+ BUSLOGIC,
+#endif
+#ifdef CONFIG_SCSI_U14_34F
+ ULTRASTOR_14_34F,
+#endif
+#ifdef CONFIG_SCSI_ULTRASTOR
+ ULTRASTOR_14F,
+#endif
+#ifdef CONFIG_SCSI_AHA152X
+ AHA152X,
+#endif
+#ifdef CONFIG_SCSI_AHA1542
+ AHA1542,
+#endif
+#ifdef CONFIG_SCSI_AHA1740
+ AHA1740,
+#endif
+#ifdef CONFIG_SCSI_AIC7XXX
+ AIC7XXX,
+#endif
+#ifdef CONFIG_SCSI_FUTURE_DOMAIN
+ FDOMAIN_16X0,
+#endif
+#ifdef CONFIG_SCSI_IN2000
+ IN2000,
+#endif
+#ifdef CONFIG_SCSI_GENERIC_NCR5380
+ GENERIC_NCR5380,
+#endif
+#ifdef CONFIG_SCSI_NCR53C406A /* 53C406A should come before QLOGIC */
+ NCR53c406a,
+#endif
+#ifdef CONFIG_SCSI_QLOGIC_FAS
+ QLOGICFAS,
+#endif
+#ifdef CONFIG_SCSI_QLOGIC_ISP
+ QLOGICISP,
+#endif
+#ifdef CONFIG_SCSI_PAS16
+ MV_PAS16,
+#endif
+#ifdef CONFIG_SCSI_SEAGATE
+ SEAGATE_ST0X,
+#endif
+#ifdef CONFIG_SCSI_T128
+ TRANTOR_T128,
+#endif
+#ifdef CONFIG_SCSI_DTC3280
+ DTC3x80,
+#endif
+#ifdef CONFIG_SCSI_DC390T
+ DC390_T,
+#endif
+#ifdef CONFIG_SCSI_NCR53C7xx
+ NCR53c7xx,
+#endif
+#ifdef CONFIG_SCSI_NCR53C8XX
+ NCR53C8XX,
+#endif
+#ifdef CONFIG_SCSI_EATA_DMA
+ EATA_DMA,
+#endif
+#ifdef CONFIG_SCSI_EATA_PIO
+ EATA_PIO,
+#endif
+#ifdef CONFIG_SCSI_7000FASST
+ WD7000,
+#endif
+#ifdef CONFIG_SCSI_EATA
+ EATA,
+#endif
+#ifdef CONFIG_SCSI_AM53C974
+ AM53C974,
+#endif
+#ifdef CONFIG_SCSI_MEGARAID
+ MEGARAID,
+#endif
+#ifdef CONFIG_SCSI_PPA
+ PPA,
+#endif
+#ifdef CONFIG_SCSI_SUNESP
+ SCSI_SPARC_ESP,
+#endif
+#ifdef CONFIG_SCSI_GDTH
+ GDTH,
+#endif
+#ifdef CONFIG_BLK_DEV_IDESCSI
+ IDESCSI,
+#endif
+#ifdef CONFIG_SCSI_DEBUG
+ SCSI_DEBUG,
+#endif
+};
+
+#define MAX_SCSI_HOSTS (sizeof(builtin_scsi_hosts) / sizeof(Scsi_Host_Template))
+
+
+/*
+ * Our semaphores and timeout counters, where size depends on
+ * MAX_SCSI_HOSTS here.
+ */
+
+struct Scsi_Host * scsi_hostlist = NULL;
+struct Scsi_Device_Template * scsi_devicelist = NULL;
+
+int max_scsi_hosts = 0;
+int next_scsi_host = 0;
+
+void
+scsi_unregister(struct Scsi_Host * sh){
+ struct Scsi_Host * shpnt;
+
+ if(scsi_hostlist == sh)
+ scsi_hostlist = sh->next;
+ else {
+ shpnt = scsi_hostlist;
+ while(shpnt->next != sh) shpnt = shpnt->next;
+ shpnt->next = shpnt->next->next;
+ }
+
+ /* If we are removing the last host registered, it is safe to reuse
+ * its host number (this avoids "holes" at boot time) (DB)
+ * It is also safe to reuse those of numbers directly below which have
+ * been released earlier (to avoid some holes in numbering).
+ */
+ if(sh->host_no == max_scsi_hosts - 1) {
+ while(--max_scsi_hosts >= next_scsi_host) {
+ shpnt = scsi_hostlist;
+ while(shpnt && shpnt->host_no != max_scsi_hosts - 1)
+ shpnt = shpnt->next;
+ if(shpnt)
+ break;
+ }
+ }
+ next_scsi_host--;
+ scsi_init_free((char *) sh, sizeof(struct Scsi_Host) + sh->extra_bytes);
+}
+
+/* We call this when we come across a new host adapter. We only do this
+ * once we are 100% sure that we want to use this host adapter - it is a
+ * pain to reverse this, so we try to avoid it
+ */
+
+struct Scsi_Host * scsi_register(Scsi_Host_Template * tpnt, int j){
+ struct Scsi_Host * retval, *shpnt;
+ retval = (struct Scsi_Host *)scsi_init_malloc(sizeof(struct Scsi_Host) + j,
+ (tpnt->unchecked_isa_dma && j ? GFP_DMA : 0) | GFP_ATOMIC);
+ retval->host_busy = 0;
+ retval->block = NULL;
+ retval->wish_block = 0;
+ if(j > 0xffff) panic("Too many extra bytes requested\n");
+ retval->extra_bytes = j;
+ retval->loaded_as_module = scsi_loadable_module_flag;
+ retval->host_no = max_scsi_hosts++; /* never reuse host_no (DB) */
+ next_scsi_host++;
+ retval->host_queue = NULL;
+ retval->host_wait = NULL;
+ retval->last_reset = 0;
+ retval->irq = 0;
+ retval->dma_channel = 0xff;
+
+ /* These three are default values which can be overridden */
+ retval->max_channel = 0;
+ retval->max_id = 8;
+ retval->max_lun = 8;
+
+ retval->unique_id = 0;
+ retval->io_port = 0;
+ retval->hostt = tpnt;
+ retval->next = NULL;
+#ifdef DEBUG
+ printk("Register %x %x: %d\n", (int)retval, (int)retval->hostt, j);
+#endif
+
+ /* The next six are the default values which can be overridden
+ * if need be */
+ retval->this_id = tpnt->this_id;
+ retval->can_queue = tpnt->can_queue;
+ retval->sg_tablesize = tpnt->sg_tablesize;
+ retval->cmd_per_lun = tpnt->cmd_per_lun;
+ retval->unchecked_isa_dma = tpnt->unchecked_isa_dma;
+ retval->use_clustering = tpnt->use_clustering;
+
+ retval->select_queue_depths = NULL;
+
+ if(!scsi_hostlist)
+ scsi_hostlist = retval;
+ else
+ {
+ shpnt = scsi_hostlist;
+ while(shpnt->next) shpnt = shpnt->next;
+ shpnt->next = retval;
+ }
+
+ return retval;
+}
+
+int
+scsi_register_device(struct Scsi_Device_Template * sdpnt)
+{
+ if(sdpnt->next) panic("Device already registered");
+ sdpnt->next = scsi_devicelist;
+ scsi_devicelist = sdpnt;
+ return 0;
+}
+
+unsigned int scsi_init()
+{
+ static int called = 0;
+ int i, pcount;
+ Scsi_Host_Template * tpnt;
+ struct Scsi_Host * shpnt;
+ const char * name;
+
+ if(called) return 0;
+
+ called = 1;
+ for (tpnt = &builtin_scsi_hosts[0], i = 0; i < MAX_SCSI_HOSTS; ++i, tpnt++)
+ {
+ /*
+ * Initialize our semaphores. -1 is interpreted to mean
+ * "inactive" - where as 0 will indicate a time out condition.
+ */
+
+ pcount = next_scsi_host;
+ if ((tpnt->detect) &&
+ (tpnt->present =
+ tpnt->detect(tpnt)))
+ {
+ /* The only time this should come up is when people use
+ * some kind of patched driver of some kind or another. */
+ if(pcount == next_scsi_host) {
+ if(tpnt->present > 1)
+ panic("Failure to register low-level scsi driver");
+ /* The low-level driver failed to register a driver. We
+ * can do this now. */
+ scsi_register(tpnt,0);
+ }
+ tpnt->next = scsi_hosts;
+ scsi_hosts = tpnt;
+
+ /* Add the driver to /proc/scsi */
+#if CONFIG_PROC_FS
+ build_proc_dir_entries(tpnt);
+#endif
+ }
+ }
+
+ for(shpnt=scsi_hostlist; shpnt; shpnt = shpnt->next)
+ {
+ if(shpnt->hostt->info)
+ name = shpnt->hostt->info(shpnt);
+ else
+ name = shpnt->hostt->name;
+ printk ("scsi%d : %s\n", /* And print a little message */
+ shpnt->host_no, name);
+ }
+
+ printk ("scsi : %d host%s.\n", next_scsi_host,
+ (next_scsi_host == 1) ? "" : "s");
+
+ scsi_make_blocked_list();
+
+ /* Now attach the high level drivers */
+#ifdef CONFIG_BLK_DEV_SD
+ scsi_register_device(&sd_template);
+#endif
+#ifdef CONFIG_BLK_DEV_SR
+ scsi_register_device(&sr_template);
+#endif
+#ifdef CONFIG_CHR_DEV_ST
+ scsi_register_device(&st_template);
+#endif
+#ifdef CONFIG_CHR_DEV_SG
+ scsi_register_device(&sg_template);
+#endif
+
+#if 0
+ max_scsi_hosts = next_scsi_host;
+#endif
+ return 0;
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 4
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */
diff --git a/linux/dev/drivers/scsi/scsi.c b/linux/dev/drivers/scsi/scsi.c
new file mode 100644
index 0000000..4a821c0
--- /dev/null
+++ b/linux/dev/drivers/scsi/scsi.c
@@ -0,0 +1,3585 @@
+/*
+ * scsi.c Copyright (C) 1992 Drew Eckhardt
+ * Copyright (C) 1993, 1994, 1995 Eric Youngdale
+ *
+ * generic mid-level SCSI driver
+ * Initial versions: Drew Eckhardt
+ * Subsequent revisions: Eric Youngdale
+ *
+ * <drew@colorado.edu>
+ *
+ * Bug correction thanks go to :
+ * Rik Faith <faith@cs.unc.edu>
+ * Tommy Thorn <tthorn>
+ * Thomas Wuensche <tw@fgb1.fgb.mw.tu-muenchen.de>
+ *
+ * Modified by Eric Youngdale eric@aib.com to
+ * add scatter-gather, multiple outstanding request, and other
+ * enhancements.
+ *
+ * Native multichannel, wide scsi, /proc/scsi and hot plugging
+ * support added by Michael Neuffer <mike@i-connect.net>
+ *
+ * Added request_module("scsi_hostadapter") for kerneld:
+ * (Put an "alias scsi_hostadapter your_hostadapter" in /etc/conf.modules)
+ * Bjorn Ekwall <bj0rn@blox.se>
+ *
+ * Major improvements to the timeout, abort, and reset processing,
+ * as well as performance modifications for large queue depths by
+ * Leonard N. Zubkoff <lnz@dandelion.com>
+ */
+
+/*
+ * Don't import our own symbols, as this would severely mess up our
+ * symbol tables.
+ */
+#define _SCSI_SYMS_VER_
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/malloc.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/stat.h>
+#include <linux/blk.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+
+#include "scsi.h"
+#include "hosts.h"
+#include "constants.h"
+
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#endif
+
+#undef USE_STATIC_SCSI_MEMORY
+
+/*
+static const char RCSid[] = "$Header: cvs/gnumach/linux/dev/drivers/scsi/Attic/scsi.c,v 1.1 1999/04/26 05:44:26 tb Exp $";
+*/
+
+
+/* Command groups 3 and 4 are reserved and should never be used. */
+const unsigned char scsi_command_size[8] = { 6, 10, 10, 12, 12, 12, 10, 10 };
+
+#define INTERNAL_ERROR (panic ("Internal error in file %s, line %d.\n", __FILE__, __LINE__))
+
+/*
+ * PAGE_SIZE must be a multiple of the sector size (512). True
+ * for all reasonably recent architectures (even the VAX...).
+ */
+#define SECTOR_SIZE 512
+#define SECTORS_PER_PAGE (PAGE_SIZE/SECTOR_SIZE)
+
+#if SECTORS_PER_PAGE <= 8
+ typedef unsigned char FreeSectorBitmap;
+#elif SECTORS_PER_PAGE <= 32
+ typedef unsigned int FreeSectorBitmap;
+#else
+# error You lose.
+#endif
+
+static void scsi_done (Scsi_Cmnd *SCpnt);
+static int update_timeout (Scsi_Cmnd *, int);
+static void print_inquiry(unsigned char *data);
+static void scsi_times_out (Scsi_Cmnd * SCpnt);
+static int scan_scsis_single (int channel,int dev,int lun,int * max_scsi_dev ,
+ int * sparse_lun, Scsi_Device ** SDpnt, Scsi_Cmnd * SCpnt,
+ struct Scsi_Host *shpnt, char * scsi_result);
+void scsi_build_commandblocks(Scsi_Device * SDpnt);
+
+#ifdef CONFIG_MODULES
+extern struct symbol_table scsi_symbol_table;
+#endif
+
+static FreeSectorBitmap * dma_malloc_freelist = NULL;
+static int scsi_need_isa_bounce_buffers;
+static unsigned int dma_sectors = 0;
+unsigned int dma_free_sectors = 0;
+unsigned int need_isa_buffer = 0;
+static unsigned char ** dma_malloc_pages = NULL;
+
+static int time_start;
+static int time_elapsed;
+static volatile struct Scsi_Host * host_active = NULL;
+#define SCSI_BLOCK(HOST) ((HOST->block && host_active && HOST != host_active) \
+ || (HOST->can_queue && HOST->host_busy >= HOST->can_queue))
+
+const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE] =
+{
+ "Direct-Access ",
+ "Sequential-Access",
+ "Printer ",
+ "Processor ",
+ "WORM ",
+ "CD-ROM ",
+ "Scanner ",
+ "Optical Device ",
+ "Medium Changer ",
+ "Communications "
+};
+
+
+/*
+ * global variables :
+ * scsi_devices an array of these specifying the address for each
+ * (host, id, LUN)
+ */
+
+Scsi_Device * scsi_devices = NULL;
+
+/* Process ID of SCSI commands */
+unsigned long scsi_pid = 0;
+
+static unsigned long serial_number = 0;
+
+static unsigned char generic_sense[6] = {REQUEST_SENSE, 0,0,0, 255, 0};
+static void resize_dma_pool(void);
+
+/* This variable is merely a hook so that we can debug the kernel with gdb. */
+Scsi_Cmnd * last_cmnd = NULL;
+
+/* This is the pointer to the /proc/scsi code.
+ * It is only initialized to !=0 if the scsi code is present
+ */
+#if CONFIG_PROC_FS
+extern int (* dispatch_scsi_info_ptr)(int ino, char *buffer, char **start,
+ off_t offset, int length, int inout);
+extern int dispatch_scsi_info(int ino, char *buffer, char **start,
+ off_t offset, int length, int inout);
+
+struct proc_dir_entry proc_scsi_scsi = {
+ PROC_SCSI_SCSI, 4, "scsi",
+ S_IFREG | S_IRUGO | S_IWUSR, 1, 0, 0, 0,
+ NULL,
+ NULL, NULL,
+ NULL, NULL, NULL
+};
+#endif
+
+/*
+ * This is the number of clock ticks we should wait before we time out
+ * and abort the command. This is for where the scsi.c module generates
+ * the command, not where it originates from a higher level, in which
+ * case the timeout is specified there.
+ *
+ * ABORT_TIMEOUT and RESET_TIMEOUT are the timeouts for RESET and ABORT
+ * respectively.
+ */
+
+#ifdef DEBUG_TIMEOUT
+static void scsi_dump_status(void);
+#endif
+
+
+#ifdef DEBUG
+ #define SCSI_TIMEOUT (5*HZ)
+#else
+ #define SCSI_TIMEOUT (2*HZ)
+#endif
+
+#ifdef DEBUG
+ #define SENSE_TIMEOUT SCSI_TIMEOUT
+ #define ABORT_TIMEOUT SCSI_TIMEOUT
+ #define RESET_TIMEOUT SCSI_TIMEOUT
+#else
+ #define SENSE_TIMEOUT (5*HZ/10)
+ #define RESET_TIMEOUT (5*HZ/10)
+ #define ABORT_TIMEOUT (5*HZ/10)
+#endif
+
+#define MIN_RESET_DELAY (2*HZ)
+
+/* Do not call reset on error if we just did a reset within 15 sec. */
+#define MIN_RESET_PERIOD (15*HZ)
+
+/* The following devices are known not to tolerate a lun != 0 scan for
+ * one reason or another. Some will respond to all luns, others will
+ * lock up.
+ */
+
+#define BLIST_NOLUN 0x01
+#define BLIST_FORCELUN 0x02
+#define BLIST_BORKEN 0x04
+#define BLIST_KEY 0x08
+#define BLIST_SINGLELUN 0x10
+#define BLIST_NOTQ 0x20
+#define BLIST_SPARSELUN 0x40
+#define BLIST_MAX5LUN 0x80
+
+struct dev_info{
+ const char * vendor;
+ const char * model;
+ const char * revision; /* Latest revision known to be bad. Not used yet */
+ unsigned flags;
+};
+
+/*
+ * This is what was previously known as the blacklist. The concept
+ * has been expanded so that we can specify other types of things we
+ * need to be aware of.
+ */
+static struct dev_info device_list[] =
+{
+{"TEAC","CD-R55S","1.0H", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
+{"CHINON","CD-ROM CDS-431","H42", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
+{"CHINON","CD-ROM CDS-535","Q14", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
+{"DENON","DRD-25X","V", BLIST_NOLUN}, /* Locks up if probed for lun != 0 */
+{"HITACHI","DK312C","CM81", BLIST_NOLUN}, /* Responds to all lun - dtg */
+{"HITACHI","DK314C","CR21" , BLIST_NOLUN}, /* responds to all lun */
+{"IMS", "CDD521/10","2.06", BLIST_NOLUN}, /* Locks-up when LUN>0 polled. */
+{"MAXTOR","XT-3280","PR02", BLIST_NOLUN}, /* Locks-up when LUN>0 polled. */
+{"MAXTOR","XT-4380S","B3C", BLIST_NOLUN}, /* Locks-up when LUN>0 polled. */
+{"MAXTOR","MXT-1240S","I1.2", BLIST_NOLUN}, /* Locks up when LUN>0 polled */
+{"MAXTOR","XT-4170S","B5A", BLIST_NOLUN}, /* Locks-up sometimes when LUN>0 polled. */
+{"MAXTOR","XT-8760S","B7B", BLIST_NOLUN}, /* guess what? */
+{"MEDIAVIS","RENO CD-ROMX2A","2.03",BLIST_NOLUN},/*Responds to all lun */
+{"MICROP", "4110", "*", BLIST_NOTQ}, /* Buggy Tagged Queuing */
+{"NEC","CD-ROM DRIVE:841","1.0", BLIST_NOLUN}, /* Locks-up when LUN>0 polled. */
+{"RODIME","RO3000S","2.33", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
+{"SANYO", "CRD-250S", "1.20", BLIST_NOLUN}, /* causes failed REQUEST SENSE on lun 1
+ * for aha152x controller, which causes
+ * SCSI code to reset bus.*/
+{"SEAGATE", "ST157N", "\004|j", BLIST_NOLUN}, /* causes failed REQUEST SENSE on lun 1
+ * for aha152x controller, which causes
+ * SCSI code to reset bus.*/
+{"SEAGATE", "ST296","921", BLIST_NOLUN}, /* Responds to all lun */
+{"SEAGATE","ST1581","6538",BLIST_NOLUN}, /* Responds to all lun */
+{"SONY","CD-ROM CDU-541","4.3d", BLIST_NOLUN},
+{"SONY","CD-ROM CDU-55S","1.0i", BLIST_NOLUN},
+{"SONY","CD-ROM CDU-561","1.7x", BLIST_NOLUN},
+{"TANDBERG","TDC 3600","U07", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
+{"TEAC","CD-ROM","1.06", BLIST_NOLUN}, /* causes failed REQUEST SENSE on lun 1
+ * for seagate controller, which causes
+ * SCSI code to reset bus.*/
+{"TEXEL","CD-ROM","1.06", BLIST_NOLUN}, /* causes failed REQUEST SENSE on lun 1
+ * for seagate controller, which causes
+ * SCSI code to reset bus.*/
+{"QUANTUM","LPS525S","3110", BLIST_NOLUN}, /* Locks sometimes if polled for lun != 0 */
+{"QUANTUM","PD1225S","3110", BLIST_NOLUN}, /* Locks sometimes if polled for lun != 0 */
+{"MEDIAVIS","CDR-H93MV","1.31", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
+{"SANKYO", "CP525","6.64", BLIST_NOLUN}, /* causes failed REQ SENSE, extra reset */
+{"HP", "C1750A", "3226", BLIST_NOLUN}, /* scanjet iic */
+{"HP", "C1790A", "", BLIST_NOLUN}, /* scanjet iip */
+{"HP", "C2500A", "", BLIST_NOLUN}, /* scanjet iicx */
+
+/*
+ * Other types of devices that have special flags.
+ */
+{"SONY","CD-ROM CDU-8001","*", BLIST_BORKEN},
+{"TEXEL","CD-ROM","1.06", BLIST_BORKEN},
+{"IOMEGA","Io20S *F","*", BLIST_KEY},
+{"INSITE","Floptical F*8I","*", BLIST_KEY},
+{"INSITE","I325VM","*", BLIST_KEY},
+{"NRC","MBR-7","*", BLIST_FORCELUN | BLIST_SINGLELUN},
+{"NRC","MBR-7.4","*", BLIST_FORCELUN | BLIST_SINGLELUN},
+{"REGAL","CDC-4X","*", BLIST_MAX5LUN | BLIST_SINGLELUN},
+{"NAKAMICH","MJ-4.8S","*", BLIST_FORCELUN | BLIST_SINGLELUN},
+{"NAKAMICH","MJ-5.16S","*", BLIST_FORCELUN | BLIST_SINGLELUN},
+{"PIONEER","CD-ROM DRM-600","*", BLIST_FORCELUN | BLIST_SINGLELUN},
+{"PIONEER","CD-ROM DRM-602X","*", BLIST_FORCELUN | BLIST_SINGLELUN},
+{"PIONEER","CD-ROM DRM-604X","*", BLIST_FORCELUN | BLIST_SINGLELUN},
+{"EMULEX","MD21/S2 ESDI","*", BLIST_SINGLELUN},
+{"CANON","IPUBJD","*", BLIST_SPARSELUN},
+{"MATSHITA","PD","*", BLIST_FORCELUN | BLIST_SINGLELUN},
+{"YAMAHA","CDR100","1.00", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
+{"YAMAHA","CDR102","1.00", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
+{"nCipher","Fastness Crypto","*", BLIST_FORCELUN},
+/*
+ * Must be at end of list...
+ */
+{NULL, NULL, NULL}
+};
+
+static int get_device_flags(unsigned char * response_data){
+ int i = 0;
+ unsigned char * pnt;
+ for(i=0; 1; i++){
+ if(device_list[i].vendor == NULL) return 0;
+ pnt = &response_data[8];
+ while(*pnt && *pnt == ' ') pnt++;
+ if(memcmp(device_list[i].vendor, pnt,
+ strlen(device_list[i].vendor))) continue;
+ pnt = &response_data[16];
+ while(*pnt && *pnt == ' ') pnt++;
+ if(memcmp(device_list[i].model, pnt,
+ strlen(device_list[i].model))) continue;
+ return device_list[i].flags;
+ }
+ return 0;
+}
+
+void scsi_make_blocked_list(void) {
+ int block_count = 0, index;
+ unsigned long flags;
+ struct Scsi_Host * sh[128], * shpnt;
+
+ /*
+ * Create a circular linked list from the scsi hosts which have
+ * the "wish_block" field in the Scsi_Host structure set.
+ * The blocked list should include all the scsi hosts using ISA DMA.
+ * In some systems, using two dma channels simultaneously causes
+ * unpredictable results.
+ * Among the scsi hosts in the blocked list, only one host at a time
+ * is allowed to have active commands queued. The transition from
+ * one active host to the next one is allowed only when host_busy == 0
+ * for the active host (which implies host_busy == 0 for all the hosts
+ * in the list). Moreover for block devices the transition to a new
+ * active host is allowed only when a request is completed, since a
+ * block device request can be divided into multiple scsi commands
+ * (when there are few sg lists or clustering is disabled).
+ *
+ * (DB, 4 Feb 1995)
+ */
+
+ save_flags(flags);
+ cli();
+ host_active = NULL;
+
+ for(shpnt=scsi_hostlist; shpnt; shpnt = shpnt->next) {
+
+#if 0
+ /*
+ * Is this is a candidate for the blocked list?
+ * Useful to put into the blocked list all the hosts whose driver
+ * does not know about the host->block feature.
+ */
+ if (shpnt->unchecked_isa_dma) shpnt->wish_block = 1;
+#endif
+
+ if (shpnt->wish_block) sh[block_count++] = shpnt;
+ }
+
+ if (block_count == 1) sh[0]->block = NULL;
+
+ else if (block_count > 1) {
+
+ for(index = 0; index < block_count - 1; index++) {
+ sh[index]->block = sh[index + 1];
+ printk("scsi%d : added to blocked host list.\n",
+ sh[index]->host_no);
+ }
+
+ sh[block_count - 1]->block = sh[0];
+ printk("scsi%d : added to blocked host list.\n",
+ sh[index]->host_no);
+ }
+
+ restore_flags(flags);
+}
+
+static void scan_scsis_done (Scsi_Cmnd * SCpnt)
+{
+
+#ifdef DEBUG
+ printk ("scan_scsis_done(%p, %06x)\n", SCpnt->host, SCpnt->result);
+#endif
+ SCpnt->request.rq_status = RQ_SCSI_DONE;
+
+ if (SCpnt->request.sem != NULL)
+ up(SCpnt->request.sem);
+}
+
+#ifdef CONFIG_SCSI_MULTI_LUN
+static int max_scsi_luns = 8;
+#else
+static int max_scsi_luns = 1;
+#endif
+
+void scsi_luns_setup(char *str, int *ints) {
+ if (ints[0] != 1)
+ printk("scsi_luns_setup : usage max_scsi_luns=n (n should be between 1 and 8)\n");
+ else
+ max_scsi_luns = ints[1];
+}
+
+/*
+ * Detecting SCSI devices :
+ * We scan all present host adapter's busses, from ID 0 to ID (max_id).
+ * We use the INQUIRY command, determine device type, and pass the ID /
+ * lun address of all sequential devices to the tape driver, all random
+ * devices to the disk driver.
+ */
+static void scan_scsis (struct Scsi_Host *shpnt, unchar hardcoded,
+ unchar hchannel, unchar hid, unchar hlun)
+{
+ int dev, lun, channel;
+ unsigned char scsi_result0[256];
+ unsigned char *scsi_result;
+ Scsi_Device *SDpnt;
+ int max_dev_lun, sparse_lun;
+ Scsi_Cmnd *SCpnt;
+
+ SCpnt = (Scsi_Cmnd *) scsi_init_malloc (sizeof (Scsi_Cmnd), GFP_ATOMIC | GFP_DMA);
+ SDpnt = (Scsi_Device *) scsi_init_malloc (sizeof (Scsi_Device), GFP_ATOMIC);
+ memset (SCpnt, 0, sizeof (Scsi_Cmnd));
+
+
+ /* Make sure we have something that is valid for DMA purposes */
+ scsi_result = ( ( !shpnt->unchecked_isa_dma )
+ ? &scsi_result0[0] : scsi_init_malloc (512, GFP_DMA));
+
+ if (scsi_result == NULL) {
+ printk ("Unable to obtain scsi_result buffer\n");
+ goto leave;
+ }
+
+ /* We must chain ourself in the host_queue, so commands can time out */
+ if(shpnt->host_queue)
+ shpnt->host_queue->prev = SCpnt;
+ SCpnt->next = shpnt->host_queue;
+ SCpnt->prev = NULL;
+ shpnt->host_queue = SCpnt;
+
+
+ if (hardcoded == 1) {
+ Scsi_Device *oldSDpnt=SDpnt;
+ struct Scsi_Device_Template * sdtpnt;
+ channel = hchannel;
+ if(channel > shpnt->max_channel) goto leave;
+ dev = hid;
+ if(dev >= shpnt->max_id) goto leave;
+ lun = hlun;
+ if(lun >= shpnt->max_lun) goto leave;
+ scan_scsis_single (channel, dev, lun, &max_dev_lun, &sparse_lun,
+ &SDpnt, SCpnt, shpnt, scsi_result);
+ if(SDpnt!=oldSDpnt) {
+
+ /* it could happen the blockdevice hasn't yet been inited */
+ for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+ if(sdtpnt->init && sdtpnt->dev_noticed) (*sdtpnt->init)();
+
+ oldSDpnt->scsi_request_fn = NULL;
+ for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+ if(sdtpnt->attach) {
+ (*sdtpnt->attach)(oldSDpnt);
+ if(oldSDpnt->attached) scsi_build_commandblocks(oldSDpnt);}
+ resize_dma_pool();
+
+ for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next) {
+ if(sdtpnt->finish && sdtpnt->nr_dev)
+ {(*sdtpnt->finish)();}
+ }
+ }
+
+ }
+ else {
+ for (channel = 0; channel <= shpnt->max_channel; channel++) {
+ for (dev = 0; dev < shpnt->max_id; ++dev) {
+ if (shpnt->this_id != dev) {
+
+ /*
+ * We need the for so our continue, etc. work fine. We put this in
+ * a variable so that we can override it during the scan if we
+ * detect a device *KNOWN* to have multiple logical units.
+ */
+ max_dev_lun = (max_scsi_luns < shpnt->max_lun ?
+ max_scsi_luns : shpnt->max_lun);
+ sparse_lun = 0;
+ for (lun = 0; lun < max_dev_lun; ++lun) {
+ if (!scan_scsis_single (channel, dev, lun, &max_dev_lun,
+ &sparse_lun, &SDpnt, SCpnt, shpnt,
+ scsi_result)
+ && !sparse_lun)
+ break; /* break means don't probe further for luns!=0 */
+ } /* for lun ends */
+ } /* if this_id != id ends */
+ } /* for dev ends */
+ } /* for channel ends */
+ } /* if/else hardcoded */
+
+ leave:
+
+ {/* Unchain SCpnt from host_queue */
+ Scsi_Cmnd *prev, *next, *hqptr;
+ for(hqptr = shpnt->host_queue; hqptr != SCpnt; hqptr = hqptr->next) ;
+ if(hqptr) {
+ prev = hqptr->prev;
+ next = hqptr->next;
+ if(prev)
+ prev->next = next;
+ else
+ shpnt->host_queue = next;
+ if(next) next->prev = prev;
+ }
+ }
+
+ /* Last device block does not exist. Free memory. */
+ if (SDpnt != NULL)
+ scsi_init_free ((char *) SDpnt, sizeof (Scsi_Device));
+
+ if (SCpnt != NULL)
+ scsi_init_free ((char *) SCpnt, sizeof (Scsi_Cmnd));
+
+ /* If we allocated a buffer so we could do DMA, free it now */
+ if (scsi_result != &scsi_result0[0] && scsi_result != NULL)
+ scsi_init_free (scsi_result, 512);
+
+}
+
+/*
+ * The worker for scan_scsis.
+ * Returning 0 means Please don't ask further for lun!=0, 1 means OK go on.
+ * Global variables used : scsi_devices(linked list)
+ */
+int scan_scsis_single (int channel, int dev, int lun, int *max_dev_lun,
+ int *sparse_lun, Scsi_Device **SDpnt2, Scsi_Cmnd * SCpnt,
+ struct Scsi_Host * shpnt, char *scsi_result)
+{
+ unsigned char scsi_cmd[12];
+ struct Scsi_Device_Template *sdtpnt;
+ Scsi_Device * SDtail, *SDpnt=*SDpnt2;
+ int bflags, type=-1;
+
+ SDtail = scsi_devices;
+ if (scsi_devices)
+ while (SDtail->next)
+ SDtail = SDtail->next;
+
+ memset (SDpnt, 0, sizeof (Scsi_Device));
+ SDpnt->host = shpnt;
+ SDpnt->id = dev;
+ SDpnt->lun = lun;
+ SDpnt->channel = channel;
+
+ /* Some low level driver could use device->type (DB) */
+ SDpnt->type = -1;
+
+ /*
+ * Assume that the device will have handshaking problems, and then fix this
+ * field later if it turns out it doesn't
+ */
+ SDpnt->borken = 1;
+ SDpnt->was_reset = 0;
+ SDpnt->expecting_cc_ua = 0;
+
+ scsi_cmd[0] = TEST_UNIT_READY;
+ scsi_cmd[1] = lun << 5;
+ scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[4] = scsi_cmd[5] = 0;
+
+ SCpnt->host = SDpnt->host;
+ SCpnt->device = SDpnt;
+ SCpnt->target = SDpnt->id;
+ SCpnt->lun = SDpnt->lun;
+ SCpnt->channel = SDpnt->channel;
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ SCpnt->request.sem = &sem;
+ SCpnt->request.rq_status = RQ_SCSI_BUSY;
+ scsi_do_cmd (SCpnt, (void *) scsi_cmd,
+ (void *) scsi_result,
+ 256, scan_scsis_done, SCSI_TIMEOUT + 4 * HZ, 5);
+ down (&sem);
+ }
+
+#if defined(DEBUG) || defined(DEBUG_INIT)
+ printk ("scsi: scan_scsis_single id %d lun %d. Return code 0x%08x\n",
+ dev, lun, SCpnt->result);
+ print_driverbyte(SCpnt->result); print_hostbyte(SCpnt->result);
+ printk("\n");
+#endif
+
+ if (SCpnt->result) {
+ if (((driver_byte (SCpnt->result) & DRIVER_SENSE) ||
+ (status_byte (SCpnt->result) & CHECK_CONDITION)) &&
+ ((SCpnt->sense_buffer[0] & 0x70) >> 4) == 7) {
+ if (((SCpnt->sense_buffer[2] & 0xf) != NOT_READY) &&
+ ((SCpnt->sense_buffer[2] & 0xf) != UNIT_ATTENTION) &&
+ ((SCpnt->sense_buffer[2] & 0xf) != ILLEGAL_REQUEST || lun > 0))
+ return 1;
+ }
+ else
+ return 0;
+ }
+
+#if defined (DEBUG) || defined(DEBUG_INIT)
+ printk ("scsi: performing INQUIRY\n");
+#endif
+ /*
+ * Build an INQUIRY command block.
+ */
+ scsi_cmd[0] = INQUIRY;
+ scsi_cmd[1] = (lun << 5) & 0xe0;
+ scsi_cmd[2] = 0;
+ scsi_cmd[3] = 0;
+ scsi_cmd[4] = 255;
+ scsi_cmd[5] = 0;
+ SCpnt->cmd_len = 0;
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ SCpnt->request.sem = &sem;
+ SCpnt->request.rq_status = RQ_SCSI_BUSY;
+ scsi_do_cmd (SCpnt, (void *) scsi_cmd,
+ (void *) scsi_result,
+ 256, scan_scsis_done, SCSI_TIMEOUT, 3);
+ down (&sem);
+ }
+
+#if defined(DEBUG) || defined(DEBUG_INIT)
+ printk ("scsi: INQUIRY %s with code 0x%x\n",
+ SCpnt->result ? "failed" : "successful", SCpnt->result);
+#endif
+
+ if (SCpnt->result)
+ return 0; /* assume no peripheral if any sort of error */
+
+ /*
+ * Check the peripheral qualifier field - this tells us whether LUNS
+ * are supported here or not.
+ */
+ if( (scsi_result[0] >> 5) == 3 )
+ {
+ return 0; /* assume no peripheral if any sort of error */
+ }
+
+ /*
+ * It would seem some TOSHIBA CDROM gets things wrong
+ */
+ if (!strncmp (scsi_result + 8, "TOSHIBA", 7) &&
+ !strncmp (scsi_result + 16, "CD-ROM", 6) &&
+ scsi_result[0] == TYPE_DISK) {
+ scsi_result[0] = TYPE_ROM;
+ scsi_result[1] |= 0x80; /* removable */
+ }
+
+ if (!strncmp (scsi_result + 8, "NEC", 3)) {
+ if (!strncmp (scsi_result + 16, "CD-ROM DRIVE:84 ", 16) ||
+ !strncmp (scsi_result + 16, "CD-ROM DRIVE:25", 15))
+ SDpnt->manufacturer = SCSI_MAN_NEC_OLDCDR;
+ else
+ SDpnt->manufacturer = SCSI_MAN_NEC;
+ }
+ else if (!strncmp (scsi_result + 8, "TOSHIBA", 7))
+ SDpnt->manufacturer = SCSI_MAN_TOSHIBA;
+ else if (!strncmp (scsi_result + 8, "SONY", 4))
+ SDpnt->manufacturer = SCSI_MAN_SONY;
+ else if (!strncmp (scsi_result + 8, "PIONEER", 7))
+ SDpnt->manufacturer = SCSI_MAN_PIONEER;
+ else
+ SDpnt->manufacturer = SCSI_MAN_UNKNOWN;
+
+ memcpy (SDpnt->vendor, scsi_result + 8, 8);
+ memcpy (SDpnt->model, scsi_result + 16, 16);
+ memcpy (SDpnt->rev, scsi_result + 32, 4);
+
+ SDpnt->removable = (0x80 & scsi_result[1]) >> 7;
+ SDpnt->lockable = SDpnt->removable;
+ SDpnt->changed = 0;
+ SDpnt->access_count = 0;
+ SDpnt->busy = 0;
+ SDpnt->has_cmdblocks = 0;
+ /*
+ * Currently, all sequential devices are assumed to be tapes, all random
+ * devices disk, with the appropriate read only flags set for ROM / WORM
+ * treated as RO.
+ */
+ switch (type = (scsi_result[0] & 0x1f)) {
+ case TYPE_TAPE:
+ case TYPE_DISK:
+ case TYPE_MOD:
+ case TYPE_PROCESSOR:
+ case TYPE_SCANNER:
+ case TYPE_MEDIUM_CHANGER:
+ SDpnt->writeable = 1;
+ break;
+ case TYPE_WORM:
+ case TYPE_ROM:
+ SDpnt->writeable = 0;
+ break;
+ default:
+ printk ("scsi: unknown type %d\n", type);
+ }
+
+ SDpnt->single_lun = 0;
+ SDpnt->soft_reset =
+ (scsi_result[7] & 1) && ((scsi_result[3] & 7) == 2);
+ SDpnt->random = (type == TYPE_TAPE) ? 0 : 1;
+ SDpnt->type = (type & 0x1f);
+
+ print_inquiry (scsi_result);
+
+ for (sdtpnt = scsi_devicelist; sdtpnt;
+ sdtpnt = sdtpnt->next)
+ if (sdtpnt->detect)
+ SDpnt->attached +=
+ (*sdtpnt->detect) (SDpnt);
+
+ SDpnt->scsi_level = scsi_result[2] & 0x07;
+ if (SDpnt->scsi_level >= 2 ||
+ (SDpnt->scsi_level == 1 &&
+ (scsi_result[3] & 0x0f) == 1))
+ SDpnt->scsi_level++;
+
+ /*
+ * Accommodate drivers that want to sleep when they should be in a polling
+ * loop.
+ */
+ SDpnt->disconnect = 0;
+
+ /*
+ * Get any flags for this device.
+ */
+ bflags = get_device_flags (scsi_result);
+
+ /*
+ * Set the tagged_queue flag for SCSI-II devices that purport to support
+ * tagged queuing in the INQUIRY data.
+ */
+ SDpnt->tagged_queue = 0;
+ if ((SDpnt->scsi_level >= SCSI_2) &&
+ (scsi_result[7] & 2) &&
+ !(bflags & BLIST_NOTQ)) {
+ SDpnt->tagged_supported = 1;
+ SDpnt->current_tag = 0;
+ }
+
+ /*
+ * Some revisions of the Texel CD ROM drives have handshaking problems when
+ * used with the Seagate controllers. Before we know what type of device
+ * we're talking to, we assume it's borken and then change it here if it
+ * turns out that it isn't a TEXEL drive.
+ */
+ if ((bflags & BLIST_BORKEN) == 0)
+ SDpnt->borken = 0;
+
+ /*
+ * If we want to only allow I/O to one of the luns attached to this device
+ * at a time, then we set this flag.
+ */
+ if (bflags & BLIST_SINGLELUN)
+ SDpnt->single_lun = 1;
+
+ /*
+ * These devices need this "key" to unlock the devices so we can use it
+ */
+ if ((bflags & BLIST_KEY) != 0) {
+ printk ("Unlocked floptical drive.\n");
+ SDpnt->lockable = 0;
+ scsi_cmd[0] = MODE_SENSE;
+ scsi_cmd[1] = (lun << 5) & 0xe0;
+ scsi_cmd[2] = 0x2e;
+ scsi_cmd[3] = 0;
+ scsi_cmd[4] = 0x2a;
+ scsi_cmd[5] = 0;
+ SCpnt->cmd_len = 0;
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ SCpnt->request.rq_status = RQ_SCSI_BUSY;
+ SCpnt->request.sem = &sem;
+ scsi_do_cmd (SCpnt, (void *) scsi_cmd,
+ (void *) scsi_result, 0x2a,
+ scan_scsis_done, SCSI_TIMEOUT, 3);
+ down (&sem);
+ }
+ }
+ /* Add this device to the linked list at the end */
+ if (SDtail)
+ SDtail->next = SDpnt;
+ else
+ scsi_devices = SDpnt;
+ SDtail = SDpnt;
+
+ SDpnt = (Scsi_Device *) scsi_init_malloc (sizeof (Scsi_Device), GFP_ATOMIC);
+ *SDpnt2=SDpnt;
+ if (!SDpnt)
+ printk ("scsi: scan_scsis_single: Cannot malloc\n");
+
+
+ /*
+ * Some scsi devices cannot be polled for lun != 0 due to firmware bugs
+ */
+ if (bflags & BLIST_NOLUN)
+ return 0; /* break; */
+
+ /*
+ * If this device is known to support sparse multiple units, override the
+ * other settings, and scan all of them.
+ */
+ if (bflags & BLIST_SPARSELUN) {
+ *max_dev_lun = 8;
+ *sparse_lun = 1;
+ return 1;
+ }
+
+ /*
+ * If this device is known to support multiple units, override the other
+ * settings, and scan all of them.
+ */
+ if (bflags & BLIST_FORCELUN) {
+ *max_dev_lun = 8;
+ return 1;
+ }
+
+ /*
+ * REGAL CDC-4X: avoid hang after LUN 4
+ */
+ if (bflags & BLIST_MAX5LUN) {
+ *max_dev_lun = 5;
+ return 1;
+ }
+
+ /*
+ * We assume the device can't handle lun!=0 if: - it reports scsi-0 (ANSI
+ * SCSI Revision 0) (old drives like MAXTOR XT-3280) or - it reports scsi-1
+ * (ANSI SCSI Revision 1) and Response Data Format 0
+ */
+ if (((scsi_result[2] & 0x07) == 0)
+ ||
+ ((scsi_result[2] & 0x07) == 1 &&
+ (scsi_result[3] & 0x0f) == 0))
+ return 0;
+ return 1;
+}
+
+/*
+ * Flag bits for the internal_timeout array
+ */
+#define NORMAL_TIMEOUT 0
+#define IN_ABORT 1
+#define IN_RESET 2
+#define IN_RESET2 4
+#define IN_RESET3 8
+
+/*
+ * This is our time out function, called when the timer expires for a
+ * given host adapter. It will attempt to abort the currently executing
+ * command, that failing perform a kernel panic.
+ */
+
+static void scsi_times_out (Scsi_Cmnd * SCpnt)
+{
+
+ switch (SCpnt->internal_timeout & (IN_ABORT | IN_RESET | IN_RESET2 | IN_RESET3))
+ {
+ case NORMAL_TIMEOUT:
+ {
+#ifdef DEBUG_TIMEOUT
+ scsi_dump_status();
+#endif
+ }
+
+ if (!scsi_abort (SCpnt, DID_TIME_OUT))
+ return;
+ case IN_ABORT:
+ printk("SCSI host %d abort (pid %ld) timed out - resetting\n",
+ SCpnt->host->host_no, SCpnt->pid);
+ if (!scsi_reset (SCpnt, SCSI_RESET_ASYNCHRONOUS))
+ return;
+ case IN_RESET:
+ case (IN_ABORT | IN_RESET):
+ /* This might be controversial, but if there is a bus hang,
+ * you might conceivably want the machine up and running
+ * esp if you have an ide disk.
+ */
+ printk("SCSI host %d channel %d reset (pid %ld) timed out - "
+ "trying harder\n",
+ SCpnt->host->host_no, SCpnt->channel, SCpnt->pid);
+ SCpnt->internal_timeout &= ~IN_RESET;
+ SCpnt->internal_timeout |= IN_RESET2;
+ scsi_reset (SCpnt,
+ SCSI_RESET_ASYNCHRONOUS | SCSI_RESET_SUGGEST_BUS_RESET);
+ return;
+ case IN_RESET2:
+ case (IN_ABORT | IN_RESET2):
+ /* Obviously the bus reset didn't work.
+ * Let's try even harder and call for an HBA reset.
+ * Maybe the HBA itself crashed and this will shake it loose.
+ */
+ printk("SCSI host %d reset (pid %ld) timed out - trying to shake it loose\n",
+ SCpnt->host->host_no, SCpnt->pid);
+ SCpnt->internal_timeout &= ~(IN_RESET | IN_RESET2);
+ SCpnt->internal_timeout |= IN_RESET3;
+ scsi_reset (SCpnt,
+ SCSI_RESET_ASYNCHRONOUS | SCSI_RESET_SUGGEST_HOST_RESET);
+ return;
+
+ default:
+ printk("SCSI host %d reset (pid %ld) timed out again -\n",
+ SCpnt->host->host_no, SCpnt->pid);
+ printk("probably an unrecoverable SCSI bus or device hang.\n");
+ return;
+
+ }
+
+}
+
+
+/* This function takes a quick look at a request, and decides if it
+ * can be queued now, or if there would be a stall while waiting for
+ * something else to finish. This routine assumes that interrupts are
+ * turned off when entering the routine. It is the responsibility
+ * of the calling code to ensure that this is the case.
+ */
+
+Scsi_Cmnd * request_queueable (struct request * req, Scsi_Device * device)
+{
+ Scsi_Cmnd * SCpnt = NULL;
+ int tablesize;
+ Scsi_Cmnd * found = NULL;
+ struct buffer_head * bh, *bhp;
+
+ if (!device)
+ panic ("No device passed to request_queueable().\n");
+
+ if (req && req->rq_status == RQ_INACTIVE)
+ panic("Inactive in request_queueable");
+
+ /*
+ * Look for a free command block. If we have been instructed not to queue
+ * multiple commands to multi-lun devices, then check to see what else is
+ * going for this device first.
+ */
+
+ if (!device->single_lun) {
+ SCpnt = device->device_queue;
+ while(SCpnt){
+ if(SCpnt->request.rq_status == RQ_INACTIVE) break;
+ SCpnt = SCpnt->device_next;
+ }
+ } else {
+ SCpnt = device->host->host_queue;
+ while(SCpnt){
+ if(SCpnt->channel == device->channel
+ && SCpnt->target == device->id) {
+ if (SCpnt->lun == device->lun) {
+ if(found == NULL
+ && SCpnt->request.rq_status == RQ_INACTIVE)
+ {
+ found=SCpnt;
+ }
+ }
+ if(SCpnt->request.rq_status != RQ_INACTIVE) {
+ /*
+ * I think that we should really limit things to one
+ * outstanding command per device - this is what tends
+ * to trip up buggy firmware.
+ */
+ return NULL;
+ }
+ }
+ SCpnt = SCpnt->next;
+ }
+ SCpnt = found;
+ }
+
+ if (!SCpnt) return NULL;
+
+ if (SCSI_BLOCK(device->host)) return NULL;
+
+ if (req) {
+ memcpy(&SCpnt->request, req, sizeof(struct request));
+ tablesize = device->host->sg_tablesize;
+ bhp = bh = req->bh;
+ if(!tablesize) bh = NULL;
+ /* Take a quick look through the table to see how big it is.
+ * We already have our copy of req, so we can mess with that
+ * if we want to.
+ */
+ while(req->nr_sectors && bh){
+ bhp = bhp->b_reqnext;
+ if(!bhp || !CONTIGUOUS_BUFFERS(bh,bhp)) tablesize--;
+ req->nr_sectors -= bh->b_size >> 9;
+ req->sector += bh->b_size >> 9;
+ if(!tablesize) break;
+ bh = bhp;
+ }
+ if(req->nr_sectors && bh && bh->b_reqnext){ /* Any leftovers? */
+ SCpnt->request.bhtail = bh;
+ req->bh = bh->b_reqnext; /* Divide request */
+ bh->b_reqnext = NULL;
+ bh = req->bh;
+
+ /* Now reset things so that req looks OK */
+ SCpnt->request.nr_sectors -= req->nr_sectors;
+ req->current_nr_sectors = bh->b_size >> 9;
+ req->buffer = bh->b_data;
+ SCpnt->request.sem = NULL; /* Wait until whole thing done */
+ } else {
+ req->rq_status = RQ_INACTIVE;
+ wake_up(&wait_for_request);
+ }
+ } else {
+ SCpnt->request.rq_status = RQ_SCSI_BUSY; /* Busy, but no request */
+ SCpnt->request.sem = NULL; /* And no one is waiting for the device
+ * either */
+ }
+
+ SCpnt->use_sg = 0; /* Reset the scatter-gather flag */
+ SCpnt->old_use_sg = 0;
+ SCpnt->transfersize = 0;
+ SCpnt->underflow = 0;
+ SCpnt->cmd_len = 0;
+
+/* Since not everyone seems to set the device info correctly
+ * before Scsi_Cmnd gets send out to scsi_do_command, we do it here.
+ */
+ SCpnt->channel = device->channel;
+ SCpnt->lun = device->lun;
+ SCpnt->target = device->id;
+
+ return SCpnt;
+}
+
+/* This function returns a structure pointer that will be valid for
+ * the device. The wait parameter tells us whether we should wait for
+ * the unit to become free or not. We are also able to tell this routine
+ * not to return a descriptor if the host is unable to accept any more
+ * commands for the time being. We need to keep in mind that there is no
+ * guarantee that the host remain not busy. Keep in mind the
+ * request_queueable function also knows the internal allocation scheme
+ * of the packets for each device
+ */
+
+Scsi_Cmnd * allocate_device (struct request ** reqp, Scsi_Device * device,
+ int wait)
+{
+ kdev_t dev;
+ struct request * req = NULL;
+ int tablesize;
+ unsigned long flags;
+ struct buffer_head * bh, *bhp;
+ struct Scsi_Host * host;
+ Scsi_Cmnd * SCpnt = NULL;
+ Scsi_Cmnd * SCwait = NULL;
+ Scsi_Cmnd * found = NULL;
+
+ if (!device)
+ panic ("No device passed to allocate_device().\n");
+
+ if (reqp) req = *reqp;
+
+ /* See if this request has already been queued by an interrupt routine */
+ if (req) {
+ if(req->rq_status == RQ_INACTIVE) return NULL;
+ dev = req->rq_dev;
+ } else
+ dev = 0; /* unused */
+
+ host = device->host;
+
+ if (intr_count && SCSI_BLOCK(host)) return NULL;
+
+ while (1==1){
+ if (!device->single_lun) {
+ SCpnt = device->device_queue;
+ while(SCpnt){
+ SCwait = SCpnt;
+ if(SCpnt->request.rq_status == RQ_INACTIVE) break;
+ SCpnt = SCpnt->device_next;
+ }
+ } else {
+ SCpnt = device->host->host_queue;
+ while(SCpnt){
+ if(SCpnt->channel == device->channel
+ && SCpnt->target == device->id) {
+ if (SCpnt->lun == device->lun) {
+ SCwait = SCpnt;
+ if(found == NULL
+ && SCpnt->request.rq_status == RQ_INACTIVE)
+ {
+ found=SCpnt;
+ }
+ }
+ if(SCpnt->request.rq_status != RQ_INACTIVE) {
+ /*
+ * I think that we should really limit things to one
+ * outstanding command per device - this is what tends
+ * to trip up buggy firmware.
+ */
+ found = NULL;
+ break;
+ }
+ }
+ SCpnt = SCpnt->next;
+ }
+ SCpnt = found;
+ }
+
+ save_flags(flags);
+ cli();
+ /* See if this request has already been queued by an interrupt routine
+ */
+ if (req && (req->rq_status == RQ_INACTIVE || req->rq_dev != dev)) {
+ restore_flags(flags);
+ return NULL;
+ }
+ if (!SCpnt || SCpnt->request.rq_status != RQ_INACTIVE) /* Might have changed */
+ {
+#if 1 /* NEW CODE */
+ if (wait && SCwait && SCwait->request.rq_status != RQ_INACTIVE){
+ sleep_on(&device->device_wait);
+ restore_flags(flags);
+ } else {
+ restore_flags(flags);
+ if (!wait) return NULL;
+ if (!SCwait) {
+ printk("Attempt to allocate device channel %d,"
+ " target %d, lun %d\n", device->channel,
+ device->id, device->lun);
+ panic("No device found in allocate_device\n");
+ }
+ }
+#else /* ORIGINAL CODE */
+ restore_flags(flags);
+ if(!wait) return NULL;
+ if (!SCwait) {
+ printk("Attempt to allocate device channel %d, target"
+ " %d, lun %d\n", device->channel, device->id,
+ device->lun);
+ panic("No device found in allocate_device\n");
+ }
+ SCSI_SLEEP(&device->device_wait,
+ (SCwait->request.rq_status != RQ_INACTIVE));
+#endif
+ } else {
+ if (req) {
+ memcpy(&SCpnt->request, req, sizeof(struct request));
+ tablesize = device->host->sg_tablesize;
+ bhp = bh = req->bh;
+ if(!tablesize) bh = NULL;
+ /* Take a quick look through the table to see how big it is.
+ * We already have our copy of req, so we can mess with that
+ * if we want to.
+ */
+ while(req->nr_sectors && bh){
+ bhp = bhp->b_reqnext;
+ if(!bhp || !CONTIGUOUS_BUFFERS(bh,bhp)) tablesize--;
+ req->nr_sectors -= bh->b_size >> 9;
+ req->sector += bh->b_size >> 9;
+ if(!tablesize) break;
+ bh = bhp;
+ }
+ if(req->nr_sectors && bh && bh->b_reqnext){/* Any leftovers? */
+ SCpnt->request.bhtail = bh;
+ req->bh = bh->b_reqnext; /* Divide request */
+ bh->b_reqnext = NULL;
+ bh = req->bh;
+ /* Now reset things so that req looks OK */
+ SCpnt->request.nr_sectors -= req->nr_sectors;
+ req->current_nr_sectors = bh->b_size >> 9;
+ req->buffer = bh->b_data;
+ SCpnt->request.sem = NULL; /* Wait until whole thing done*/
+ }
+ else
+ {
+ req->rq_status = RQ_INACTIVE;
+ *reqp = req->next;
+ wake_up(&wait_for_request);
+ }
+ } else {
+ SCpnt->request.rq_status = RQ_SCSI_BUSY;
+ SCpnt->request.sem = NULL; /* And no one is waiting for this
+ * to complete */
+ }
+ restore_flags(flags);
+ break;
+ }
+ }
+
+ SCpnt->use_sg = 0; /* Reset the scatter-gather flag */
+ SCpnt->old_use_sg = 0;
+ SCpnt->transfersize = 0; /* No default transfer size */
+ SCpnt->cmd_len = 0;
+
+ SCpnt->underflow = 0; /* Do not flag underflow conditions */
+
+ /* Since not everyone seems to set the device info correctly
+ * before Scsi_Cmnd gets send out to scsi_do_command, we do it here.
+ */
+ SCpnt->channel = device->channel;
+ SCpnt->lun = device->lun;
+ SCpnt->target = device->id;
+
+ return SCpnt;
+}
+
+/*
+ * This is inline because we have stack problemes if we recurse to deeply.
+ */
+
+inline void internal_cmnd (Scsi_Cmnd * SCpnt)
+{
+ unsigned long flags, timeout;
+ struct Scsi_Host * host;
+#ifdef DEBUG_DELAY
+ unsigned long clock;
+#endif
+
+#if DEBUG
+ unsigned long *ret = 0;
+#ifdef __mips__
+ __asm__ __volatile__ ("move\t%0,$31":"=r"(ret));
+#else
+ ret = __builtin_return_address(0);
+#endif
+#endif
+
+ host = SCpnt->host;
+
+ save_flags(flags);
+ cli();
+ /* Assign a unique nonzero serial_number. */
+ if (++serial_number == 0) serial_number = 1;
+ SCpnt->serial_number = serial_number;
+
+ /*
+ * We will wait MIN_RESET_DELAY clock ticks after the last reset so
+ * we can avoid the drive not being ready.
+ */
+ timeout = host->last_reset + MIN_RESET_DELAY;
+ if (jiffies < timeout) {
+ int ticks_remaining = timeout - jiffies;
+ /*
+ * NOTE: This may be executed from within an interrupt
+ * handler! This is bad, but for now, it'll do. The irq
+ * level of the interrupt handler has been masked out by the
+ * platform dependent interrupt handling code already, so the
+ * sti() here will not cause another call to the SCSI host's
+ * interrupt handler (assuming there is one irq-level per
+ * host).
+ */
+ sti();
+ while (--ticks_remaining >= 0) udelay(1000000/HZ);
+ host->last_reset = jiffies - MIN_RESET_DELAY;
+ }
+ restore_flags(flags);
+
+ update_timeout(SCpnt, SCpnt->timeout_per_command);
+
+ /*
+ * We will use a queued command if possible, otherwise we will emulate the
+ * queuing and calling of completion function ourselves.
+ */
+#ifdef DEBUG
+ printk("internal_cmnd (host = %d, channel = %d, target = %d, "
+ "command = %p, buffer = %p, \nbufflen = %d, done = %p)\n",
+ SCpnt->host->host_no, SCpnt->channel, SCpnt->target, SCpnt->cmnd,
+ SCpnt->buffer, SCpnt->bufflen, SCpnt->done);
+#endif
+
+ if (host->can_queue)
+ {
+#ifdef DEBUG
+ printk("queuecommand : routine at %p\n",
+ host->hostt->queuecommand);
+#endif
+ /* This locking tries to prevent all sorts of races between
+ * queuecommand and the interrupt code. In effect,
+ * we are only allowed to be in queuecommand once at
+ * any given time, and we can only be in the interrupt
+ * handler and the queuecommand function at the same time
+ * when queuecommand is called while servicing the
+ * interrupt.
+ */
+
+ if(!intr_count && SCpnt->host->irq)
+ disable_irq(SCpnt->host->irq);
+
+ host->hostt->queuecommand (SCpnt, scsi_done);
+
+ if(!intr_count && SCpnt->host->irq)
+ enable_irq(SCpnt->host->irq);
+ }
+ else
+ {
+ int temp;
+
+#ifdef DEBUG
+ printk("command() : routine at %p\n", host->hostt->command);
+#endif
+ temp = host->hostt->command (SCpnt);
+ SCpnt->result = temp;
+#ifdef DEBUG_DELAY
+ clock = jiffies + 4 * HZ;
+ while (jiffies < clock) barrier();
+ printk("done(host = %d, result = %04x) : routine at %p\n",
+ host->host_no, temp, host->hostt->command);
+#endif
+ scsi_done(SCpnt);
+ }
+#ifdef DEBUG
+ printk("leaving internal_cmnd()\n");
+#endif
+}
+
+static void scsi_request_sense (Scsi_Cmnd * SCpnt)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ SCpnt->flags |= WAS_SENSE | ASKED_FOR_SENSE;
+ update_timeout(SCpnt, SENSE_TIMEOUT);
+ restore_flags(flags);
+
+
+ memcpy ((void *) SCpnt->cmnd , (void *) generic_sense,
+ sizeof(generic_sense));
+
+ SCpnt->cmnd[1] = SCpnt->lun << 5;
+ SCpnt->cmnd[4] = sizeof(SCpnt->sense_buffer);
+
+ SCpnt->request_buffer = &SCpnt->sense_buffer;
+ SCpnt->request_bufflen = sizeof(SCpnt->sense_buffer);
+ SCpnt->use_sg = 0;
+ SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]);
+ internal_cmnd (SCpnt);
+}
+
+
+
+/*
+ * scsi_do_cmd sends all the commands out to the low-level driver. It
+ * handles the specifics required for each low level driver - ie queued
+ * or non queued. It also prevents conflicts when different high level
+ * drivers go for the same host at the same time.
+ */
+
+void scsi_do_cmd (Scsi_Cmnd * SCpnt, const void *cmnd ,
+ void *buffer, unsigned bufflen, void (*done)(Scsi_Cmnd *),
+ int timeout, int retries)
+{
+ unsigned long flags;
+ struct Scsi_Host * host = SCpnt->host;
+
+#ifdef DEBUG
+ {
+ int i;
+ int target = SCpnt->target;
+ printk ("scsi_do_cmd (host = %d, channel = %d target = %d, "
+ "buffer =%p, bufflen = %d, done = %p, timeout = %d, "
+ "retries = %d)\n"
+ "command : " , host->host_no, SCpnt->channel, target, buffer,
+ bufflen, done, timeout, retries);
+ for (i = 0; i < 10; ++i)
+ printk ("%02x ", ((unsigned char *) cmnd)[i]);
+ printk("\n");
+ }
+#endif
+
+ if (!host)
+ {
+ panic ("Invalid or not present host.\n");
+ }
+
+
+ /*
+ * We must prevent reentrancy to the lowlevel host driver. This prevents
+ * it - we enter a loop until the host we want to talk to is not busy.
+ * Race conditions are prevented, as interrupts are disabled in between the
+ * time we check for the host being not busy, and the time we mark it busy
+ * ourselves.
+ */
+
+ save_flags(flags);
+ cli();
+ SCpnt->pid = scsi_pid++;
+
+ while (SCSI_BLOCK(host)) {
+ restore_flags(flags);
+ SCSI_SLEEP(&host->host_wait, SCSI_BLOCK(host));
+ cli();
+ }
+
+ if (host->block) host_active = host;
+
+ host->host_busy++;
+ restore_flags(flags);
+
+ /*
+ * Our own function scsi_done (which marks the host as not busy, disables
+ * the timeout counter, etc) will be called by us or by the
+ * scsi_hosts[host].queuecommand() function needs to also call
+ * the completion function for the high level driver.
+ */
+
+ memcpy ((void *) SCpnt->data_cmnd , (const void *) cmnd, 12);
+#if 0
+ SCpnt->host = host;
+ SCpnt->channel = channel;
+ SCpnt->target = target;
+ SCpnt->lun = (SCpnt->data_cmnd[1] >> 5);
+#endif
+ SCpnt->reset_chain = NULL;
+ SCpnt->serial_number = 0;
+ SCpnt->bufflen = bufflen;
+ SCpnt->buffer = buffer;
+ SCpnt->flags = 0;
+ SCpnt->retries = 0;
+ SCpnt->allowed = retries;
+ SCpnt->done = done;
+ SCpnt->timeout_per_command = timeout;
+
+ memcpy ((void *) SCpnt->cmnd , (const void *) cmnd, 12);
+ /* Zero the sense buffer. Some host adapters automatically request
+ * sense on error. 0 is not a valid sense code.
+ */
+ memset ((void *) SCpnt->sense_buffer, 0, sizeof SCpnt->sense_buffer);
+ SCpnt->request_buffer = buffer;
+ SCpnt->request_bufflen = bufflen;
+ SCpnt->old_use_sg = SCpnt->use_sg;
+ if (SCpnt->cmd_len == 0)
+ SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]);
+ SCpnt->old_cmd_len = SCpnt->cmd_len;
+
+ /* Start the timer ticking. */
+
+ SCpnt->internal_timeout = NORMAL_TIMEOUT;
+ SCpnt->abort_reason = 0;
+ internal_cmnd (SCpnt);
+
+#ifdef DEBUG
+ printk ("Leaving scsi_do_cmd()\n");
+#endif
+}
+
+static int check_sense (Scsi_Cmnd * SCpnt)
+{
+ /* If there is no sense information, request it. If we have already
+ * requested it, there is no point in asking again - the firmware must
+ * be confused.
+ */
+ if (((SCpnt->sense_buffer[0] & 0x70) >> 4) != 7) {
+ if(!(SCpnt->flags & ASKED_FOR_SENSE))
+ return SUGGEST_SENSE;
+ else
+ return SUGGEST_RETRY;
+ }
+
+ SCpnt->flags &= ~ASKED_FOR_SENSE;
+
+#ifdef DEBUG_INIT
+ printk("scsi%d, channel%d : ", SCpnt->host->host_no, SCpnt->channel);
+ print_sense("", SCpnt);
+ printk("\n");
+#endif
+ if (SCpnt->sense_buffer[2] & 0xe0)
+ return SUGGEST_ABORT;
+
+ switch (SCpnt->sense_buffer[2] & 0xf)
+ {
+ case NO_SENSE:
+ return 0;
+ case RECOVERED_ERROR:
+ return SUGGEST_IS_OK;
+
+ case ABORTED_COMMAND:
+ return SUGGEST_RETRY;
+ case NOT_READY:
+ case UNIT_ATTENTION:
+ /*
+ * If we are expecting a CC/UA because of a bus reset that we
+ * performed, treat this just as a retry. Otherwise this is
+ * information that we should pass up to the upper-level driver
+ * so that we can deal with it there.
+ */
+ if( SCpnt->device->expecting_cc_ua )
+ {
+ SCpnt->device->expecting_cc_ua = 0;
+ return SUGGEST_RETRY;
+ }
+ return SUGGEST_ABORT;
+
+ /* these three are not supported */
+ case COPY_ABORTED:
+ case VOLUME_OVERFLOW:
+ case MISCOMPARE:
+
+ case MEDIUM_ERROR:
+ return SUGGEST_REMAP;
+ case BLANK_CHECK:
+ case DATA_PROTECT:
+ case HARDWARE_ERROR:
+ case ILLEGAL_REQUEST:
+ default:
+ return SUGGEST_ABORT;
+ }
+}
+
+/* This function is the mid-level interrupt routine, which decides how
+ * to handle error conditions. Each invocation of this function must
+ * do one and *only* one of the following:
+ *
+ * (1) Call last_cmnd[host].done. This is done for fatal errors and
+ * normal completion, and indicates that the handling for this
+ * request is complete.
+ * (2) Call internal_cmnd to requeue the command. This will result in
+ * scsi_done being called again when the retry is complete.
+ * (3) Call scsi_request_sense. This asks the host adapter/drive for
+ * more information about the error condition. When the information
+ * is available, scsi_done will be called again.
+ * (4) Call reset(). This is sort of a last resort, and the idea is that
+ * this may kick things loose and get the drive working again. reset()
+ * automatically calls scsi_request_sense, and thus scsi_done will be
+ * called again once the reset is complete.
+ *
+ * If none of the above actions are taken, the drive in question
+ * will hang. If more than one of the above actions are taken by
+ * scsi_done, then unpredictable behavior will result.
+ */
+static void scsi_done (Scsi_Cmnd * SCpnt)
+{
+ int status=0;
+ int exit=0;
+ int checked;
+ int oldto;
+ struct Scsi_Host * host = SCpnt->host;
+ int result = SCpnt->result;
+ SCpnt->serial_number = 0;
+ oldto = update_timeout(SCpnt, 0);
+
+#ifdef DEBUG_TIMEOUT
+ if(result) printk("Non-zero result in scsi_done %x %d:%d\n",
+ result, SCpnt->target, SCpnt->lun);
+#endif
+
+ /* If we requested an abort, (and we got it) then fix up the return
+ * status to say why
+ */
+ if(host_byte(result) == DID_ABORT && SCpnt->abort_reason)
+ SCpnt->result = result = (result & 0xff00ffff) |
+ (SCpnt->abort_reason << 16);
+
+
+#define FINISHED 0
+#define MAYREDO 1
+#define REDO 3
+#define PENDING 4
+
+#ifdef DEBUG
+ printk("In scsi_done(host = %d, result = %06x)\n", host->host_no, result);
+#endif
+
+ if(SCpnt->flags & WAS_SENSE)
+ {
+ SCpnt->use_sg = SCpnt->old_use_sg;
+ SCpnt->cmd_len = SCpnt->old_cmd_len;
+ }
+
+ switch (host_byte(result))
+ {
+ case DID_OK:
+ if (status_byte(result) && (SCpnt->flags & WAS_SENSE))
+ /* Failed to obtain sense information */
+ {
+ SCpnt->flags &= ~WAS_SENSE;
+#if 0 /* This cannot possibly be correct. */
+ SCpnt->internal_timeout &= ~SENSE_TIMEOUT;
+#endif
+
+ if (!(SCpnt->flags & WAS_RESET))
+ {
+ printk("scsi%d : channel %d target %d lun %d request sense"
+ " failed, performing reset.\n",
+ SCpnt->host->host_no, SCpnt->channel, SCpnt->target,
+ SCpnt->lun);
+ scsi_reset(SCpnt, SCSI_RESET_SYNCHRONOUS);
+ return;
+ }
+ else
+ {
+ exit = (DRIVER_HARD | SUGGEST_ABORT);
+ status = FINISHED;
+ }
+ }
+ else switch(msg_byte(result))
+ {
+ case COMMAND_COMPLETE:
+ switch (status_byte(result))
+ {
+ case GOOD:
+ if (SCpnt->flags & WAS_SENSE)
+ {
+#ifdef DEBUG
+ printk ("In scsi_done, GOOD status, COMMAND COMPLETE, "
+ "parsing sense information.\n");
+#endif
+ SCpnt->flags &= ~WAS_SENSE;
+#if 0 /* This cannot possibly be correct. */
+ SCpnt->internal_timeout &= ~SENSE_TIMEOUT;
+#endif
+
+ switch (checked = check_sense(SCpnt))
+ {
+ case SUGGEST_SENSE:
+ case 0:
+#ifdef DEBUG
+ printk("NO SENSE. status = REDO\n");
+#endif
+ update_timeout(SCpnt, oldto);
+ status = REDO;
+ break;
+ case SUGGEST_IS_OK:
+ break;
+ case SUGGEST_REMAP:
+#ifdef DEBUG
+ printk("SENSE SUGGEST REMAP - status = FINISHED\n");
+#endif
+ status = FINISHED;
+ exit = DRIVER_SENSE | SUGGEST_ABORT;
+ break;
+ case SUGGEST_RETRY:
+#ifdef DEBUG
+ printk("SENSE SUGGEST RETRY - status = MAYREDO\n");
+#endif
+ status = MAYREDO;
+ exit = DRIVER_SENSE | SUGGEST_RETRY;
+ break;
+ case SUGGEST_ABORT:
+#ifdef DEBUG
+ printk("SENSE SUGGEST ABORT - status = FINISHED");
+#endif
+ status = FINISHED;
+ exit = DRIVER_SENSE | SUGGEST_ABORT;
+ break;
+ default:
+ printk ("Internal error %s %d \n", __FILE__,
+ __LINE__);
+ }
+ } /* end WAS_SENSE */
+ else
+ {
+#ifdef DEBUG
+ printk("COMMAND COMPLETE message returned, "
+ "status = FINISHED. \n");
+#endif
+ exit = DRIVER_OK;
+ status = FINISHED;
+ }
+ break;
+
+ case CHECK_CONDITION:
+ case COMMAND_TERMINATED:
+ switch (check_sense(SCpnt))
+ {
+ case 0:
+ update_timeout(SCpnt, oldto);
+ status = REDO;
+ break;
+ case SUGGEST_REMAP:
+ status = FINISHED;
+ exit = DRIVER_SENSE | SUGGEST_ABORT;
+ break;
+ case SUGGEST_RETRY:
+ status = MAYREDO;
+ exit = DRIVER_SENSE | SUGGEST_RETRY;
+ break;
+ case SUGGEST_ABORT:
+ status = FINISHED;
+ exit = DRIVER_SENSE | SUGGEST_ABORT;
+ break;
+ case SUGGEST_SENSE:
+ scsi_request_sense (SCpnt);
+ status = PENDING;
+ break;
+ }
+ break;
+
+ case CONDITION_GOOD:
+ case INTERMEDIATE_GOOD:
+ case INTERMEDIATE_C_GOOD:
+ break;
+
+ case BUSY:
+ case QUEUE_FULL:
+ update_timeout(SCpnt, oldto);
+ status = REDO;
+ break;
+
+ case RESERVATION_CONFLICT:
+ printk("scsi%d, channel %d : RESERVATION CONFLICT performing"
+ " reset.\n", SCpnt->host->host_no, SCpnt->channel);
+ scsi_reset(SCpnt, SCSI_RESET_SYNCHRONOUS);
+ return;
+#if 0
+ exit = DRIVER_SOFT | SUGGEST_ABORT;
+ status = MAYREDO;
+ break;
+#endif
+ default:
+ printk ("Internal error %s %d \n"
+ "status byte = %d \n", __FILE__,
+ __LINE__, status_byte(result));
+
+ }
+ break;
+ default:
+ panic("scsi: unsupported message byte %d received\n",
+ msg_byte(result));
+ }
+ break;
+ case DID_TIME_OUT:
+#ifdef DEBUG
+ printk("Host returned DID_TIME_OUT - ");
+#endif
+
+ if (SCpnt->flags & WAS_TIMEDOUT)
+ {
+#ifdef DEBUG
+ printk("Aborting\n");
+#endif
+ /*
+ Allow TEST_UNIT_READY and INQUIRY commands to timeout early
+ without causing resets. All other commands should be retried.
+ */
+ if (SCpnt->cmnd[0] != TEST_UNIT_READY &&
+ SCpnt->cmnd[0] != INQUIRY)
+ status = MAYREDO;
+ exit = (DRIVER_TIMEOUT | SUGGEST_ABORT);
+ }
+ else
+ {
+#ifdef DEBUG
+ printk ("Retrying.\n");
+#endif
+ SCpnt->flags |= WAS_TIMEDOUT;
+ SCpnt->internal_timeout &= ~IN_ABORT;
+ status = REDO;
+ }
+ break;
+ case DID_BUS_BUSY:
+ case DID_PARITY:
+ status = REDO;
+ break;
+ case DID_NO_CONNECT:
+#ifdef DEBUG
+ printk("Couldn't connect.\n");
+#endif
+ exit = (DRIVER_HARD | SUGGEST_ABORT);
+ break;
+ case DID_ERROR:
+ status = MAYREDO;
+ exit = (DRIVER_HARD | SUGGEST_ABORT);
+ break;
+ case DID_BAD_TARGET:
+ case DID_ABORT:
+ exit = (DRIVER_INVALID | SUGGEST_ABORT);
+ break;
+ case DID_RESET:
+ if (SCpnt->flags & IS_RESETTING)
+ {
+ SCpnt->flags &= ~IS_RESETTING;
+ status = REDO;
+ break;
+ }
+
+ if(msg_byte(result) == GOOD &&
+ status_byte(result) == CHECK_CONDITION) {
+ switch (check_sense(SCpnt)) {
+ case 0:
+ update_timeout(SCpnt, oldto);
+ status = REDO;
+ break;
+ case SUGGEST_REMAP:
+ case SUGGEST_RETRY:
+ status = MAYREDO;
+ exit = DRIVER_SENSE | SUGGEST_RETRY;
+ break;
+ case SUGGEST_ABORT:
+ status = FINISHED;
+ exit = DRIVER_SENSE | SUGGEST_ABORT;
+ break;
+ case SUGGEST_SENSE:
+ scsi_request_sense (SCpnt);
+ status = PENDING;
+ break;
+ }
+ } else {
+ status=REDO;
+ exit = SUGGEST_RETRY;
+ }
+ break;
+ default :
+ exit = (DRIVER_ERROR | SUGGEST_DIE);
+ }
+
+ switch (status)
+ {
+ case FINISHED:
+ case PENDING:
+ break;
+ case MAYREDO:
+#ifdef DEBUG
+ printk("In MAYREDO, allowing %d retries, have %d\n",
+ SCpnt->allowed, SCpnt->retries);
+#endif
+ if ((++SCpnt->retries) < SCpnt->allowed)
+ {
+ if ((SCpnt->retries >= (SCpnt->allowed >> 1))
+ && !(SCpnt->host->last_reset > 0 &&
+ jiffies < SCpnt->host->last_reset + MIN_RESET_PERIOD)
+ && !(SCpnt->flags & WAS_RESET))
+ {
+ printk("scsi%d channel %d : resetting for second half of retries.\n",
+ SCpnt->host->host_no, SCpnt->channel);
+ scsi_reset(SCpnt, SCSI_RESET_SYNCHRONOUS);
+ break;
+ }
+
+ }
+ else
+ {
+ status = FINISHED;
+ break;
+ }
+ /* fall through to REDO */
+
+ case REDO:
+
+ if (SCpnt->flags & WAS_SENSE)
+ scsi_request_sense(SCpnt);
+ else
+ {
+ memcpy ((void *) SCpnt->cmnd,
+ (void*) SCpnt->data_cmnd,
+ sizeof(SCpnt->data_cmnd));
+ SCpnt->request_buffer = SCpnt->buffer;
+ SCpnt->request_bufflen = SCpnt->bufflen;
+ SCpnt->use_sg = SCpnt->old_use_sg;
+ SCpnt->cmd_len = SCpnt->old_cmd_len;
+ internal_cmnd (SCpnt);
+ }
+ break;
+ default:
+ INTERNAL_ERROR;
+ }
+
+ if (status == FINISHED) {
+#ifdef DEBUG
+ printk("Calling done function - at address %p\n", SCpnt->done);
+#endif
+ host->host_busy--; /* Indicate that we are free */
+
+ if (host->block && host->host_busy == 0) {
+ host_active = NULL;
+
+ /* For block devices "wake_up" is done in end_scsi_request */
+ if (MAJOR(SCpnt->request.rq_dev) != SCSI_DISK_MAJOR &&
+ MAJOR(SCpnt->request.rq_dev) != SCSI_CDROM_MAJOR) {
+ struct Scsi_Host * next;
+
+ for (next = host->block; next != host; next = next->block)
+ wake_up(&next->host_wait);
+ }
+
+ }
+
+ wake_up(&host->host_wait);
+ SCpnt->result = result | ((exit & 0xff) << 24);
+ SCpnt->use_sg = SCpnt->old_use_sg;
+ SCpnt->cmd_len = SCpnt->old_cmd_len;
+ SCpnt->done (SCpnt);
+ }
+
+#undef FINISHED
+#undef REDO
+#undef MAYREDO
+#undef PENDING
+}
+
+/*
+ * The scsi_abort function interfaces with the abort() function of the host
+ * we are aborting, and causes the current command to not complete. The
+ * caller should deal with any error messages or status returned on the
+ * next call.
+ *
+ * This will not be called reentrantly for a given host.
+ */
+
+/*
+ * Since we're nice guys and specified that abort() and reset()
+ * can be non-reentrant. The internal_timeout flags are used for
+ * this.
+ */
+
+
+int scsi_abort (Scsi_Cmnd * SCpnt, int why)
+{
+ int oldto;
+ unsigned long flags;
+ struct Scsi_Host * host = SCpnt->host;
+
+ while(1)
+ {
+ save_flags(flags);
+ cli();
+
+ /*
+ * Protect against races here. If the command is done, or we are
+ * on a different command forget it.
+ */
+ if (SCpnt->serial_number != SCpnt->serial_number_at_timeout) {
+ restore_flags(flags);
+ return 0;
+ }
+
+ if (SCpnt->internal_timeout & IN_ABORT)
+ {
+ restore_flags(flags);
+ while (SCpnt->internal_timeout & IN_ABORT)
+ barrier();
+ }
+ else
+ {
+ SCpnt->internal_timeout |= IN_ABORT;
+ oldto = update_timeout(SCpnt, ABORT_TIMEOUT);
+
+ if ((SCpnt->flags & IS_RESETTING) && SCpnt->device->soft_reset) {
+ /* OK, this command must have died when we did the
+ * reset. The device itself must have lied.
+ */
+ printk("Stale command on %d %d:%d appears to have died when"
+ " the bus was reset\n",
+ SCpnt->channel, SCpnt->target, SCpnt->lun);
+ }
+
+ restore_flags(flags);
+ if (!host->host_busy) {
+ SCpnt->internal_timeout &= ~IN_ABORT;
+ update_timeout(SCpnt, oldto);
+ return 0;
+ }
+ printk("scsi : aborting command due to timeout : pid %lu, scsi%d,"
+ " channel %d, id %d, lun %d ",
+ SCpnt->pid, SCpnt->host->host_no, (int) SCpnt->channel,
+ (int) SCpnt->target, (int) SCpnt->lun);
+ print_command (SCpnt->cmnd);
+ if (SCpnt->serial_number != SCpnt->serial_number_at_timeout)
+ return 0;
+ SCpnt->abort_reason = why;
+ switch(host->hostt->abort(SCpnt)) {
+ /* We do not know how to abort. Try waiting another
+ * time increment and see if this helps. Set the
+ * WAS_TIMEDOUT flag set so we do not try this twice
+ */
+ case SCSI_ABORT_BUSY: /* Tough call - returning 1 from
+ * this is too severe
+ */
+ case SCSI_ABORT_SNOOZE:
+ if(why == DID_TIME_OUT) {
+ save_flags(flags);
+ cli();
+ SCpnt->internal_timeout &= ~IN_ABORT;
+ if(SCpnt->flags & WAS_TIMEDOUT) {
+ restore_flags(flags);
+ return 1; /* Indicate we cannot handle this.
+ * We drop down into the reset handler
+ * and try again
+ */
+ } else {
+ SCpnt->flags |= WAS_TIMEDOUT;
+ oldto = SCpnt->timeout_per_command;
+ update_timeout(SCpnt, oldto);
+ }
+ restore_flags(flags);
+ }
+ return 0;
+ case SCSI_ABORT_PENDING:
+ if(why != DID_TIME_OUT) {
+ save_flags(flags);
+ cli();
+ update_timeout(SCpnt, oldto);
+ restore_flags(flags);
+ }
+ return 0;
+ case SCSI_ABORT_SUCCESS:
+ /* We should have already aborted this one. No
+ * need to adjust timeout
+ */
+ SCpnt->internal_timeout &= ~IN_ABORT;
+ return 0;
+ case SCSI_ABORT_NOT_RUNNING:
+ SCpnt->internal_timeout &= ~IN_ABORT;
+ update_timeout(SCpnt, 0);
+ return 0;
+ case SCSI_ABORT_ERROR:
+ default:
+ SCpnt->internal_timeout &= ~IN_ABORT;
+ return 1;
+ }
+ }
+ }
+}
+
+
+/* Mark a single SCSI Device as having been reset. */
+
+static inline void scsi_mark_device_reset(Scsi_Device *Device)
+{
+ Device->was_reset = 1;
+ Device->expecting_cc_ua = 1;
+}
+
+
+/* Mark all SCSI Devices on a specific Host as having been reset. */
+
+void scsi_mark_host_reset(struct Scsi_Host *Host)
+{
+ Scsi_Cmnd *SCpnt;
+ for (SCpnt = Host->host_queue; SCpnt; SCpnt = SCpnt->next)
+ scsi_mark_device_reset(SCpnt->device);
+}
+
+
+/* Mark all SCSI Devices on a specific Host Bus as having been reset. */
+
+void scsi_mark_bus_reset(struct Scsi_Host *Host, int channel)
+{
+ Scsi_Cmnd *SCpnt;
+ for (SCpnt = Host->host_queue; SCpnt; SCpnt = SCpnt->next)
+ if (SCpnt->channel == channel)
+ scsi_mark_device_reset(SCpnt->device);
+}
+
+
+int scsi_reset (Scsi_Cmnd * SCpnt, unsigned int reset_flags)
+{
+ int temp;
+ unsigned long flags;
+ Scsi_Cmnd * SCpnt1;
+ struct Scsi_Host * host = SCpnt->host;
+
+ printk("SCSI bus is being reset for host %d channel %d.\n",
+ host->host_no, SCpnt->channel);
+
+#if 0
+ /*
+ * First of all, we need to make a recommendation to the low-level
+ * driver as to whether a BUS_DEVICE_RESET should be performed,
+ * or whether we should do a full BUS_RESET. There is no simple
+ * algorithm here - we basically use a series of heuristics
+ * to determine what we should do.
+ */
+ SCpnt->host->suggest_bus_reset = FALSE;
+
+ /*
+ * First see if all of the active devices on the bus have
+ * been jammed up so that we are attempting resets. If so,
+ * then suggest a bus reset. Forcing a bus reset could
+ * result in some race conditions, but no more than
+ * you would usually get with timeouts. We will cross
+ * that bridge when we come to it.
+ *
+ * This is actually a pretty bad idea, since a sequence of
+ * commands will often timeout together and this will cause a
+ * Bus Device Reset followed immediately by a SCSI Bus Reset.
+ * If all of the active devices really are jammed up, the
+ * Bus Device Reset will quickly timeout and scsi_times_out
+ * will follow up with a SCSI Bus Reset anyway.
+ */
+ SCpnt1 = host->host_queue;
+ while(SCpnt1) {
+ if( SCpnt1->request.rq_status != RQ_INACTIVE
+ && (SCpnt1->flags & (WAS_RESET | IS_RESETTING)) == 0 )
+ break;
+ SCpnt1 = SCpnt1->next;
+ }
+ if( SCpnt1 == NULL ) {
+ reset_flags |= SCSI_RESET_SUGGEST_BUS_RESET;
+ }
+
+ /*
+ * If the code that called us is suggesting a hard reset, then
+ * definitely request it. This usually occurs because a
+ * BUS_DEVICE_RESET times out.
+ *
+ * Passing reset_flags along takes care of this automatically.
+ */
+ if( reset_flags & SCSI_RESET_SUGGEST_BUS_RESET ) {
+ SCpnt->host->suggest_bus_reset = TRUE;
+ }
+#endif
+
+ while (1) {
+ save_flags(flags);
+ cli();
+
+ /*
+ * Protect against races here. If the command is done, or we are
+ * on a different command forget it.
+ */
+ if (reset_flags & SCSI_RESET_ASYNCHRONOUS)
+ if (SCpnt->serial_number != SCpnt->serial_number_at_timeout) {
+ restore_flags(flags);
+ return 0;
+ }
+
+ if (SCpnt->internal_timeout & IN_RESET)
+ {
+ restore_flags(flags);
+ while (SCpnt->internal_timeout & IN_RESET)
+ barrier();
+ }
+ else
+ {
+ SCpnt->internal_timeout |= IN_RESET;
+ update_timeout(SCpnt, RESET_TIMEOUT);
+
+ if (host->host_busy)
+ {
+ restore_flags(flags);
+ SCpnt1 = host->host_queue;
+ while(SCpnt1) {
+ if (SCpnt1->request.rq_status != RQ_INACTIVE) {
+#if 0
+ if (!(SCpnt1->flags & IS_RESETTING) &&
+ !(SCpnt1->internal_timeout & IN_ABORT))
+ scsi_abort(SCpnt1, DID_RESET);
+#endif
+ SCpnt1->flags |= (WAS_RESET | IS_RESETTING);
+ }
+ SCpnt1 = SCpnt1->next;
+ }
+
+ host->last_reset = jiffies;
+ temp = host->hostt->reset(SCpnt, reset_flags);
+ /*
+ This test allows the driver to introduce an additional bus
+ settle time delay by setting last_reset up to 20 seconds in
+ the future. In the normal case where the driver does not
+ modify last_reset, it must be assumed that the actual bus
+ reset occurred immediately prior to the return to this code,
+ and so last_reset must be updated to the current time, so
+ that the delay in internal_cmnd will guarantee at least a
+ MIN_RESET_DELAY bus settle time.
+ */
+ if ((host->last_reset < jiffies) ||
+ (host->last_reset > (jiffies + 20 * HZ)))
+ host->last_reset = jiffies;
+ }
+ else
+ {
+ if (!host->block) host->host_busy++;
+ restore_flags(flags);
+ host->last_reset = jiffies;
+ SCpnt->flags |= (WAS_RESET | IS_RESETTING);
+ temp = host->hostt->reset(SCpnt, reset_flags);
+ if ((host->last_reset < jiffies) ||
+ (host->last_reset > (jiffies + 20 * HZ)))
+ host->last_reset = jiffies;
+ if (!host->block) host->host_busy--;
+ }
+
+#ifdef DEBUG
+ printk("scsi reset function returned %d\n", temp);
+#endif
+
+ /*
+ * Now figure out what we need to do, based upon
+ * what the low level driver said that it did.
+ * If the result is SCSI_RESET_SUCCESS, SCSI_RESET_PENDING,
+ * or SCSI_RESET_WAKEUP, then the low level driver did a
+ * bus device reset or bus reset, so we should go through
+ * and mark one or all of the devices on that bus
+ * as having been reset.
+ */
+ switch(temp & SCSI_RESET_ACTION) {
+ case SCSI_RESET_SUCCESS:
+ if (temp & SCSI_RESET_HOST_RESET)
+ scsi_mark_host_reset(host);
+ else if (temp & SCSI_RESET_BUS_RESET)
+ scsi_mark_bus_reset(host, SCpnt->channel);
+ else scsi_mark_device_reset(SCpnt->device);
+ save_flags(flags);
+ cli();
+ SCpnt->internal_timeout &= ~(IN_RESET|IN_RESET2|IN_RESET3);
+ restore_flags(flags);
+ return 0;
+ case SCSI_RESET_PENDING:
+ if (temp & SCSI_RESET_HOST_RESET)
+ scsi_mark_host_reset(host);
+ else if (temp & SCSI_RESET_BUS_RESET)
+ scsi_mark_bus_reset(host, SCpnt->channel);
+ else scsi_mark_device_reset(SCpnt->device);
+ case SCSI_RESET_NOT_RUNNING:
+ return 0;
+ case SCSI_RESET_PUNT:
+ SCpnt->internal_timeout &= ~(IN_RESET|IN_RESET2|IN_RESET3);
+ scsi_request_sense (SCpnt);
+ return 0;
+ case SCSI_RESET_WAKEUP:
+ if (temp & SCSI_RESET_HOST_RESET)
+ scsi_mark_host_reset(host);
+ else if (temp & SCSI_RESET_BUS_RESET)
+ scsi_mark_bus_reset(host, SCpnt->channel);
+ else scsi_mark_device_reset(SCpnt->device);
+ SCpnt->internal_timeout &= ~(IN_RESET|IN_RESET2|IN_RESET3);
+ scsi_request_sense (SCpnt);
+ /*
+ * If a bus reset was performed, we
+ * need to wake up each and every command
+ * that was active on the bus or if it was a HBA
+ * reset all active commands on all channels
+ */
+ if( temp & SCSI_RESET_HOST_RESET )
+ {
+ SCpnt1 = host->host_queue;
+ while(SCpnt1) {
+ if (SCpnt1->request.rq_status != RQ_INACTIVE
+ && SCpnt1 != SCpnt)
+ scsi_request_sense (SCpnt1);
+ SCpnt1 = SCpnt1->next;
+ }
+ } else if( temp & SCSI_RESET_BUS_RESET ) {
+ SCpnt1 = host->host_queue;
+ while(SCpnt1) {
+ if(SCpnt1->request.rq_status != RQ_INACTIVE
+ && SCpnt1 != SCpnt
+ && SCpnt1->channel == SCpnt->channel)
+ scsi_request_sense (SCpnt);
+ SCpnt1 = SCpnt1->next;
+ }
+ }
+ return 0;
+ case SCSI_RESET_SNOOZE:
+ /* In this case, we set the timeout field to 0
+ * so that this command does not time out any more,
+ * and we return 1 so that we get a message on the
+ * screen.
+ */
+ save_flags(flags);
+ cli();
+ SCpnt->internal_timeout &= ~(IN_RESET|IN_RESET2|IN_RESET3);
+ update_timeout(SCpnt, 0);
+ restore_flags(flags);
+ /* If you snooze, you lose... */
+ case SCSI_RESET_ERROR:
+ default:
+ return 1;
+ }
+
+ return temp;
+ }
+ }
+}
+
+
+static void scsi_main_timeout(void)
+{
+ /*
+ * We must not enter update_timeout with a timeout condition still pending.
+ */
+
+ int timed_out;
+ unsigned long flags;
+ struct Scsi_Host * host;
+ Scsi_Cmnd * SCpnt = NULL;
+
+ save_flags(flags);
+ cli();
+
+ update_timeout(NULL, 0);
+
+ /*
+ * Find all timers such that they have 0 or negative (shouldn't happen)
+ * time remaining on them.
+ */
+ timed_out = 0;
+ for (host = scsi_hostlist; host; host = host->next) {
+ for (SCpnt = host->host_queue; SCpnt; SCpnt = SCpnt->next)
+ if (SCpnt->timeout == -1)
+ {
+ SCpnt->timeout = 0;
+ SCpnt->serial_number_at_timeout = SCpnt->serial_number;
+ ++timed_out;
+ }
+ }
+ if (timed_out > 0) {
+ for (host = scsi_hostlist; host; host = host->next) {
+ for (SCpnt = host->host_queue; SCpnt; SCpnt = SCpnt->next)
+ if (SCpnt->serial_number_at_timeout > 0 &&
+ SCpnt->serial_number_at_timeout == SCpnt->serial_number)
+ {
+ restore_flags(flags);
+ scsi_times_out(SCpnt);
+ SCpnt->serial_number_at_timeout = 0;
+ cli();
+ }
+ }
+ }
+ restore_flags(flags);
+}
+
+/*
+ * The strategy is to cause the timer code to call scsi_times_out()
+ * when the soonest timeout is pending.
+ * The arguments are used when we are queueing a new command, because
+ * we do not want to subtract the time used from this time, but when we
+ * set the timer, we want to take this value into account.
+ */
+
+static int update_timeout(Scsi_Cmnd * SCset, int timeout)
+{
+ unsigned int least, used;
+ unsigned int oldto;
+ unsigned long flags;
+ struct Scsi_Host * host;
+ Scsi_Cmnd * SCpnt = NULL;
+
+ save_flags(flags);
+ cli();
+
+ oldto = 0;
+
+ /*
+ * This routine can be a performance bottleneck under high loads, since
+ * it is called twice per SCSI operation: once when internal_cmnd is
+ * called, and again when scsi_done completes the command. To limit
+ * the load this routine can cause, we shortcut processing if no clock
+ * ticks have occurred since the last time it was called.
+ */
+
+ if (jiffies == time_start && timer_table[SCSI_TIMER].expires > 0) {
+ if(SCset){
+ oldto = SCset->timeout;
+ SCset->timeout = timeout;
+ if (timeout > 0 &&
+ jiffies + timeout < timer_table[SCSI_TIMER].expires)
+ timer_table[SCSI_TIMER].expires = jiffies + timeout;
+ }
+ restore_flags(flags);
+ return oldto;
+ }
+
+ /*
+ * Figure out how much time has passed since the last time the timeouts
+ * were updated
+ */
+ used = (time_start) ? (jiffies - time_start) : 0;
+
+ /*
+ * Find out what is due to timeout soonest, and adjust all timeouts for
+ * the amount of time that has passed since the last time we called
+ * update_timeout.
+ */
+
+ oldto = 0;
+
+ if(SCset){
+ oldto = SCset->timeout - used;
+ SCset->timeout = timeout;
+ }
+
+ least = 0xffffffff;
+
+ for(host = scsi_hostlist; host; host = host->next)
+ for(SCpnt = host->host_queue; SCpnt; SCpnt = SCpnt->next)
+ if (SCpnt->timeout > 0) {
+ if (SCpnt != SCset)
+ SCpnt->timeout -= used;
+ if(SCpnt->timeout <= 0) SCpnt->timeout = -1;
+ if(SCpnt->timeout > 0 && SCpnt->timeout < least)
+ least = SCpnt->timeout;
+ }
+
+ /*
+ * If something is due to timeout again, then we will set the next timeout
+ * interrupt to occur. Otherwise, timeouts are disabled.
+ */
+
+ if (least != 0xffffffff)
+ {
+ time_start = jiffies;
+ timer_table[SCSI_TIMER].expires = (time_elapsed = least) + jiffies;
+ timer_active |= 1 << SCSI_TIMER;
+ }
+ else
+ {
+ timer_table[SCSI_TIMER].expires = time_start = time_elapsed = 0;
+ timer_active &= ~(1 << SCSI_TIMER);
+ }
+ restore_flags(flags);
+ return oldto;
+}
+
+#ifdef CONFIG_MODULES
+static int scsi_register_host(Scsi_Host_Template *);
+static void scsi_unregister_host(Scsi_Host_Template *);
+#endif
+
+void *scsi_malloc(unsigned int len)
+{
+ unsigned int nbits, mask;
+ unsigned long flags;
+ int i, j;
+ if(len % SECTOR_SIZE != 0 || len > PAGE_SIZE)
+ return NULL;
+
+ save_flags(flags);
+ cli();
+ nbits = len >> 9;
+ mask = (1 << nbits) - 1;
+
+ for(i=0;i < dma_sectors / SECTORS_PER_PAGE; i++)
+ for(j=0; j<=SECTORS_PER_PAGE - nbits; j++){
+ if ((dma_malloc_freelist[i] & (mask << j)) == 0){
+ dma_malloc_freelist[i] |= (mask << j);
+ restore_flags(flags);
+ dma_free_sectors -= nbits;
+#ifdef DEBUG
+ printk("SMalloc: %d %p\n",len, dma_malloc_pages[i] + (j << 9));
+#endif
+ return (void *) ((unsigned long) dma_malloc_pages[i] + (j << 9));
+ }
+ }
+ restore_flags(flags);
+ return NULL; /* Nope. No more */
+}
+
+int scsi_free(void *obj, unsigned int len)
+{
+ unsigned int page, sector, nbits, mask;
+ unsigned long flags;
+
+#ifdef DEBUG
+ unsigned long ret = 0;
+
+#ifdef __mips__
+ __asm__ __volatile__ ("move\t%0,$31":"=r"(ret));
+#else
+ ret = __builtin_return_address(0);
+#endif
+ printk("scsi_free %p %d\n",obj, len);
+#endif
+
+ for (page = 0; page < dma_sectors / SECTORS_PER_PAGE; page++) {
+ unsigned long page_addr = (unsigned long) dma_malloc_pages[page];
+ if ((unsigned long) obj >= page_addr &&
+ (unsigned long) obj < page_addr + PAGE_SIZE)
+ {
+ sector = (((unsigned long) obj) - page_addr) >> 9;
+
+ nbits = len >> 9;
+ mask = (1 << nbits) - 1;
+
+ if ((mask << sector) >= (1 << SECTORS_PER_PAGE))
+ panic ("scsi_free:Bad memory alignment");
+
+ save_flags(flags);
+ cli();
+ if((dma_malloc_freelist[page] &
+ (mask << sector)) != (mask<<sector)){
+#ifdef DEBUG
+ printk("scsi_free(obj=%p, len=%d) called from %08lx\n",
+ obj, len, ret);
+#endif
+ panic("scsi_free:Trying to free unused memory");
+ }
+ dma_free_sectors += nbits;
+ dma_malloc_freelist[page] &= ~(mask << sector);
+ restore_flags(flags);
+ return 0;
+ }
+ }
+ panic("scsi_free:Bad offset");
+}
+
+
+int scsi_loadable_module_flag; /* Set after we scan builtin drivers */
+
+void * scsi_init_malloc(unsigned int size, int priority)
+{
+ void * retval;
+
+ /*
+ * For buffers used by the DMA pool, we assume page aligned
+ * structures.
+ */
+ if ((size % PAGE_SIZE) == 0) {
+ int order, a_size;
+ for (order = 0, a_size = PAGE_SIZE;
+ a_size < size; order++, a_size <<= 1)
+ ;
+ retval = (void *) __get_dma_pages(priority & GFP_LEVEL_MASK,
+ order);
+ } else
+ retval = kmalloc(size, priority);
+
+ if (retval)
+ memset(retval, 0, size);
+ return retval;
+}
+
+
+void scsi_init_free(char * ptr, unsigned int size)
+{
+ /*
+ * We need this special code here because the DMA pool assumes
+ * page aligned data. Besides, it is wasteful to allocate
+ * page sized chunks with kmalloc.
+ */
+ if ((size % PAGE_SIZE) == 0) {
+ int order, a_size;
+
+ for (order = 0, a_size = PAGE_SIZE;
+ a_size < size; order++, a_size <<= 1)
+ ;
+ free_pages((unsigned long)ptr, order);
+ } else
+ kfree(ptr);
+}
+
+void scsi_build_commandblocks(Scsi_Device * SDpnt)
+{
+ struct Scsi_Host *host = SDpnt->host;
+ int j;
+ Scsi_Cmnd * SCpnt;
+
+ if (SDpnt->queue_depth == 0)
+ SDpnt->queue_depth = host->cmd_per_lun;
+ SDpnt->device_queue = NULL;
+
+ for(j=0;j<SDpnt->queue_depth;j++){
+ SCpnt = (Scsi_Cmnd *)
+ scsi_init_malloc(sizeof(Scsi_Cmnd),
+ GFP_ATOMIC |
+ (host->unchecked_isa_dma ? GFP_DMA : 0));
+ SCpnt->host = host;
+ SCpnt->device = SDpnt;
+ SCpnt->target = SDpnt->id;
+ SCpnt->lun = SDpnt->lun;
+ SCpnt->channel = SDpnt->channel;
+ SCpnt->request.rq_status = RQ_INACTIVE;
+ SCpnt->use_sg = 0;
+ SCpnt->old_use_sg = 0;
+ SCpnt->old_cmd_len = 0;
+ SCpnt->timeout = 0;
+ SCpnt->underflow = 0;
+ SCpnt->transfersize = 0;
+ SCpnt->serial_number = 0;
+ SCpnt->serial_number_at_timeout = 0;
+ SCpnt->host_scribble = NULL;
+ if(host->host_queue)
+ host->host_queue->prev = SCpnt;
+ SCpnt->next = host->host_queue;
+ SCpnt->prev = NULL;
+ host->host_queue = SCpnt;
+ SCpnt->device_next = SDpnt->device_queue;
+ SDpnt->device_queue = SCpnt;
+ }
+ SDpnt->has_cmdblocks = 1;
+}
+
+/*
+ * scsi_dev_init() is our initialization routine, which in turn calls host
+ * initialization, bus scanning, and sd/st initialization routines.
+ */
+
+int scsi_dev_init(void)
+{
+ Scsi_Device * SDpnt;
+ struct Scsi_Host * shpnt;
+ struct Scsi_Device_Template * sdtpnt;
+#ifdef FOO_ON_YOU
+ return;
+#endif
+
+ /* Yes we're here... */
+#if CONFIG_PROC_FS
+ dispatch_scsi_info_ptr = dispatch_scsi_info;
+#endif
+
+ /* Init a few things so we can "malloc" memory. */
+ scsi_loadable_module_flag = 0;
+
+ timer_table[SCSI_TIMER].fn = scsi_main_timeout;
+ timer_table[SCSI_TIMER].expires = 0;
+
+#ifdef CONFIG_MODULES
+ register_symtab(&scsi_symbol_table);
+#endif
+
+ /* Register the /proc/scsi/scsi entry */
+#if CONFIG_PROC_FS
+ proc_scsi_register(0, &proc_scsi_scsi);
+#endif
+
+ /* initialize all hosts */
+ scsi_init();
+
+ scsi_devices = (Scsi_Device *) NULL;
+
+ for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) {
+ scan_scsis(shpnt,0,0,0,0); /* scan for scsi devices */
+ if (shpnt->select_queue_depths != NULL)
+ (shpnt->select_queue_depths)(shpnt, scsi_devices);
+ }
+
+ printk("scsi : detected ");
+ for (sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+ if (sdtpnt->dev_noticed && sdtpnt->name)
+ printk("%d SCSI %s%s ", sdtpnt->dev_noticed, sdtpnt->name,
+ (sdtpnt->dev_noticed != 1) ? "s" : "");
+ printk("total.\n");
+
+ for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+ if(sdtpnt->init && sdtpnt->dev_noticed) (*sdtpnt->init)();
+
+ for (SDpnt=scsi_devices; SDpnt; SDpnt = SDpnt->next) {
+ SDpnt->scsi_request_fn = NULL;
+ for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+ if(sdtpnt->attach) (*sdtpnt->attach)(SDpnt);
+ if(SDpnt->attached) scsi_build_commandblocks(SDpnt);
+ }
+
+
+ /*
+ * This should build the DMA pool.
+ */
+ resize_dma_pool();
+
+ /*
+ * OK, now we finish the initialization by doing spin-up, read
+ * capacity, etc, etc
+ */
+ for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+ if(sdtpnt->finish && sdtpnt->nr_dev)
+ (*sdtpnt->finish)();
+
+ scsi_loadable_module_flag = 1;
+
+ return 0;
+}
+
+static void print_inquiry(unsigned char *data)
+{
+ int i;
+
+ printk(" Vendor: ");
+ for (i = 8; i < 16; i++)
+ {
+ if (data[i] >= 0x20 && i < data[4] + 5)
+ printk("%c", data[i]);
+ else
+ printk(" ");
+ }
+
+ printk(" Model: ");
+ for (i = 16; i < 32; i++)
+ {
+ if (data[i] >= 0x20 && i < data[4] + 5)
+ printk("%c", data[i]);
+ else
+ printk(" ");
+ }
+
+ printk(" Rev: ");
+ for (i = 32; i < 36; i++)
+ {
+ if (data[i] >= 0x20 && i < data[4] + 5)
+ printk("%c", data[i]);
+ else
+ printk(" ");
+ }
+
+ printk("\n");
+
+ i = data[0] & 0x1f;
+
+ printk(" Type: %s ",
+ i < MAX_SCSI_DEVICE_CODE ? scsi_device_types[i] : "Unknown " );
+ printk(" ANSI SCSI revision: %02x", data[2] & 0x07);
+ if ((data[2] & 0x07) == 1 && (data[3] & 0x0f) == 1)
+ printk(" CCS\n");
+ else
+ printk("\n");
+}
+
+
+#ifdef CONFIG_PROC_FS
+int scsi_proc_info(char *buffer, char **start, off_t offset, int length,
+ int hostno, int inout)
+{
+ Scsi_Cmnd *SCpnt;
+ struct Scsi_Device_Template *SDTpnt;
+ Scsi_Device *scd, *scd_h = NULL;
+ struct Scsi_Host *HBA_ptr;
+ char *p;
+ int host, channel, id, lun;
+ int size, len = 0;
+ off_t begin = 0;
+ off_t pos = 0;
+
+ scd = scsi_devices;
+ HBA_ptr = scsi_hostlist;
+
+ if(inout == 0) {
+ size = sprintf(buffer+len,"Attached devices: %s\n", (scd)?"":"none");
+ len += size;
+ pos = begin + len;
+ while (HBA_ptr) {
+#if 0
+ size += sprintf(buffer+len,"scsi%2d: %s\n", (int) HBA_ptr->host_no,
+ HBA_ptr->hostt->procname);
+ len += size;
+ pos = begin + len;
+#endif
+ scd = scsi_devices;
+ while (scd) {
+ if (scd->host == HBA_ptr) {
+ proc_print_scsidevice(scd, buffer, &size, len);
+ len += size;
+ pos = begin + len;
+
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+ if (pos > offset + length)
+ goto stop_output;
+ }
+ scd = scd->next;
+ }
+ HBA_ptr = HBA_ptr->next;
+ }
+
+ stop_output:
+ *start=buffer+(offset-begin); /* Start of wanted data */
+ len-=(offset-begin); /* Start slop */
+ if(len>length)
+ len = length; /* Ending slop */
+ return (len);
+ }
+
+ if(!buffer || length < 25 || strncmp("scsi", buffer, 4))
+ return(-EINVAL);
+
+ /*
+ * Usage: echo "scsi add-single-device 0 1 2 3" >/proc/scsi/scsi
+ * with "0 1 2 3" replaced by your "Host Channel Id Lun".
+ * Consider this feature BETA.
+ * CAUTION: This is not for hotplugging your peripherals. As
+ * SCSI was not designed for this you could damage your
+ * hardware !
+ * However perhaps it is legal to switch on an
+ * already connected device. It is perhaps not
+ * guaranteed this device doesn't corrupt an ongoing data transfer.
+ */
+ if(!strncmp("add-single-device", buffer + 5, 17)) {
+ p = buffer + 23;
+
+ host = simple_strtoul(p, &p, 0);
+ channel = simple_strtoul(p+1, &p, 0);
+ id = simple_strtoul(p+1, &p, 0);
+ lun = simple_strtoul(p+1, &p, 0);
+
+ printk("scsi singledevice %d %d %d %d\n", host, channel,
+ id, lun);
+
+ while(scd && (scd->host->host_no != host
+ || scd->channel != channel
+ || scd->id != id
+ || scd->lun != lun)) {
+ scd = scd->next;
+ }
+ if(scd)
+ return(-ENOSYS); /* We do not yet support unplugging */
+ while(HBA_ptr && HBA_ptr->host_no != host)
+ HBA_ptr = HBA_ptr->next;
+
+ if(!HBA_ptr)
+ return(-ENXIO);
+
+ scan_scsis (HBA_ptr, 1, channel, id, lun);
+ return(length);
+
+ }
+
+ /*
+ * Usage: echo "scsi remove-single-device 0 1 2 3" >/proc/scsi/scsi
+ * with "0 1 2 3" replaced by your "Host Channel Id Lun".
+ *
+ * Consider this feature pre-BETA.
+ *
+ * CAUTION: This is not for hotplugging your peripherals. As
+ * SCSI was not designed for this you could damage your
+ * hardware and thoroughly confuse the SCSI subsystem.
+ *
+ */
+ else if(!strncmp("remove-single-device", buffer + 5, 20)) {
+ p = buffer + 26;
+
+ host = simple_strtoul(p, &p, 0);
+ channel = simple_strtoul(p+1, &p, 0);
+ id = simple_strtoul(p+1, &p, 0);
+ lun = simple_strtoul(p+1, &p, 0);
+
+ while(scd != NULL) {
+ if(scd->host->host_no == host
+ && scd->channel == channel
+ && scd->id == id
+ && scd->lun == lun){
+ break;
+ }
+ scd_h = scd;
+ scd = scd->next;
+ }
+
+ if(scd == NULL)
+ return(-ENODEV); /* there is no such device attached */
+
+ if(scd->access_count)
+ return(-EBUSY);
+
+ SDTpnt = scsi_devicelist;
+ while(SDTpnt != NULL) {
+ if(SDTpnt->detach) (*SDTpnt->detach)(scd);
+ SDTpnt = SDTpnt->next;
+ }
+
+ if(scd->attached == 0) {
+ /*
+ * Nobody is using this device any more.
+ * Free all of the command structures.
+ */
+ for(SCpnt=scd->host->host_queue; SCpnt; SCpnt = SCpnt->next){
+ if(SCpnt->device == scd) {
+ if(SCpnt->prev != NULL)
+ SCpnt->prev->next = SCpnt->next;
+ if(SCpnt->next != NULL)
+ SCpnt->next->prev = SCpnt->prev;
+ if(SCpnt == scd->host->host_queue)
+ scd->host->host_queue = SCpnt->next;
+ scsi_init_free((char *) SCpnt, sizeof(*SCpnt));
+ }
+ }
+ /* Now we can remove the device structure */
+ if(scd_h != NULL) {
+ scd_h->next = scd->next;
+ } else if (scsi_devices == scd) {
+ /* We had a hit on the first entry of the device list */
+ scsi_devices = scd->next;
+ }
+ scsi_init_free((char *) scd, sizeof(Scsi_Device));
+ } else {
+ return(-EBUSY);
+ }
+ return(0);
+ }
+ return(-EINVAL);
+}
+#endif
+
+/*
+ * Go through the device list and recompute the most appropriate size
+ * for the dma pool. Then grab more memory (as required).
+ */
+static void resize_dma_pool(void)
+{
+ int i;
+ unsigned long size;
+ struct Scsi_Host * shpnt;
+ struct Scsi_Host * host = NULL;
+ Scsi_Device * SDpnt;
+ unsigned long flags;
+ FreeSectorBitmap * new_dma_malloc_freelist = NULL;
+ unsigned int new_dma_sectors = 0;
+ unsigned int new_need_isa_buffer = 0;
+ unsigned char ** new_dma_malloc_pages = NULL;
+
+ if( !scsi_devices )
+ {
+ /*
+ * Free up the DMA pool.
+ */
+ if( dma_free_sectors != dma_sectors )
+ panic("SCSI DMA pool memory leak %d %d\n",dma_free_sectors,dma_sectors);
+
+ for(i=0; i < dma_sectors / SECTORS_PER_PAGE; i++)
+ scsi_init_free(dma_malloc_pages[i], PAGE_SIZE);
+ if (dma_malloc_pages)
+ scsi_init_free((char *) dma_malloc_pages,
+ (dma_sectors / SECTORS_PER_PAGE)*sizeof(*dma_malloc_pages));
+ dma_malloc_pages = NULL;
+ if (dma_malloc_freelist)
+ scsi_init_free((char *) dma_malloc_freelist,
+ (dma_sectors / SECTORS_PER_PAGE)*sizeof(*dma_malloc_freelist));
+ dma_malloc_freelist = NULL;
+ dma_sectors = 0;
+ dma_free_sectors = 0;
+ return;
+ }
+ /* Next, check to see if we need to extend the DMA buffer pool */
+
+ new_dma_sectors = 2*SECTORS_PER_PAGE; /* Base value we use */
+
+ if (high_memory-1 > ISA_DMA_THRESHOLD)
+ scsi_need_isa_bounce_buffers = 1;
+ else
+ scsi_need_isa_bounce_buffers = 0;
+
+ if (scsi_devicelist)
+ for(shpnt=scsi_hostlist; shpnt; shpnt = shpnt->next)
+ new_dma_sectors += SECTORS_PER_PAGE; /* Increment for each host */
+
+ for (SDpnt=scsi_devices; SDpnt; SDpnt = SDpnt->next) {
+ host = SDpnt->host;
+
+ /*
+ * sd and sr drivers allocate scatterlists.
+ * sr drivers may allocate for each command 1x2048 or 2x1024 extra
+ * buffers for 2k sector size and 1k fs.
+ * sg driver allocates buffers < 4k.
+ * st driver does not need buffers from the dma pool.
+ * estimate 4k buffer/command for devices of unknown type (should panic).
+ */
+ if (SDpnt->type == TYPE_WORM || SDpnt->type == TYPE_ROM ||
+ SDpnt->type == TYPE_DISK || SDpnt->type == TYPE_MOD) {
+ new_dma_sectors += ((host->sg_tablesize *
+ sizeof(struct scatterlist) + 511) >> 9) *
+ SDpnt->queue_depth;
+ if (SDpnt->type == TYPE_WORM || SDpnt->type == TYPE_ROM)
+ new_dma_sectors += (2048 >> 9) * SDpnt->queue_depth;
+ }
+ else if (SDpnt->type == TYPE_SCANNER ||
+ SDpnt->type == TYPE_PROCESSOR ||
+ SDpnt->type == TYPE_MEDIUM_CHANGER) {
+ new_dma_sectors += (4096 >> 9) * SDpnt->queue_depth;
+ }
+ else {
+ if (SDpnt->type != TYPE_TAPE) {
+ printk("resize_dma_pool: unknown device type %d\n", SDpnt->type);
+ new_dma_sectors += (4096 >> 9) * SDpnt->queue_depth;
+ }
+ }
+
+ if(host->unchecked_isa_dma &&
+ scsi_need_isa_bounce_buffers &&
+ SDpnt->type != TYPE_TAPE) {
+ new_dma_sectors += (PAGE_SIZE >> 9) * host->sg_tablesize *
+ SDpnt->queue_depth;
+ new_need_isa_buffer++;
+ }
+ }
+
+#ifdef DEBUG_INIT
+ printk("resize_dma_pool: needed dma sectors = %d\n", new_dma_sectors);
+#endif
+
+ /* limit DMA memory to 32MB: */
+ new_dma_sectors = (new_dma_sectors + 15) & 0xfff0;
+
+ /*
+ * We never shrink the buffers - this leads to
+ * race conditions that I would rather not even think
+ * about right now.
+ */
+ if( new_dma_sectors < dma_sectors )
+ new_dma_sectors = dma_sectors;
+
+ if (new_dma_sectors)
+ {
+ size = (new_dma_sectors / SECTORS_PER_PAGE)*sizeof(FreeSectorBitmap);
+ new_dma_malloc_freelist = (FreeSectorBitmap *) scsi_init_malloc(size, GFP_ATOMIC);
+ memset(new_dma_malloc_freelist, 0, size);
+
+ size = (new_dma_sectors / SECTORS_PER_PAGE)*sizeof(*new_dma_malloc_pages);
+ new_dma_malloc_pages = (unsigned char **) scsi_init_malloc(size, GFP_ATOMIC);
+ memset(new_dma_malloc_pages, 0, size);
+ }
+
+ /*
+ * If we need more buffers, expand the list.
+ */
+ if( new_dma_sectors > dma_sectors ) {
+ for(i=dma_sectors / SECTORS_PER_PAGE; i< new_dma_sectors / SECTORS_PER_PAGE; i++)
+ new_dma_malloc_pages[i] = (unsigned char *)
+ scsi_init_malloc(PAGE_SIZE, GFP_ATOMIC | GFP_DMA);
+ }
+
+ /* When we dick with the actual DMA list, we need to
+ * protect things
+ */
+ save_flags(flags);
+ cli();
+ if (dma_malloc_freelist)
+ {
+ size = (dma_sectors / SECTORS_PER_PAGE)*sizeof(FreeSectorBitmap);
+ memcpy(new_dma_malloc_freelist, dma_malloc_freelist, size);
+ scsi_init_free((char *) dma_malloc_freelist, size);
+ }
+ dma_malloc_freelist = new_dma_malloc_freelist;
+
+ if (dma_malloc_pages)
+ {
+ size = (dma_sectors / SECTORS_PER_PAGE)*sizeof(*dma_malloc_pages);
+ memcpy(new_dma_malloc_pages, dma_malloc_pages, size);
+ scsi_init_free((char *) dma_malloc_pages, size);
+ }
+
+ dma_free_sectors += new_dma_sectors - dma_sectors;
+ dma_malloc_pages = new_dma_malloc_pages;
+ dma_sectors = new_dma_sectors;
+ need_isa_buffer = new_need_isa_buffer;
+ restore_flags(flags);
+
+#ifdef DEBUG_INIT
+ printk("resize_dma_pool: dma free sectors = %d\n", dma_free_sectors);
+ printk("resize_dma_pool: dma sectors = %d\n", dma_sectors);
+ printk("resize_dma_pool: need isa buffers = %d\n", need_isa_buffer);
+#endif
+}
+
+#ifdef CONFIG_MODULES /* a big #ifdef block... */
+
+/*
+ * This entry point should be called by a loadable module if it is trying
+ * add a low level scsi driver to the system.
+ */
+static int scsi_register_host(Scsi_Host_Template * tpnt)
+{
+ int pcount;
+ struct Scsi_Host * shpnt;
+ Scsi_Device * SDpnt;
+ struct Scsi_Device_Template * sdtpnt;
+ const char * name;
+
+ if (tpnt->next || !tpnt->detect) return 1;/* Must be already loaded, or
+ * no detect routine available
+ */
+ pcount = next_scsi_host;
+ if ((tpnt->present = tpnt->detect(tpnt)))
+ {
+ if(pcount == next_scsi_host) {
+ if(tpnt->present > 1) {
+ printk("Failure to register low-level scsi driver");
+ scsi_unregister_host(tpnt);
+ return 1;
+ }
+ /* The low-level driver failed to register a driver. We
+ * can do this now.
+ */
+ scsi_register(tpnt,0);
+ }
+ tpnt->next = scsi_hosts; /* Add to the linked list */
+ scsi_hosts = tpnt;
+
+ /* Add the new driver to /proc/scsi */
+#if CONFIG_PROC_FS
+ build_proc_dir_entries(tpnt);
+#endif
+
+ for(shpnt=scsi_hostlist; shpnt; shpnt = shpnt->next)
+ if(shpnt->hostt == tpnt)
+ {
+ if(tpnt->info)
+ name = tpnt->info(shpnt);
+ else
+ name = tpnt->name;
+ printk ("scsi%d : %s\n", /* And print a little message */
+ shpnt->host_no, name);
+ }
+
+ printk ("scsi : %d host%s.\n", next_scsi_host,
+ (next_scsi_host == 1) ? "" : "s");
+
+ scsi_make_blocked_list();
+
+ /* The next step is to call scan_scsis here. This generates the
+ * Scsi_Devices entries
+ */
+
+ for(shpnt=scsi_hostlist; shpnt; shpnt = shpnt->next)
+ if(shpnt->hostt == tpnt) {
+ scan_scsis(shpnt,0,0,0,0);
+ if (shpnt->select_queue_depths != NULL)
+ (shpnt->select_queue_depths)(shpnt, scsi_devices);
+ }
+
+ for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+ if(sdtpnt->init && sdtpnt->dev_noticed) (*sdtpnt->init)();
+
+ /* Next we create the Scsi_Cmnd structures for this host */
+
+ for(SDpnt = scsi_devices; SDpnt; SDpnt = SDpnt->next)
+ if(SDpnt->host->hostt == tpnt)
+ {
+ for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+ if(sdtpnt->attach) (*sdtpnt->attach)(SDpnt);
+ if(SDpnt->attached) scsi_build_commandblocks(SDpnt);
+ }
+
+ /*
+ * Now that we have all of the devices, resize the DMA pool,
+ * as required. */
+ resize_dma_pool();
+
+
+ /* This does any final handling that is required. */
+ for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+ if(sdtpnt->finish && sdtpnt->nr_dev)
+ (*sdtpnt->finish)();
+ }
+
+#if defined(USE_STATIC_SCSI_MEMORY)
+ printk ("SCSI memory: total %ldKb, used %ldKb, free %ldKb.\n",
+ (scsi_memory_upper_value - scsi_memory_lower_value) / 1024,
+ (scsi_init_memory_start - scsi_memory_lower_value) / 1024,
+ (scsi_memory_upper_value - scsi_init_memory_start) / 1024);
+#endif
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+/*
+ * Similarly, this entry point should be called by a loadable module if it
+ * is trying to remove a low level scsi driver from the system.
+ */
+static void scsi_unregister_host(Scsi_Host_Template * tpnt)
+{
+ Scsi_Host_Template * SHT, *SHTp;
+ Scsi_Device *sdpnt, * sdppnt, * sdpnt1;
+ Scsi_Cmnd * SCpnt;
+ unsigned long flags;
+ struct Scsi_Device_Template * sdtpnt;
+ struct Scsi_Host * shpnt, *sh1;
+ int pcount;
+
+ /* First verify that this host adapter is completely free with no pending
+ * commands */
+
+ for(sdpnt = scsi_devices; sdpnt; sdpnt = sdpnt->next)
+ if(sdpnt->host->hostt == tpnt && sdpnt->host->hostt->usage_count
+ && *sdpnt->host->hostt->usage_count) return;
+
+ for(shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next)
+ {
+ if (shpnt->hostt != tpnt) continue;
+ for(SCpnt = shpnt->host_queue; SCpnt; SCpnt = SCpnt->next)
+ {
+ save_flags(flags);
+ cli();
+ if(SCpnt->request.rq_status != RQ_INACTIVE) {
+ restore_flags(flags);
+ for(SCpnt = shpnt->host_queue; SCpnt; SCpnt = SCpnt->next)
+ if(SCpnt->request.rq_status == RQ_SCSI_DISCONNECTING)
+ SCpnt->request.rq_status = RQ_INACTIVE;
+ printk("Device busy???\n");
+ return;
+ }
+ SCpnt->request.rq_status = RQ_SCSI_DISCONNECTING; /* Mark as busy */
+ restore_flags(flags);
+ }
+ }
+ /* Next we detach the high level drivers from the Scsi_Device structures */
+
+ for(sdpnt = scsi_devices; sdpnt; sdpnt = sdpnt->next)
+ if(sdpnt->host->hostt == tpnt)
+ {
+ for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+ if(sdtpnt->detach) (*sdtpnt->detach)(sdpnt);
+ /* If something still attached, punt */
+ if (sdpnt->attached) {
+ printk("Attached usage count = %d\n", sdpnt->attached);
+ return;
+ }
+ }
+
+ /* Next we free up the Scsi_Cmnd structures for this host */
+
+ for(sdpnt = scsi_devices; sdpnt; sdpnt = sdpnt->next)
+ if(sdpnt->host->hostt == tpnt)
+ while (sdpnt->host->host_queue) {
+ SCpnt = sdpnt->host->host_queue->next;
+ scsi_init_free((char *) sdpnt->host->host_queue, sizeof(Scsi_Cmnd));
+ sdpnt->host->host_queue = SCpnt;
+ if (SCpnt) SCpnt->prev = NULL;
+ sdpnt->has_cmdblocks = 0;
+ }
+
+ /* Next free up the Scsi_Device structures for this host */
+
+ sdppnt = NULL;
+ for(sdpnt = scsi_devices; sdpnt; sdpnt = sdpnt1)
+ {
+ sdpnt1 = sdpnt->next;
+ if (sdpnt->host->hostt == tpnt) {
+ if (sdppnt)
+ sdppnt->next = sdpnt->next;
+ else
+ scsi_devices = sdpnt->next;
+ scsi_init_free((char *) sdpnt, sizeof (Scsi_Device));
+ } else
+ sdppnt = sdpnt;
+ }
+
+ /* Next we go through and remove the instances of the individual hosts
+ * that were detected */
+
+ shpnt = scsi_hostlist;
+ while(shpnt) {
+ sh1 = shpnt->next;
+ if(shpnt->hostt == tpnt) {
+ if(shpnt->loaded_as_module) {
+ pcount = next_scsi_host;
+ /* Remove the /proc/scsi directory entry */
+#if CONFIG_PROC_FS
+ proc_scsi_unregister(tpnt->proc_dir,
+ shpnt->host_no + PROC_SCSI_FILE);
+#endif
+ if(tpnt->release)
+ (*tpnt->release)(shpnt);
+ else {
+ /* This is the default case for the release function.
+ * It should do the right thing for most correctly
+ * written host adapters.
+ */
+ if (shpnt->irq) free_irq(shpnt->irq, NULL);
+ if (shpnt->dma_channel != 0xff) free_dma(shpnt->dma_channel);
+ if (shpnt->io_port && shpnt->n_io_port)
+ release_region(shpnt->io_port, shpnt->n_io_port);
+ }
+ if(pcount == next_scsi_host) scsi_unregister(shpnt);
+ tpnt->present--;
+ }
+ }
+ shpnt = sh1;
+ }
+
+ /*
+ * If there are absolutely no more hosts left, it is safe
+ * to completely nuke the DMA pool. The resize operation will
+ * do the right thing and free everything.
+ */
+ if( !scsi_devices )
+ resize_dma_pool();
+
+ printk ("scsi : %d host%s.\n", next_scsi_host,
+ (next_scsi_host == 1) ? "" : "s");
+
+#if defined(USE_STATIC_SCSI_MEMORY)
+ printk ("SCSI memory: total %ldKb, used %ldKb, free %ldKb.\n",
+ (scsi_memory_upper_value - scsi_memory_lower_value) / 1024,
+ (scsi_init_memory_start - scsi_memory_lower_value) / 1024,
+ (scsi_memory_upper_value - scsi_init_memory_start) / 1024);
+#endif
+
+ scsi_make_blocked_list();
+
+ /* There were some hosts that were loaded at boot time, so we cannot
+ do any more than this */
+ if (tpnt->present) return;
+
+ /* OK, this is the very last step. Remove this host adapter from the
+ linked list. */
+ for(SHTp=NULL, SHT=scsi_hosts; SHT; SHTp=SHT, SHT=SHT->next)
+ if(SHT == tpnt) {
+ if(SHTp)
+ SHTp->next = SHT->next;
+ else
+ scsi_hosts = SHT->next;
+ SHT->next = NULL;
+ break;
+ }
+
+ /* Rebuild the /proc/scsi directory entries */
+#if CONFIG_PROC_FS
+ proc_scsi_unregister(tpnt->proc_dir, tpnt->proc_dir->low_ino);
+#endif
+ MOD_DEC_USE_COUNT;
+}
+
+/*
+ * This entry point should be called by a loadable module if it is trying
+ * add a high level scsi driver to the system.
+ */
+static int scsi_register_device_module(struct Scsi_Device_Template * tpnt)
+{
+ Scsi_Device * SDpnt;
+
+ if (tpnt->next) return 1;
+
+ scsi_register_device(tpnt);
+ /*
+ * First scan the devices that we know about, and see if we notice them.
+ */
+
+ for(SDpnt = scsi_devices; SDpnt; SDpnt = SDpnt->next)
+ if(tpnt->detect) SDpnt->attached += (*tpnt->detect)(SDpnt);
+
+ /*
+ * If any of the devices would match this driver, then perform the
+ * init function.
+ */
+ if(tpnt->init && tpnt->dev_noticed)
+ if ((*tpnt->init)()) return 1;
+
+ /*
+ * Now actually connect the devices to the new driver.
+ */
+ for(SDpnt = scsi_devices; SDpnt; SDpnt = SDpnt->next)
+ {
+ if(tpnt->attach) (*tpnt->attach)(SDpnt);
+ /*
+ * If this driver attached to the device, and we no longer
+ * have anything attached, release the scsi command blocks.
+ */
+ if(SDpnt->attached && SDpnt->has_cmdblocks == 0)
+ scsi_build_commandblocks(SDpnt);
+ }
+
+ /*
+ * This does any final handling that is required.
+ */
+ if(tpnt->finish && tpnt->nr_dev) (*tpnt->finish)();
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int scsi_unregister_device(struct Scsi_Device_Template * tpnt)
+{
+ Scsi_Device * SDpnt;
+ Scsi_Cmnd * SCpnt;
+ struct Scsi_Device_Template * spnt;
+ struct Scsi_Device_Template * prev_spnt;
+
+ /*
+ * If we are busy, this is not going to fly.
+ */
+ if( *tpnt->usage_count != 0) return 0;
+ /*
+ * Next, detach the devices from the driver.
+ */
+
+ for(SDpnt = scsi_devices; SDpnt; SDpnt = SDpnt->next)
+ {
+ if(tpnt->detach) (*tpnt->detach)(SDpnt);
+ if(SDpnt->attached == 0)
+ {
+ /*
+ * Nobody is using this device any more. Free all of the
+ * command structures.
+ */
+ for(SCpnt = SDpnt->host->host_queue; SCpnt; SCpnt = SCpnt->next)
+ {
+ if(SCpnt->device == SDpnt)
+ {
+ if(SCpnt->prev != NULL)
+ SCpnt->prev->next = SCpnt->next;
+ if(SCpnt->next != NULL)
+ SCpnt->next->prev = SCpnt->prev;
+ if(SCpnt == SDpnt->host->host_queue)
+ SDpnt->host->host_queue = SCpnt->next;
+ scsi_init_free((char *) SCpnt, sizeof(*SCpnt));
+ }
+ }
+ SDpnt->has_cmdblocks = 0;
+ }
+ }
+ /*
+ * Extract the template from the linked list.
+ */
+ spnt = scsi_devicelist;
+ prev_spnt = NULL;
+ while(spnt != tpnt)
+ {
+ prev_spnt = spnt;
+ spnt = spnt->next;
+ }
+ if(prev_spnt == NULL)
+ scsi_devicelist = tpnt->next;
+ else
+ prev_spnt->next = spnt->next;
+
+ MOD_DEC_USE_COUNT;
+ /*
+ * Final cleanup for the driver is done in the driver sources in the
+ * cleanup function.
+ */
+ return 0;
+}
+
+
+int scsi_register_module(int module_type, void * ptr)
+{
+ switch(module_type){
+ case MODULE_SCSI_HA:
+ return scsi_register_host((Scsi_Host_Template *) ptr);
+
+ /* Load upper level device handler of some kind */
+ case MODULE_SCSI_DEV:
+#ifdef CONFIG_KERNELD
+ if (scsi_hosts == NULL)
+ request_module("scsi_hostadapter");
+#endif
+ return scsi_register_device_module((struct Scsi_Device_Template *) ptr);
+ /* The rest of these are not yet implemented */
+
+ /* Load constants.o */
+ case MODULE_SCSI_CONST:
+
+ /* Load specialized ioctl handler for some device. Intended for
+ * cdroms that have non-SCSI2 audio command sets. */
+ case MODULE_SCSI_IOCTL:
+
+ default:
+ return 1;
+ }
+}
+
+void scsi_unregister_module(int module_type, void * ptr)
+{
+ switch(module_type) {
+ case MODULE_SCSI_HA:
+ scsi_unregister_host((Scsi_Host_Template *) ptr);
+ break;
+ case MODULE_SCSI_DEV:
+ scsi_unregister_device((struct Scsi_Device_Template *) ptr);
+ break;
+ /* The rest of these are not yet implemented. */
+ case MODULE_SCSI_CONST:
+ case MODULE_SCSI_IOCTL:
+ break;
+ default:
+ }
+ return;
+}
+
+#endif /* CONFIG_MODULES */
+
+#ifdef DEBUG_TIMEOUT
+static void
+scsi_dump_status(void)
+{
+ int i;
+ struct Scsi_Host * shpnt;
+ Scsi_Cmnd * SCpnt;
+ printk("Dump of scsi parameters:\n");
+ i = 0;
+ for(shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next)
+ for(SCpnt=shpnt->host_queue; SCpnt; SCpnt = SCpnt->next)
+ {
+ /* (0) 0:0:0:0 (802 123434 8 8 0) (3 3 2) (%d %d %d) %d %x */
+ printk("(%d) %d:%d:%d:%d (%s %ld %ld %ld %d) (%d %d %x) (%d %d %d) %x %x %x\n",
+ i++, SCpnt->host->host_no,
+ SCpnt->channel,
+ SCpnt->target,
+ SCpnt->lun,
+ kdevname(SCpnt->request.rq_dev),
+ SCpnt->request.sector,
+ SCpnt->request.nr_sectors,
+ SCpnt->request.current_nr_sectors,
+ SCpnt->use_sg,
+ SCpnt->retries,
+ SCpnt->allowed,
+ SCpnt->flags,
+ SCpnt->timeout_per_command,
+ SCpnt->timeout,
+ SCpnt->internal_timeout,
+ SCpnt->cmnd[0],
+ SCpnt->sense_buffer[2],
+ SCpnt->result);
+ }
+ printk("wait_for_request = %p\n", wait_for_request);
+ /* Now dump the request lists for each block device */
+ printk("Dump of pending block device requests\n");
+ for(i=0; i<MAX_BLKDEV; i++)
+ if(blk_dev[i].current_request)
+ {
+ struct request * req;
+ printk("%d: ", i);
+ req = blk_dev[i].current_request;
+ while(req) {
+ printk("(%s %d %ld %ld %ld) ",
+ kdevname(req->rq_dev),
+ req->cmd,
+ req->sector,
+ req->nr_sectors,
+ req->current_nr_sectors);
+ req = req->next;
+ }
+ printk("\n");
+ }
+}
+#endif
+
+#ifdef MODULE
+
+int init_module(void) {
+ unsigned long size;
+
+ /*
+ * This makes /proc/scsi visible.
+ */
+#if CONFIG_PROC_FS
+ dispatch_scsi_info_ptr = dispatch_scsi_info;
+#endif
+
+ timer_table[SCSI_TIMER].fn = scsi_main_timeout;
+ timer_table[SCSI_TIMER].expires = 0;
+ register_symtab(&scsi_symbol_table);
+ scsi_loadable_module_flag = 1;
+
+ /* Register the /proc/scsi/scsi entry */
+#if CONFIG_PROC_FS
+ proc_scsi_register(0, &proc_scsi_scsi);
+#endif
+
+
+ dma_sectors = PAGE_SIZE / SECTOR_SIZE;
+ dma_free_sectors= dma_sectors;
+ /*
+ * Set up a minimal DMA buffer list - this will be used during scan_scsis
+ * in some cases.
+ */
+
+ /* One bit per sector to indicate free/busy */
+ size = (dma_sectors / SECTORS_PER_PAGE)*sizeof(FreeSectorBitmap);
+ dma_malloc_freelist = (unsigned char *) scsi_init_malloc(size, GFP_ATOMIC);
+ memset(dma_malloc_freelist, 0, size);
+
+ /* One pointer per page for the page list */
+ dma_malloc_pages = (unsigned char **)
+ scsi_init_malloc((dma_sectors / SECTORS_PER_PAGE)*sizeof(*dma_malloc_pages), GFP_ATOMIC);
+ dma_malloc_pages[0] = (unsigned char *)
+ scsi_init_malloc(PAGE_SIZE, GFP_ATOMIC | GFP_DMA);
+ return 0;
+}
+
+void cleanup_module( void)
+{
+#if CONFIG_PROC_FS
+ proc_scsi_unregister(0, PROC_SCSI_SCSI);
+
+ /* No, we're not here anymore. Don't show the /proc/scsi files. */
+ dispatch_scsi_info_ptr = 0L;
+#endif
+
+ /*
+ * Free up the DMA pool.
+ */
+ resize_dma_pool();
+
+ timer_table[SCSI_TIMER].fn = NULL;
+ timer_table[SCSI_TIMER].expires = 0;
+}
+#endif /* MODULE */
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 4
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */
diff --git a/linux/dev/drivers/scsi/scsi.h b/linux/dev/drivers/scsi/scsi.h
new file mode 100644
index 0000000..13052ba
--- /dev/null
+++ b/linux/dev/drivers/scsi/scsi.h
@@ -0,0 +1,650 @@
+/*
+ * scsi.h Copyright (C) 1992 Drew Eckhardt
+ * Copyright (C) 1993, 1994, 1995 Eric Youngdale
+ * generic SCSI package header file by
+ * Initial versions: Drew Eckhardt
+ * Subsequent revisions: Eric Youngdale
+ *
+ * <drew@colorado.edu>
+ *
+ * Modified by Eric Youngdale eric@aib.com to
+ * add scatter-gather, multiple outstanding request, and other
+ * enhancements.
+ */
+
+#ifndef _SCSI_H
+#define _SCSI_H
+
+/*
+ * Some of the public constants are being moved to this file.
+ * We include it here so that what came from where is transparent.
+ */
+#include <scsi/scsi.h>
+
+#include <linux/random.h>
+
+
+/*
+ * Some defs, in case these are not defined elsewhere.
+ */
+#ifndef TRUE
+# define TRUE 1
+#endif
+#ifndef FALSE
+# define FALSE 0
+#endif
+
+
+extern void scsi_make_blocked_list(void);
+extern volatile int in_scan_scsis;
+extern const unsigned char scsi_command_size[8];
+#define COMMAND_SIZE(opcode) scsi_command_size[((opcode) >> 5) & 7]
+#define IDENTIFY_BASE 0x80
+#define IDENTIFY(can_disconnect, lun) (IDENTIFY_BASE |\
+ ((can_disconnect) ? 0x40 : 0) |\
+ ((lun) & 0x07))
+#define MAX_SCSI_DEVICE_CODE 10
+extern const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE];
+
+
+
+/*
+ * the return of the status word will be in the following format :
+ * The low byte is the status returned by the SCSI command,
+ * with vendor specific bits masked.
+ *
+ * The next byte is the message which followed the SCSI status.
+ * This allows a stos to be used, since the Intel is a little
+ * endian machine.
+ *
+ * The final byte is a host return code, which is one of the following.
+ *
+ * IE
+ * lsb msb
+ * status msg host code
+ *
+ * Our errors returned by OUR driver, NOT SCSI message. Or'd with
+ * SCSI message passed back to driver <IF any>.
+ */
+
+
+#define DID_OK 0x00 /* NO error */
+#define DID_NO_CONNECT 0x01 /* Couldn't connect before timeout period */
+#define DID_BUS_BUSY 0x02 /* BUS stayed busy through time out period */
+#define DID_TIME_OUT 0x03 /* TIMED OUT for other reason */
+#define DID_BAD_TARGET 0x04 /* BAD target. */
+#define DID_ABORT 0x05 /* Told to abort for some other reason */
+#define DID_PARITY 0x06 /* Parity error */
+#define DID_ERROR 0x07 /* Internal error */
+#define DID_RESET 0x08 /* Reset by somebody. */
+#define DID_BAD_INTR 0x09 /* Got an interrupt we weren't expecting. */
+#define DRIVER_OK 0x00 /* Driver status */
+
+/*
+ * These indicate the error that occurred, and what is available.
+ */
+
+#define DRIVER_BUSY 0x01
+#define DRIVER_SOFT 0x02
+#define DRIVER_MEDIA 0x03
+#define DRIVER_ERROR 0x04
+
+#define DRIVER_INVALID 0x05
+#define DRIVER_TIMEOUT 0x06
+#define DRIVER_HARD 0x07
+#define DRIVER_SENSE 0x08
+
+#define SUGGEST_RETRY 0x10
+#define SUGGEST_ABORT 0x20
+#define SUGGEST_REMAP 0x30
+#define SUGGEST_DIE 0x40
+#define SUGGEST_SENSE 0x80
+#define SUGGEST_IS_OK 0xff
+
+#define DRIVER_MASK 0x0f
+#define SUGGEST_MASK 0xf0
+
+#define MAX_COMMAND_SIZE 12
+
+/*
+ * SCSI command sets
+ */
+
+#define SCSI_UNKNOWN 0
+#define SCSI_1 1
+#define SCSI_1_CCS 2
+#define SCSI_2 3
+
+/*
+ * Every SCSI command starts with a one byte OP-code.
+ * The next byte's high three bits are the LUN of the
+ * device. Any multi-byte quantities are stored high byte
+ * first, and may have a 5 bit MSB in the same byte
+ * as the LUN.
+ */
+
+/*
+ * Manufacturers list
+ */
+
+#define SCSI_MAN_UNKNOWN 0
+#define SCSI_MAN_NEC 1
+#define SCSI_MAN_TOSHIBA 2
+#define SCSI_MAN_NEC_OLDCDR 3
+#define SCSI_MAN_SONY 4
+#define SCSI_MAN_PIONEER 5
+
+/*
+ * As the scsi do command functions are intelligent, and may need to
+ * redo a command, we need to keep track of the last command
+ * executed on each one.
+ */
+
+#define WAS_RESET 0x01
+#define WAS_TIMEDOUT 0x02
+#define WAS_SENSE 0x04
+#define IS_RESETTING 0x08
+#define IS_ABORTING 0x10
+#define ASKED_FOR_SENSE 0x20
+
+/*
+ * The scsi_device struct contains what we know about each given scsi
+ * device.
+ */
+
+typedef struct scsi_device {
+ struct scsi_device * next; /* Used for linked list */
+
+ unsigned char id, lun, channel;
+
+ unsigned int manufacturer; /* Manufacturer of device, for using
+ * vendor-specific cmd's */
+ int attached; /* # of high level drivers attached to
+ * this */
+ int access_count; /* Count of open channels/mounts */
+ struct wait_queue * device_wait;/* Used to wait if device is busy */
+ struct Scsi_Host * host;
+ void (*scsi_request_fn)(void); /* Used to jumpstart things after an
+ * ioctl */
+ struct scsi_cmnd *device_queue; /* queue of SCSI Command structures */
+ void *hostdata; /* available to low-level driver */
+ char type;
+ char scsi_level;
+ char vendor[8], model[16], rev[4];
+ unsigned char current_tag; /* current tag */
+ unsigned char sync_min_period; /* Not less than this period */
+ unsigned char sync_max_offset; /* Not greater than this offset */
+ unsigned char queue_depth; /* How deep a queue to use */
+
+ unsigned writeable:1;
+ unsigned removable:1;
+ unsigned random:1;
+ unsigned has_cmdblocks:1;
+ unsigned changed:1; /* Data invalid due to media change */
+ unsigned busy:1; /* Used to prevent races */
+ unsigned lockable:1; /* Able to prevent media removal */
+ unsigned borken:1; /* Tell the Seagate driver to be
+ * painfully slow on this device */
+ unsigned tagged_supported:1; /* Supports SCSI-II tagged queuing */
+ unsigned tagged_queue:1; /* SCSI-II tagged queuing enabled */
+ unsigned disconnect:1; /* can disconnect */
+ unsigned soft_reset:1; /* Uses soft reset option */
+ unsigned sync:1; /* Negotiate for sync transfers */
+ unsigned single_lun:1; /* Indicates we should only allow I/O to
+ * one of the luns for the device at a
+ * time. */
+ unsigned was_reset:1; /* There was a bus reset on the bus for
+ * this device */
+ unsigned expecting_cc_ua:1; /* Expecting a CHECK_CONDITION/UNIT_ATTN
+ * because we did a bus reset. */
+} Scsi_Device;
+
+/*
+ * Use these to separate status msg and our bytes
+ */
+
+#define status_byte(result) (((result) >> 1) & 0x1f)
+#define msg_byte(result) (((result) >> 8) & 0xff)
+#define host_byte(result) (((result) >> 16) & 0xff)
+#define driver_byte(result) (((result) >> 24) & 0xff)
+#define suggestion(result) (driver_byte(result) & SUGGEST_MASK)
+
+#define sense_class(sense) (((sense) >> 4) & 0x7)
+#define sense_error(sense) ((sense) & 0xf)
+#define sense_valid(sense) ((sense) & 0x80);
+
+/*
+ * These are the SCSI devices available on the system.
+ */
+
+extern Scsi_Device * scsi_devices;
+
+extern struct hd_struct * sd;
+
+#if defined(MAJOR_NR) && (MAJOR_NR == SCSI_DISK_MAJOR)
+extern struct hd_struct * sd;
+#endif
+
+/*
+ * Initializes all SCSI devices. This scans all scsi busses.
+ */
+
+extern int scsi_dev_init (void);
+
+struct scatterlist {
+ char * address; /* Location data is to be transferred to */
+ char * alt_address; /* Location of actual if address is a
+ * dma indirect buffer. NULL otherwise */
+ unsigned int length;
+};
+
+#ifdef __alpha__
+# define ISA_DMA_THRESHOLD (~0UL)
+#else
+# define ISA_DMA_THRESHOLD (0x00ffffff)
+#endif
+#define CONTIGUOUS_BUFFERS(X,Y) ((X->b_data+X->b_size) == Y->b_data)
+
+
+/*
+ * These are the return codes for the abort and reset functions. The mid-level
+ * code uses these to decide what to do next. Each of the low level abort
+ * and reset functions must correctly indicate what it has done.
+ * The descriptions are written from the point of view of the mid-level code,
+ * so that the return code is telling the mid-level drivers exactly what
+ * the low level driver has already done, and what remains to be done.
+ */
+
+/* We did not do anything.
+ * Wait some more for this command to complete, and if this does not work,
+ * try something more serious. */
+#define SCSI_ABORT_SNOOZE 0
+
+/* This means that we were able to abort the command. We have already
+ * called the mid-level done function, and do not expect an interrupt that
+ * will lead to another call to the mid-level done function for this command */
+#define SCSI_ABORT_SUCCESS 1
+
+/* We called for an abort of this command, and we should get an interrupt
+ * when this succeeds. Thus we should not restore the timer for this
+ * command in the mid-level abort function. */
+#define SCSI_ABORT_PENDING 2
+
+/* Unable to abort - command is currently on the bus. Grin and bear it. */
+#define SCSI_ABORT_BUSY 3
+
+/* The command is not active in the low level code. Command probably
+ * finished. */
+#define SCSI_ABORT_NOT_RUNNING 4
+
+/* Something went wrong. The low level driver will indicate the correct
+ * error condition when it calls scsi_done, so the mid-level abort function
+ * can simply wait until this comes through */
+#define SCSI_ABORT_ERROR 5
+
+/* We do not know how to reset the bus, or we do not want to. Bummer.
+ * Anyway, just wait a little more for the command in question, and hope that
+ * it eventually finishes. If it never finishes, the SCSI device could
+ * hang, so use this with caution. */
+#define SCSI_RESET_SNOOZE 0
+
+/* We do not know how to reset the bus, or we do not want to. Bummer.
+ * We have given up on this ever completing. The mid-level code will
+ * request sense information to decide how to proceed from here. */
+#define SCSI_RESET_PUNT 1
+
+/* This means that we were able to reset the bus. We have restarted all of
+ * the commands that should be restarted, and we should be able to continue
+ * on normally from here. We do not expect any interrupts that will return
+ * DID_RESET to any of the other commands in the host_queue, and the mid-level
+ * code does not need to do anything special to keep the commands alive.
+ * If a hard reset was performed then all outstanding commands on the
+ * bus have been restarted. */
+#define SCSI_RESET_SUCCESS 2
+
+/* We called for a reset of this bus, and we should get an interrupt
+ * when this succeeds. Each command should get its own status
+ * passed up to scsi_done, but this has not happened yet.
+ * If a hard reset was performed, then we expect an interrupt
+ * for *each* of the outstanding commands that will have the
+ * effect of restarting the commands.
+ */
+#define SCSI_RESET_PENDING 3
+
+/* We did a reset, but do not expect an interrupt to signal DID_RESET.
+ * This tells the upper level code to request the sense info, and this
+ * should keep the command alive. */
+#define SCSI_RESET_WAKEUP 4
+
+/* The command is not active in the low level code. Command probably
+ finished. */
+#define SCSI_RESET_NOT_RUNNING 5
+
+/* Something went wrong, and we do not know how to fix it. */
+#define SCSI_RESET_ERROR 6
+
+#define SCSI_RESET_SYNCHRONOUS 0x01
+#define SCSI_RESET_ASYNCHRONOUS 0x02
+#define SCSI_RESET_SUGGEST_BUS_RESET 0x04
+#define SCSI_RESET_SUGGEST_HOST_RESET 0x08
+/*
+ * This is a bitmask that is ored with one of the above codes.
+ * It tells the mid-level code that we did a hard reset.
+ */
+#define SCSI_RESET_BUS_RESET 0x100
+/*
+ * This is a bitmask that is ored with one of the above codes.
+ * It tells the mid-level code that we did a host adapter reset.
+ */
+#define SCSI_RESET_HOST_RESET 0x200
+/*
+ * Used to mask off bits and to obtain the basic action that was
+ * performed.
+ */
+#define SCSI_RESET_ACTION 0xff
+
+void * scsi_malloc(unsigned int);
+int scsi_free(void *, unsigned int);
+extern unsigned int dma_free_sectors; /* How much room do we have left */
+extern unsigned int need_isa_buffer; /* True if some devices need indirection
+ * buffers */
+
+/*
+ * The Scsi_Cmnd structure is used by scsi.c internally, and for communication
+ * with low level drivers that support multiple outstanding commands.
+ */
+typedef struct scsi_pointer {
+ char * ptr; /* data pointer */
+ int this_residual; /* left in this buffer */
+ struct scatterlist *buffer; /* which buffer */
+ int buffers_residual; /* how many buffers left */
+
+ volatile int Status;
+ volatile int Message;
+ volatile int have_data_in;
+ volatile int sent_command;
+ volatile int phase;
+} Scsi_Pointer;
+
+typedef struct scsi_cmnd {
+ struct Scsi_Host * host;
+ Scsi_Device * device;
+ unsigned char target, lun, channel;
+ unsigned char cmd_len;
+ unsigned char old_cmd_len;
+ struct scsi_cmnd *next, *prev, *device_next, *reset_chain;
+
+ /* These elements define the operation we are about to perform */
+ unsigned char cmnd[12];
+ unsigned request_bufflen; /* Actual request size */
+
+ void * request_buffer; /* Actual requested buffer */
+
+ /* These elements define the operation we ultimately want to perform */
+ unsigned char data_cmnd[12];
+ unsigned short old_use_sg; /* We save use_sg here when requesting
+ * sense info */
+ unsigned short use_sg; /* Number of pieces of scatter-gather */
+ unsigned short sglist_len; /* size of malloc'd scatter-gather list */
+ unsigned short abort_reason;/* If the mid-level code requests an
+ * abort, this is the reason. */
+ unsigned bufflen; /* Size of data buffer */
+ void *buffer; /* Data buffer */
+
+ unsigned underflow; /* Return error if less than this amount is
+ * transfered */
+
+ unsigned transfersize; /* How much we are guaranteed to transfer with
+ * each SCSI transfer (ie, between disconnect /
+ * reconnects. Probably == sector size */
+
+
+ struct request request; /* A copy of the command we are working on */
+
+ unsigned char sense_buffer[16]; /* Sense for this command, if needed */
+
+ /*
+ A SCSI Command is assigned a nonzero serial_number when internal_cmnd
+ passes it to the driver's queue command function. The serial_number
+ is cleared when scsi_done is entered indicating that the command has
+ been completed. If a timeout occurs, the serial number at the moment
+ of timeout is copied into serial_number_at_timeout. By subsequently
+ comparing the serial_number and serial_number_at_timeout fields
+ during abort or reset processing, we can detect whether the command
+ has already completed. This also detects cases where the command has
+ completed and the SCSI Command structure has already being reused
+ for another command, so that we can avoid incorrectly aborting or
+ resetting the new command.
+ */
+
+ unsigned long serial_number;
+ unsigned long serial_number_at_timeout;
+
+ int retries;
+ int allowed;
+ int timeout_per_command, timeout_total, timeout;
+
+ /*
+ * We handle the timeout differently if it happens when a reset,
+ * abort, etc are in process.
+ */
+ unsigned volatile char internal_timeout;
+
+ unsigned flags;
+
+ /* These variables are for the cdrom only. Once we have variable size
+ * buffers in the buffer cache, they will go away. */
+ int this_count;
+ /* End of special cdrom variables */
+
+ /* Low-level done function - can be used by low-level driver to point
+ * to completion function. Not used by mid/upper level code. */
+ void (*scsi_done)(struct scsi_cmnd *);
+ void (*done)(struct scsi_cmnd *); /* Mid-level done function */
+
+ /*
+ * The following fields can be written to by the host specific code.
+ * Everything else should be left alone.
+ */
+
+ Scsi_Pointer SCp; /* Scratchpad used by some host adapters */
+
+ unsigned char * host_scribble; /* The host adapter is allowed to
+ * call scsi_malloc and get some memory
+ * and hang it here. The host adapter
+ * is also expected to call scsi_free
+ * to release this memory. (The memory
+ * obtained by scsi_malloc is guaranteed
+ * to be at an address < 16Mb). */
+
+ int result; /* Status code from lower level driver */
+
+ unsigned char tag; /* SCSI-II queued command tag */
+ unsigned long pid; /* Process ID, starts at 0 */
+} Scsi_Cmnd;
+
+/*
+ * scsi_abort aborts the current command that is executing on host host.
+ * The error code, if non zero is returned in the host byte, otherwise
+ * DID_ABORT is returned in the hostbyte.
+ */
+
+extern int scsi_abort (Scsi_Cmnd *, int code);
+
+extern void scsi_do_cmd (Scsi_Cmnd *, const void *cmnd ,
+ void *buffer, unsigned bufflen,
+ void (*done)(struct scsi_cmnd *),
+ int timeout, int retries);
+
+
+extern Scsi_Cmnd * allocate_device(struct request **, Scsi_Device *, int);
+
+extern Scsi_Cmnd * request_queueable(struct request *, Scsi_Device *);
+extern int scsi_reset (Scsi_Cmnd *, unsigned int);
+
+extern int max_scsi_hosts;
+
+extern void proc_print_scsidevice(Scsi_Device *, char *, int *, int);
+
+extern void print_command(unsigned char *);
+extern void print_sense(const char *, Scsi_Cmnd *);
+extern void print_driverbyte(int scsiresult);
+extern void print_hostbyte(int scsiresult);
+
+extern void scsi_mark_host_reset(struct Scsi_Host *Host);
+extern void scsi_mark_bus_reset(struct Scsi_Host *Host, int channel);
+
+#if defined(MAJOR_NR) && (MAJOR_NR != SCSI_TAPE_MAJOR)
+#include "hosts.h"
+
+static Scsi_Cmnd * end_scsi_request(Scsi_Cmnd * SCpnt, int uptodate, int sectors)
+{
+ struct request * req;
+ struct buffer_head * bh;
+
+ req = &SCpnt->request;
+ req->errors = 0;
+ if (!uptodate) {
+#if defined(MAJOR_NR) && (MAJOR_NR == SCSI_DISK_MAJOR)
+ printk(DEVICE_NAME " I/O error: dev %s, sector %lu, absolute sector %lu\n",
+ kdevname(req->rq_dev), req->sector,
+ req->sector + sd[MINOR(SCpnt->request.rq_dev)].start_sect);
+#else
+ printk(DEVICE_NAME " I/O error: dev %s, sector %lu\n",
+ kdevname(req->rq_dev), req->sector);
+#endif
+ }
+
+ do {
+ if ((bh = req->bh) != NULL) {
+ req->bh = bh->b_reqnext;
+ req->nr_sectors -= bh->b_size >> 9;
+ req->sector += bh->b_size >> 9;
+ bh->b_reqnext = NULL;
+ /*
+ * This is our 'MD IO has finished' event handler.
+ * note that b_state should be cached in a register
+ * anyways, so the overhead if this checking is almost
+ * zero. But anyways .. we never get OO for free :)
+ */
+ if (test_bit(BH_MD, &bh->b_state)) {
+ struct md_personality * pers=(struct md_personality *)bh->personality;
+ pers->end_request(bh,uptodate);
+ }
+ /*
+ * the normal (nonmirrored and no RAID5) case:
+ */
+ else {
+ mark_buffer_uptodate(bh, uptodate);
+ unlock_buffer(bh);
+ }
+ sectors -= bh->b_size >> 9;
+ if ((bh = req->bh) != NULL) {
+ req->current_nr_sectors = bh->b_size >> 9;
+ if (req->nr_sectors < req->current_nr_sectors) {
+ req->nr_sectors = req->current_nr_sectors;
+ printk("end_scsi_request: buffer-list destroyed\n");
+ }
+ }
+ }
+ } while(sectors && bh);
+ if (req->bh){
+ req->buffer = bh->b_data;
+ return SCpnt;
+ }
+ DEVICE_OFF(req->rq_dev);
+ if (req->sem != NULL) {
+ up(req->sem);
+ }
+ add_blkdev_randomness(MAJOR(req->rq_dev));
+
+ if (SCpnt->host->block) {
+ struct Scsi_Host * next;
+
+ for (next = SCpnt->host->block; next != SCpnt->host;
+ next = next->block)
+ wake_up(&next->host_wait);
+ }
+
+ req->rq_status = RQ_INACTIVE;
+ wake_up(&wait_for_request);
+ wake_up(&SCpnt->device->device_wait);
+ return NULL;
+}
+
+
+/* This is just like INIT_REQUEST, but we need to be aware of the fact
+ * that an interrupt may start another request, so we run this with interrupts
+ * turned off
+ */
+#define INIT_SCSI_REQUEST \
+ if (!CURRENT) { \
+ CLEAR_INTR; \
+ restore_flags(flags); \
+ return; \
+ } \
+ if (MAJOR(CURRENT->rq_dev) != MAJOR_NR) \
+ panic(DEVICE_NAME ": request list destroyed");\
+ if (CURRENT->bh) { \
+ if (!buffer_locked(CURRENT->bh)) \
+ panic(DEVICE_NAME ": block not locked"); \
+ }
+#endif
+
+#ifdef MACH
+#define SCSI_SLEEP(QUEUE, CONDITION) { \
+ if (CONDITION) { \
+ struct wait_queue wait = { NULL, NULL}; \
+ add_wait_queue(QUEUE, &wait); \
+ for(;;) { \
+ if (CONDITION) { \
+ if (intr_count) \
+ panic("scsi: trying to call schedule() in interrupt" \
+ ", file %s, line %d.\n", __FILE__, __LINE__); \
+ schedule(); \
+ } \
+ else \
+ break; \
+ } \
+ remove_wait_queue(QUEUE, &wait);\
+ }; }
+#else /* !MACH */
+#define SCSI_SLEEP(QUEUE, CONDITION) { \
+ if (CONDITION) { \
+ struct wait_queue wait = { current, NULL}; \
+ add_wait_queue(QUEUE, &wait); \
+ for(;;) { \
+ current->state = TASK_UNINTERRUPTIBLE; \
+ if (CONDITION) { \
+ if (intr_count) \
+ panic("scsi: trying to call schedule() in interrupt" \
+ ", file %s, line %d.\n", __FILE__, __LINE__); \
+ schedule(); \
+ } \
+ else \
+ break; \
+ } \
+ remove_wait_queue(QUEUE, &wait);\
+ current->state = TASK_RUNNING; \
+ }; }
+#endif /* !MACH */
+#endif
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 4
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */
diff --git a/linux/dev/drivers/scsi/sd.c b/linux/dev/drivers/scsi/sd.c
new file mode 100644
index 0000000..d5f1524
--- /dev/null
+++ b/linux/dev/drivers/scsi/sd.c
@@ -0,0 +1,1691 @@
+/*
+ * sd.c Copyright (C) 1992 Drew Eckhardt
+ * Copyright (C) 1993, 1994, 1995 Eric Youngdale
+ *
+ * Linux scsi disk driver
+ * Initial versions: Drew Eckhardt
+ * Subsequent revisions: Eric Youngdale
+ *
+ * <drew@colorado.edu>
+ *
+ * Modified by Eric Youngdale ericy@cais.com to
+ * add scatter-gather, multiple outstanding request, and other
+ * enhancements.
+ *
+ * Modified by Eric Youngdale eric@aib.com to support loadable
+ * low-level scsi drivers.
+ */
+
+#include <linux/module.h>
+#ifdef MODULE
+/*
+ * This is a variable in scsi.c that is set when we are processing something
+ * after boot time. By definition, this is true when we are a loadable module
+ * ourselves.
+ */
+#define MODULE_FLAG 1
+#else
+#define MODULE_FLAG scsi_loadable_module_flag
+#endif /* MODULE */
+
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+
+#include <asm/system.h>
+
+#define MAJOR_NR SCSI_DISK_MAJOR
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+#include "sd.h"
+#include <scsi/scsi_ioctl.h>
+#include "constants.h"
+
+#include <linux/genhd.h>
+
+/*
+ * static const char RCSid[] = "$Header:";
+ */
+
+#define MAX_RETRIES 5
+
+/*
+ * Time out in seconds for disks and Magneto-opticals (which are slower).
+ */
+
+#define SD_TIMEOUT (20 * HZ)
+#define SD_MOD_TIMEOUT (25 * HZ)
+
+#define CLUSTERABLE_DEVICE(SC) (SC->host->use_clustering && \
+ SC->device->type != TYPE_MOD)
+
+struct hd_struct * sd;
+
+Scsi_Disk * rscsi_disks = NULL;
+static int * sd_sizes;
+static int * sd_blocksizes;
+static int * sd_hardsizes; /* Hardware sector size */
+
+extern int sd_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
+
+static int check_scsidisk_media_change(kdev_t);
+static int fop_revalidate_scsidisk(kdev_t);
+
+static int sd_init_onedisk(int);
+
+static void requeue_sd_request (Scsi_Cmnd * SCpnt);
+
+static int sd_init(void);
+static void sd_finish(void);
+static int sd_attach(Scsi_Device *);
+static int sd_detect(Scsi_Device *);
+static void sd_detach(Scsi_Device *);
+
+struct Scsi_Device_Template sd_template =
+{ NULL, "disk", "sd", NULL, TYPE_DISK,
+ SCSI_DISK_MAJOR, 0, 0, 0, 1,
+ sd_detect, sd_init,
+ sd_finish, sd_attach, sd_detach
+};
+
+static int sd_open(struct inode * inode, struct file * filp)
+{
+ int target;
+ target = DEVICE_NR(inode->i_rdev);
+
+ if(target >= sd_template.dev_max || !rscsi_disks[target].device)
+ return -ENXIO; /* No such device */
+
+ /*
+ * Make sure that only one process can do a check_change_disk at one time.
+ * This is also used to lock out further access when the partition table
+ * is being re-read.
+ */
+
+ while (rscsi_disks[target].device->busy)
+ barrier();
+ if(rscsi_disks[target].device->removable) {
+ check_disk_change(inode->i_rdev);
+
+ /*
+ * If the drive is empty, just let the open fail.
+ */
+ if ( !rscsi_disks[target].ready )
+ return -ENXIO;
+
+ /*
+ * Similarly, if the device has the write protect tab set,
+ * have the open fail if the user expects to be able to write
+ * to the thing.
+ */
+ if ( (rscsi_disks[target].write_prot) && (filp->f_mode & 2) )
+ return -EROFS;
+ }
+
+ /*
+ * See if we are requesting a non-existent partition. Do this
+ * after checking for disk change.
+ */
+ if(sd_sizes[MINOR(inode->i_rdev)] == 0)
+ return -ENXIO;
+
+ if(rscsi_disks[target].device->removable)
+ if(!rscsi_disks[target].device->access_count)
+ sd_ioctl(inode, NULL, SCSI_IOCTL_DOORLOCK, 0);
+
+ rscsi_disks[target].device->access_count++;
+ if (rscsi_disks[target].device->host->hostt->usage_count)
+ (*rscsi_disks[target].device->host->hostt->usage_count)++;
+ if(sd_template.usage_count) (*sd_template.usage_count)++;
+ return 0;
+}
+
+static void sd_release(struct inode * inode, struct file * file)
+{
+ int target;
+ fsync_dev(inode->i_rdev);
+
+ target = DEVICE_NR(inode->i_rdev);
+
+ rscsi_disks[target].device->access_count--;
+ if (rscsi_disks[target].device->host->hostt->usage_count)
+ (*rscsi_disks[target].device->host->hostt->usage_count)--;
+ if(sd_template.usage_count) (*sd_template.usage_count)--;
+
+ if(rscsi_disks[target].device->removable) {
+ if(!rscsi_disks[target].device->access_count)
+ sd_ioctl(inode, NULL, SCSI_IOCTL_DOORUNLOCK, 0);
+ }
+}
+
+static void sd_geninit(struct gendisk *);
+
+static struct file_operations sd_fops = {
+ NULL, /* lseek - default */
+ block_read, /* read - general block-dev read */
+ block_write, /* write - general block-dev write */
+ NULL, /* readdir - bad */
+ NULL, /* select */
+ sd_ioctl, /* ioctl */
+ NULL, /* mmap */
+ sd_open, /* open code */
+ sd_release, /* release */
+ block_fsync, /* fsync */
+ NULL, /* fasync */
+ check_scsidisk_media_change, /* Disk change */
+ fop_revalidate_scsidisk /* revalidate */
+};
+
+static struct gendisk sd_gendisk = {
+ MAJOR_NR, /* Major number */
+ "sd", /* Major name */
+ 4, /* Bits to shift to get real from partition */
+ 1 << 4, /* Number of partitions per real */
+ 0, /* maximum number of real */
+ sd_geninit, /* init function */
+ NULL, /* hd struct */
+ NULL, /* block sizes */
+ 0, /* number */
+ NULL, /* internal */
+ NULL /* next */
+};
+
+static void sd_geninit (struct gendisk *ignored)
+{
+ int i;
+
+ for (i = 0; i < sd_template.dev_max; ++i)
+ if(rscsi_disks[i].device)
+ sd[i << 4].nr_sects = rscsi_disks[i].capacity;
+#if 0
+ /* No longer needed - we keep track of this as we attach/detach */
+ sd_gendisk.nr_real = sd_template.dev_max;
+#endif
+}
+
+/*
+ * rw_intr is the interrupt routine for the device driver.
+ * It will be notified on the end of a SCSI read / write, and
+ * will take one of several actions based on success or failure.
+ */
+
+static void rw_intr (Scsi_Cmnd *SCpnt)
+{
+ int result = SCpnt->result;
+ int this_count = SCpnt->bufflen >> 9;
+ int good_sectors = (result == 0 ? this_count : 0);
+ int block_sectors = 1;
+
+#ifdef DEBUG
+ printk("sd%c : rw_intr(%d, %d)\n", 'a' + MINOR(SCpnt->request.rq_dev),
+ SCpnt->host->host_no, result);
+#endif
+
+ /*
+ Handle MEDIUM ERRORs that indicate partial success. Since this is a
+ relatively rare error condition, no care is taken to avoid unnecessary
+ additional work such as memcpy's that could be avoided.
+ */
+
+ if (driver_byte(result) != 0 && /* An error occurred */
+ SCpnt->sense_buffer[0] == 0xF0 && /* Sense data is valid */
+ SCpnt->sense_buffer[2] == MEDIUM_ERROR)
+ {
+ long error_sector = (SCpnt->sense_buffer[3] << 24) |
+ (SCpnt->sense_buffer[4] << 16) |
+ (SCpnt->sense_buffer[5] << 8) |
+ SCpnt->sense_buffer[6];
+ int sector_size =
+ rscsi_disks[DEVICE_NR(SCpnt->request.rq_dev)].sector_size;
+ if (SCpnt->request.bh != NULL)
+ block_sectors = SCpnt->request.bh->b_size >> 9;
+ if (sector_size == 1024)
+ {
+ error_sector <<= 1;
+ if (block_sectors < 2) block_sectors = 2;
+ }
+ else if (sector_size == 256)
+ error_sector >>= 1;
+ error_sector -= sd[MINOR(SCpnt->request.rq_dev)].start_sect;
+ error_sector &= ~ (block_sectors - 1);
+ good_sectors = error_sector - SCpnt->request.sector;
+ if (good_sectors < 0 || good_sectors >= this_count)
+ good_sectors = 0;
+ }
+
+ /*
+ * Handle RECOVERED ERRORs that indicate success after recovery action
+ * by the target device.
+ */
+
+ if (SCpnt->sense_buffer[0] == 0xF0 && /* Sense data is valid */
+ SCpnt->sense_buffer[2] == RECOVERED_ERROR)
+ {
+ printk("scsidisk recovered I/O error: dev %s, sector %lu, absolute sector %lu\n",
+ kdevname(SCpnt->request.rq_dev), SCpnt->request.sector,
+ SCpnt->request.sector + sd[MINOR(SCpnt->request.rq_dev)].start_sect);
+ good_sectors = this_count;
+ result = 0;
+ }
+
+ /*
+ * First case : we assume that the command succeeded. One of two things
+ * will happen here. Either we will be finished, or there will be more
+ * sectors that we were unable to read last time.
+ */
+
+ if (good_sectors > 0) {
+
+#ifdef DEBUG
+ printk("sd%c : %d sectors remain.\n", 'a' + MINOR(SCpnt->request.rq_dev),
+ SCpnt->request.nr_sectors);
+ printk("use_sg is %d\n ",SCpnt->use_sg);
+#endif
+ if (SCpnt->use_sg) {
+ struct scatterlist * sgpnt;
+ int i;
+ sgpnt = (struct scatterlist *) SCpnt->buffer;
+ for(i=0; i<SCpnt->use_sg; i++) {
+#ifdef DEBUG
+ printk(":%x %x %d\n",sgpnt[i].alt_address, sgpnt[i].address,
+ sgpnt[i].length);
+#endif
+ if (sgpnt[i].alt_address) {
+ if (SCpnt->request.cmd == READ)
+ memcpy(sgpnt[i].alt_address, sgpnt[i].address,
+ sgpnt[i].length);
+ scsi_free(sgpnt[i].address, sgpnt[i].length);
+ }
+ }
+
+ /* Free list of scatter-gather pointers */
+ scsi_free(SCpnt->buffer, SCpnt->sglist_len);
+ } else {
+ if (SCpnt->buffer != SCpnt->request.buffer) {
+#ifdef DEBUG
+ printk("nosg: %x %x %d\n",SCpnt->request.buffer, SCpnt->buffer,
+ SCpnt->bufflen);
+#endif
+ if (SCpnt->request.cmd == READ)
+ memcpy(SCpnt->request.buffer, SCpnt->buffer,
+ SCpnt->bufflen);
+ scsi_free(SCpnt->buffer, SCpnt->bufflen);
+ }
+ }
+ /*
+ * If multiple sectors are requested in one buffer, then
+ * they will have been finished off by the first command.
+ * If not, then we have a multi-buffer command.
+ */
+ if (SCpnt->request.nr_sectors > this_count)
+ {
+ SCpnt->request.errors = 0;
+
+ if (!SCpnt->request.bh)
+ {
+#ifdef DEBUG
+ printk("sd%c : handling page request, no buffer\n",
+ 'a' + MINOR(SCpnt->request.rq_dev));
+#endif
+ /*
+ * The SCpnt->request.nr_sectors field is always done in
+ * 512 byte sectors, even if this really isn't the case.
+ */
+ panic("sd.c: linked page request (%lx %x)",
+ SCpnt->request.sector, this_count);
+ }
+ }
+ SCpnt = end_scsi_request(SCpnt, 1, good_sectors);
+ if (result == 0)
+ {
+ requeue_sd_request(SCpnt);
+ return;
+ }
+ }
+
+ if (good_sectors == 0) {
+
+ /* Free up any indirection buffers we allocated for DMA purposes. */
+ if (SCpnt->use_sg) {
+ struct scatterlist * sgpnt;
+ int i;
+ sgpnt = (struct scatterlist *) SCpnt->buffer;
+ for(i=0; i<SCpnt->use_sg; i++) {
+#ifdef DEBUG
+ printk("err: %x %x %d\n",SCpnt->request.buffer, SCpnt->buffer,
+ SCpnt->bufflen);
+#endif
+ if (sgpnt[i].alt_address) {
+ scsi_free(sgpnt[i].address, sgpnt[i].length);
+ }
+ }
+ scsi_free(SCpnt->buffer, SCpnt->sglist_len); /* Free list of scatter-gather pointers */
+ } else {
+#ifdef DEBUG
+ printk("nosgerr: %x %x %d\n",SCpnt->request.buffer, SCpnt->buffer,
+ SCpnt->bufflen);
+#endif
+ if (SCpnt->buffer != SCpnt->request.buffer)
+ scsi_free(SCpnt->buffer, SCpnt->bufflen);
+ }
+ }
+
+ /*
+ * Now, if we were good little boys and girls, Santa left us a request
+ * sense buffer. We can extract information from this, so we
+ * can choose a block to remap, etc.
+ */
+
+ if (driver_byte(result) != 0) {
+ if (suggestion(result) == SUGGEST_REMAP) {
+#ifdef REMAP
+ /*
+ * Not yet implemented. A read will fail after being remapped,
+ * a write will call the strategy routine again.
+ */
+ if rscsi_disks[DEVICE_NR(SCpnt->request.rq_dev)].remap
+ {
+ result = 0;
+ }
+ else
+#endif
+ }
+
+ if ((SCpnt->sense_buffer[0] & 0x7f) == 0x70) {
+ if ((SCpnt->sense_buffer[2] & 0xf) == UNIT_ATTENTION) {
+ if(rscsi_disks[DEVICE_NR(SCpnt->request.rq_dev)].device->removable) {
+ /* detected disc change. set a bit and quietly refuse
+ * further access.
+ */
+ rscsi_disks[DEVICE_NR(SCpnt->request.rq_dev)].device->changed = 1;
+ SCpnt = end_scsi_request(SCpnt, 0, this_count);
+ requeue_sd_request(SCpnt);
+ return;
+ }
+ else
+ {
+ /*
+ * Must have been a power glitch, or a bus reset.
+ * Could not have been a media change, so we just retry
+ * the request and see what happens.
+ */
+ requeue_sd_request(SCpnt);
+ return;
+ }
+ }
+ }
+
+
+ /* If we had an ILLEGAL REQUEST returned, then we may have
+ * performed an unsupported command. The only thing this should be
+ * would be a ten byte read where only a six byte read was supported.
+ * Also, on a system where READ CAPACITY failed, we have read past
+ * the end of the disk.
+ */
+
+ if (SCpnt->sense_buffer[2] == ILLEGAL_REQUEST) {
+ if (rscsi_disks[DEVICE_NR(SCpnt->request.rq_dev)].ten) {
+ rscsi_disks[DEVICE_NR(SCpnt->request.rq_dev)].ten = 0;
+ requeue_sd_request(SCpnt);
+ result = 0;
+ } else {
+ /* ???? */
+ }
+ }
+
+ if (SCpnt->sense_buffer[2] == MEDIUM_ERROR) {
+ printk("scsi%d: MEDIUM ERROR on channel %d, id %d, lun %d, CDB: ",
+ SCpnt->host->host_no, (int) SCpnt->channel,
+ (int) SCpnt->target, (int) SCpnt->lun);
+ print_command(SCpnt->cmnd);
+ print_sense("sd", SCpnt);
+ SCpnt = end_scsi_request(SCpnt, 0, block_sectors);
+ requeue_sd_request(SCpnt);
+ return;
+ }
+ } /* driver byte != 0 */
+ if (result) {
+ printk("SCSI disk error : host %d channel %d id %d lun %d return code = %x\n",
+ rscsi_disks[DEVICE_NR(SCpnt->request.rq_dev)].device->host->host_no,
+ rscsi_disks[DEVICE_NR(SCpnt->request.rq_dev)].device->channel,
+ rscsi_disks[DEVICE_NR(SCpnt->request.rq_dev)].device->id,
+ rscsi_disks[DEVICE_NR(SCpnt->request.rq_dev)].device->lun, result);
+
+ if (driver_byte(result) & DRIVER_SENSE)
+ print_sense("sd", SCpnt);
+ SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.current_nr_sectors);
+ requeue_sd_request(SCpnt);
+ return;
+ }
+}
+
+/*
+ * requeue_sd_request() is the request handler function for the sd driver.
+ * Its function in life is to take block device requests, and translate
+ * them to SCSI commands.
+ */
+
+static void do_sd_request (void)
+{
+ Scsi_Cmnd * SCpnt = NULL;
+ Scsi_Device * SDev;
+ struct request * req = NULL;
+ unsigned long flags;
+ int flag = 0;
+
+ save_flags(flags);
+ while (1==1){
+ cli();
+ if (CURRENT != NULL && CURRENT->rq_status == RQ_INACTIVE) {
+ restore_flags(flags);
+ return;
+ }
+
+ INIT_SCSI_REQUEST;
+ SDev = rscsi_disks[DEVICE_NR(CURRENT->rq_dev)].device;
+
+ /*
+ * I am not sure where the best place to do this is. We need
+ * to hook in a place where we are likely to come if in user
+ * space.
+ */
+ if( SDev->was_reset )
+ {
+ /*
+ * We need to relock the door, but we might
+ * be in an interrupt handler. Only do this
+ * from user space, since we do not want to
+ * sleep from an interrupt.
+ */
+ if( SDev->removable && !intr_count )
+ {
+ scsi_ioctl(SDev, SCSI_IOCTL_DOORLOCK, 0);
+ /* scsi_ioctl may allow CURRENT to change, so start over. */
+ SDev->was_reset = 0;
+ continue;
+ }
+ SDev->was_reset = 0;
+ }
+
+ /* We have to be careful here. allocate_device will get a free pointer,
+ * but there is no guarantee that it is queueable. In normal usage,
+ * we want to call this, because other types of devices may have the
+ * host all tied up, and we want to make sure that we have at least
+ * one request pending for this type of device. We can also come
+ * through here while servicing an interrupt, because of the need to
+ * start another command. If we call allocate_device more than once,
+ * then the system can wedge if the command is not queueable. The
+ * request_queueable function is safe because it checks to make sure
+ * that the host is able to take another command before it returns
+ * a pointer.
+ */
+
+ if (flag++ == 0)
+ SCpnt = allocate_device(&CURRENT,
+ rscsi_disks[DEVICE_NR(CURRENT->rq_dev)].device, 0);
+ else SCpnt = NULL;
+
+ /*
+ * The following restore_flags leads to latency problems. FIXME.
+ * Using a "sti()" gets rid of the latency problems but causes
+ * race conditions and crashes.
+ */
+ restore_flags(flags);
+
+ /* This is a performance enhancement. We dig down into the request
+ * list and try to find a queueable request (i.e. device not busy,
+ * and host able to accept another command. If we find one, then we
+ * queue it. This can make a big difference on systems with more than
+ * one disk drive. We want to have the interrupts off when monkeying
+ * with the request list, because otherwise the kernel might try to
+ * slip in a request in between somewhere.
+ */
+
+ if (!SCpnt && sd_template.nr_dev > 1){
+ struct request *req1;
+ req1 = NULL;
+ cli();
+ req = CURRENT;
+ while(req){
+ SCpnt = request_queueable(req,
+ rscsi_disks[DEVICE_NR(req->rq_dev)].device);
+ if(SCpnt) break;
+ req1 = req;
+ req = req->next;
+ }
+ if (SCpnt && req->rq_status == RQ_INACTIVE) {
+ if (req == CURRENT)
+ CURRENT = CURRENT->next;
+ else
+ req1->next = req->next;
+ }
+ restore_flags(flags);
+ }
+
+ if (!SCpnt) return; /* Could not find anything to do */
+
+ /* Queue command */
+ requeue_sd_request(SCpnt);
+ } /* While */
+}
+
+static void requeue_sd_request (Scsi_Cmnd * SCpnt)
+{
+ int dev, devm, block, this_count;
+ unsigned char cmd[10];
+ int bounce_size, contiguous;
+ int max_sg;
+ struct buffer_head * bh, *bhp;
+ char * buff, *bounce_buffer;
+
+ repeat:
+
+ if(!SCpnt || SCpnt->request.rq_status == RQ_INACTIVE) {
+ do_sd_request();
+ return;
+ }
+
+ devm = MINOR(SCpnt->request.rq_dev);
+ dev = DEVICE_NR(SCpnt->request.rq_dev);
+
+ block = SCpnt->request.sector;
+ this_count = 0;
+
+#ifdef DEBUG
+ printk("Doing sd request, dev = %d, block = %d\n", devm, block);
+#endif
+
+ if (devm >= (sd_template.dev_max << 4) ||
+ !rscsi_disks[dev].device ||
+ block + SCpnt->request.nr_sectors > sd[devm].nr_sects)
+ {
+ SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.nr_sectors);
+ goto repeat;
+ }
+
+ block += sd[devm].start_sect;
+
+ if (rscsi_disks[dev].device->changed)
+ {
+ /*
+ * quietly refuse to do anything to a changed disc until the changed
+ * bit has been reset
+ */
+ /* printk("SCSI disk has been changed. Prohibiting further I/O.\n"); */
+ SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.nr_sectors);
+ goto repeat;
+ }
+
+#ifdef DEBUG
+ printk("sd%c : real dev = /dev/sd%c, block = %d\n",
+ 'a' + devm, dev, block);
+#endif
+
+ /*
+ * If we have a 1K hardware sectorsize, prevent access to single
+ * 512 byte sectors. In theory we could handle this - in fact
+ * the scsi cdrom driver must be able to handle this because
+ * we typically use 1K blocksizes, and cdroms typically have
+ * 2K hardware sectorsizes. Of course, things are simpler
+ * with the cdrom, since it is read-only. For performance
+ * reasons, the filesystems should be able to handle this
+ * and not force the scsi disk driver to use bounce buffers
+ * for this.
+ */
+ if (rscsi_disks[dev].sector_size == 1024)
+ if((block & 1) || (SCpnt->request.nr_sectors & 1)) {
+ printk("sd.c:Bad block number requested");
+ SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.nr_sectors);
+ goto repeat;
+ }
+
+ switch (SCpnt->request.cmd)
+ {
+ case WRITE :
+ if (!rscsi_disks[dev].device->writeable)
+ {
+ SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.nr_sectors);
+ goto repeat;
+ }
+ cmd[0] = WRITE_6;
+ break;
+ case READ :
+ cmd[0] = READ_6;
+ break;
+ default :
+ panic ("Unknown sd command %d\n", SCpnt->request.cmd);
+ }
+
+ SCpnt->this_count = 0;
+
+ /* If the host adapter can deal with very large scatter-gather
+ * requests, it is a waste of time to cluster
+ */
+ contiguous = (!CLUSTERABLE_DEVICE(SCpnt) ? 0 :1);
+ bounce_buffer = NULL;
+ bounce_size = (SCpnt->request.nr_sectors << 9);
+
+ /* First see if we need a bounce buffer for this request. If we do, make
+ * sure that we can allocate a buffer. Do not waste space by allocating
+ * a bounce buffer if we are straddling the 16Mb line
+ */
+ if (contiguous && SCpnt->request.bh &&
+ ((long) SCpnt->request.bh->b_data)
+ + (SCpnt->request.nr_sectors << 9) - 1 > ISA_DMA_THRESHOLD
+ && SCpnt->host->unchecked_isa_dma) {
+ if(((long) SCpnt->request.bh->b_data) > ISA_DMA_THRESHOLD)
+ bounce_buffer = (char *) scsi_malloc(bounce_size);
+ if(!bounce_buffer) contiguous = 0;
+ }
+
+ if(contiguous && SCpnt->request.bh && SCpnt->request.bh->b_reqnext)
+ for(bh = SCpnt->request.bh, bhp = bh->b_reqnext; bhp; bh = bhp,
+ bhp = bhp->b_reqnext) {
+ if(!CONTIGUOUS_BUFFERS(bh,bhp)) {
+ if(bounce_buffer) scsi_free(bounce_buffer, bounce_size);
+ contiguous = 0;
+ break;
+ }
+ }
+ if (!SCpnt->request.bh || contiguous) {
+
+ /* case of page request (i.e. raw device), or unlinked buffer */
+ this_count = SCpnt->request.nr_sectors;
+ buff = SCpnt->request.buffer;
+ SCpnt->use_sg = 0;
+
+ } else if (SCpnt->host->sg_tablesize == 0 ||
+ (need_isa_buffer && dma_free_sectors <= 10)) {
+
+ /* Case of host adapter that cannot scatter-gather. We also
+ * come here if we are running low on DMA buffer memory. We set
+ * a threshold higher than that we would need for this request so
+ * we leave room for other requests. Even though we would not need
+ * it all, we need to be conservative, because if we run low enough
+ * we have no choice but to panic.
+ */
+ if (SCpnt->host->sg_tablesize != 0 &&
+ need_isa_buffer &&
+ dma_free_sectors <= 10)
+ printk("Warning: SCSI DMA buffer space running low. Using non scatter-gather I/O.\n");
+
+ this_count = SCpnt->request.current_nr_sectors;
+ buff = SCpnt->request.buffer;
+ SCpnt->use_sg = 0;
+
+ } else {
+
+ /* Scatter-gather capable host adapter */
+ struct scatterlist * sgpnt;
+ int count, this_count_max;
+ int counted;
+
+ bh = SCpnt->request.bh;
+ this_count = 0;
+ this_count_max = (rscsi_disks[dev].ten ? 0xffff : 0xff);
+ count = 0;
+ bhp = NULL;
+ while(bh) {
+ if ((this_count + (bh->b_size >> 9)) > this_count_max) break;
+ if(!bhp || !CONTIGUOUS_BUFFERS(bhp,bh) ||
+ !CLUSTERABLE_DEVICE(SCpnt) ||
+ (SCpnt->host->unchecked_isa_dma &&
+ ((unsigned long) bh->b_data-1) == ISA_DMA_THRESHOLD)) {
+ if (count < SCpnt->host->sg_tablesize) count++;
+ else break;
+ }
+ this_count += (bh->b_size >> 9);
+ bhp = bh;
+ bh = bh->b_reqnext;
+ }
+#if 0
+ if(SCpnt->host->unchecked_isa_dma &&
+ ((unsigned int) SCpnt->request.bh->b_data-1) == ISA_DMA_THRESHOLD) count--;
+#endif
+ SCpnt->use_sg = count; /* Number of chains */
+ /* scsi_malloc can only allocate in chunks of 512 bytes */
+ count = (SCpnt->use_sg * sizeof(struct scatterlist) + 511) & ~511;
+
+ SCpnt->sglist_len = count;
+ max_sg = count / sizeof(struct scatterlist);
+ if(SCpnt->host->sg_tablesize < max_sg)
+ max_sg = SCpnt->host->sg_tablesize;
+ sgpnt = (struct scatterlist * ) scsi_malloc(count);
+ if (!sgpnt) {
+ printk("Warning - running *really* short on DMA buffers\n");
+ SCpnt->use_sg = 0; /* No memory left - bail out */
+ this_count = SCpnt->request.current_nr_sectors;
+ buff = SCpnt->request.buffer;
+ } else {
+ memset(sgpnt, 0, count); /* Zero so it is easy to fill, but only
+ * if memory is available
+ */
+ buff = (char *) sgpnt;
+ counted = 0;
+ for(count = 0, bh = SCpnt->request.bh, bhp = bh->b_reqnext;
+ count < SCpnt->use_sg && bh;
+ count++, bh = bhp) {
+
+ bhp = bh->b_reqnext;
+
+ if(!sgpnt[count].address) sgpnt[count].address = bh->b_data;
+ sgpnt[count].length += bh->b_size;
+ counted += bh->b_size >> 9;
+
+ if (((long) sgpnt[count].address) + sgpnt[count].length - 1 >
+ ISA_DMA_THRESHOLD && (SCpnt->host->unchecked_isa_dma) &&
+ !sgpnt[count].alt_address) {
+ sgpnt[count].alt_address = sgpnt[count].address;
+ /* We try to avoid exhausting the DMA pool, since it is
+ * easier to control usage here. In other places we might
+ * have a more pressing need, and we would be screwed if
+ * we ran out */
+ if(dma_free_sectors < (sgpnt[count].length >> 9) + 10) {
+ sgpnt[count].address = NULL;
+ } else {
+ sgpnt[count].address =
+ (char *) scsi_malloc(sgpnt[count].length);
+ }
+ /* If we start running low on DMA buffers, we abort the
+ * scatter-gather operation, and free all of the memory
+ * we have allocated. We want to ensure that all scsi
+ * operations are able to do at least a non-scatter/gather
+ * operation */
+ if(sgpnt[count].address == NULL){ /* Out of dma memory */
+#if 0
+ printk("Warning: Running low on SCSI DMA buffers");
+ /* Try switching back to a non s-g operation. */
+ while(--count >= 0){
+ if(sgpnt[count].alt_address)
+ scsi_free(sgpnt[count].address,
+ sgpnt[count].length);
+ }
+ this_count = SCpnt->request.current_nr_sectors;
+ buff = SCpnt->request.buffer;
+ SCpnt->use_sg = 0;
+ scsi_free(sgpnt, SCpnt->sglist_len);
+#endif
+ SCpnt->use_sg = count;
+ this_count = counted -= bh->b_size >> 9;
+ break;
+ }
+ }
+
+ /* Only cluster buffers if we know that we can supply DMA
+ * buffers large enough to satisfy the request. Do not cluster
+ * a new request if this would mean that we suddenly need to
+ * start using DMA bounce buffers */
+ if(bhp && CONTIGUOUS_BUFFERS(bh,bhp)
+ && CLUSTERABLE_DEVICE(SCpnt)) {
+ char * tmp;
+
+ if (((long) sgpnt[count].address) + sgpnt[count].length +
+ bhp->b_size - 1 > ISA_DMA_THRESHOLD &&
+ (SCpnt->host->unchecked_isa_dma) &&
+ !sgpnt[count].alt_address) continue;
+
+ if(!sgpnt[count].alt_address) {count--; continue; }
+ if(dma_free_sectors > 10)
+ tmp = (char *) scsi_malloc(sgpnt[count].length
+ + bhp->b_size);
+ else {
+ tmp = NULL;
+ max_sg = SCpnt->use_sg;
+ }
+ if(tmp){
+ scsi_free(sgpnt[count].address, sgpnt[count].length);
+ sgpnt[count].address = tmp;
+ count--;
+ continue;
+ }
+
+ /* If we are allowed another sg chain, then increment
+ * counter so we can insert it. Otherwise we will end
+ up truncating */
+
+ if (SCpnt->use_sg < max_sg) SCpnt->use_sg++;
+ } /* contiguous buffers */
+ } /* for loop */
+
+ /* This is actually how many we are going to transfer */
+ this_count = counted;
+
+ if(count < SCpnt->use_sg || SCpnt->use_sg
+ > SCpnt->host->sg_tablesize){
+ bh = SCpnt->request.bh;
+ printk("Use sg, count %d %x %d\n",
+ SCpnt->use_sg, count, dma_free_sectors);
+ printk("maxsg = %x, counted = %d this_count = %d\n",
+ max_sg, counted, this_count);
+ while(bh){
+ printk("[%p %lx] ", bh->b_data, bh->b_size);
+ bh = bh->b_reqnext;
+ }
+ if(SCpnt->use_sg < 16)
+ for(count=0; count<SCpnt->use_sg; count++)
+ printk("{%d:%p %p %d} ", count,
+ sgpnt[count].address,
+ sgpnt[count].alt_address,
+ sgpnt[count].length);
+ panic("Ooops");
+ }
+
+ if (SCpnt->request.cmd == WRITE)
+ for(count=0; count<SCpnt->use_sg; count++)
+ if(sgpnt[count].alt_address)
+ memcpy(sgpnt[count].address, sgpnt[count].alt_address,
+ sgpnt[count].length);
+ } /* Able to malloc sgpnt */
+ } /* Host adapter capable of scatter-gather */
+
+ /* Now handle the possibility of DMA to addresses > 16Mb */
+
+ if(SCpnt->use_sg == 0){
+ if (((long) buff) + (this_count << 9) - 1 > ISA_DMA_THRESHOLD &&
+ (SCpnt->host->unchecked_isa_dma)) {
+ if(bounce_buffer)
+ buff = bounce_buffer;
+ else
+ buff = (char *) scsi_malloc(this_count << 9);
+ if(buff == NULL) { /* Try backing off a bit if we are low on mem*/
+ this_count = SCpnt->request.current_nr_sectors;
+ buff = (char *) scsi_malloc(this_count << 9);
+ if(!buff) panic("Ran out of DMA buffers.");
+ }
+ if (SCpnt->request.cmd == WRITE)
+ memcpy(buff, (char *)SCpnt->request.buffer, this_count << 9);
+ }
+ }
+#ifdef DEBUG
+ printk("sd%c : %s %d/%d 512 byte blocks.\n",
+ 'a' + devm,
+ (SCpnt->request.cmd == WRITE) ? "writing" : "reading",
+ this_count, SCpnt->request.nr_sectors);
+#endif
+
+ cmd[1] = (SCpnt->lun << 5) & 0xe0;
+
+ if (rscsi_disks[dev].sector_size == 1024){
+ if(block & 1) panic("sd.c:Bad block number requested");
+ if(this_count & 1) panic("sd.c:Bad block number requested");
+ block = block >> 1;
+ this_count = this_count >> 1;
+ }
+
+ if (rscsi_disks[dev].sector_size == 256){
+ block = block << 1;
+ this_count = this_count << 1;
+ }
+
+ if (((this_count > 0xff) || (block > 0x1fffff)) && rscsi_disks[dev].ten)
+ {
+ if (this_count > 0xffff)
+ this_count = 0xffff;
+
+ cmd[0] += READ_10 - READ_6 ;
+ cmd[2] = (unsigned char) (block >> 24) & 0xff;
+ cmd[3] = (unsigned char) (block >> 16) & 0xff;
+ cmd[4] = (unsigned char) (block >> 8) & 0xff;
+ cmd[5] = (unsigned char) block & 0xff;
+ cmd[6] = cmd[9] = 0;
+ cmd[7] = (unsigned char) (this_count >> 8) & 0xff;
+ cmd[8] = (unsigned char) this_count & 0xff;
+ }
+ else
+ {
+ if (this_count > 0xff)
+ this_count = 0xff;
+
+ cmd[1] |= (unsigned char) ((block >> 16) & 0x1f);
+ cmd[2] = (unsigned char) ((block >> 8) & 0xff);
+ cmd[3] = (unsigned char) block & 0xff;
+ cmd[4] = (unsigned char) this_count;
+ cmd[5] = 0;
+ }
+
+ /*
+ * We shouldn't disconnect in the middle of a sector, so with a dumb
+ * host adapter, it's safe to assume that we can at least transfer
+ * this many bytes between each connect / disconnect.
+ */
+
+ SCpnt->transfersize = rscsi_disks[dev].sector_size;
+ SCpnt->underflow = this_count << 9;
+ scsi_do_cmd (SCpnt, (void *) cmd, buff,
+ this_count * rscsi_disks[dev].sector_size,
+ rw_intr,
+ (SCpnt->device->type == TYPE_DISK ?
+ SD_TIMEOUT : SD_MOD_TIMEOUT),
+ MAX_RETRIES);
+}
+
+static int check_scsidisk_media_change(kdev_t full_dev){
+ int retval;
+ int target;
+ struct inode inode;
+ int flag = 0;
+
+ target = DEVICE_NR(full_dev);
+
+ if (target >= sd_template.dev_max ||
+ !rscsi_disks[target].device) {
+ printk("SCSI disk request error: invalid device.\n");
+ return 0;
+ }
+
+ if(!rscsi_disks[target].device->removable) return 0;
+
+ inode.i_rdev = full_dev; /* This is all we really need here */
+
+ /* Using Start/Stop enables differentiation between drive with
+ * no cartridge loaded - NOT READY, drive with changed cartridge -
+ * UNIT ATTENTION, or with same cartridge - GOOD STATUS.
+ * This also handles drives that auto spin down. eg iomega jaz 1GB
+ * as this will spin up the drive.
+ */
+ retval = sd_ioctl(&inode, NULL, SCSI_IOCTL_START_UNIT, 0);
+
+ if(retval){ /* Unable to test, unit probably not ready. This usually
+ * means there is no disc in the drive. Mark as changed,
+ * and we will figure it out later once the drive is
+ * available again. */
+
+ rscsi_disks[target].ready = 0;
+ rscsi_disks[target].device->changed = 1;
+ return 1; /* This will force a flush, if called from
+ * check_disk_change */
+ }
+
+ /*
+ * for removable scsi disk ( FLOPTICAL ) we have to recognise the
+ * presence of disk in the drive. This is kept in the Scsi_Disk
+ * struct and tested at open ! Daniel Roche ( dan@lectra.fr )
+ */
+
+ rscsi_disks[target].ready = 1; /* FLOPTICAL */
+
+ retval = rscsi_disks[target].device->changed;
+ if(!flag) rscsi_disks[target].device->changed = 0;
+ return retval;
+}
+
+static void sd_init_done (Scsi_Cmnd * SCpnt)
+{
+ struct request * req;
+
+ req = &SCpnt->request;
+ req->rq_status = RQ_SCSI_DONE; /* Busy, but indicate request done */
+
+ if (req->sem != NULL) {
+ up(req->sem);
+ }
+}
+
+static int sd_init_onedisk(int i)
+{
+ unsigned char cmd[10];
+ unsigned char *buffer;
+ unsigned long spintime;
+ int the_result, retries;
+ Scsi_Cmnd * SCpnt;
+
+ /* We need to retry the READ_CAPACITY because a UNIT_ATTENTION is
+ * considered a fatal error, and many devices report such an error
+ * just after a scsi bus reset.
+ */
+
+ SCpnt = allocate_device(NULL, rscsi_disks[i].device, 1);
+ buffer = (unsigned char *) scsi_malloc(512);
+
+ spintime = 0;
+
+ /* Spin up drives, as required. Only do this at boot time */
+ /* Spinup needs to be done for module loads too. */
+ do{
+ retries = 0;
+ while(retries < 3)
+ {
+ cmd[0] = TEST_UNIT_READY;
+ cmd[1] = (rscsi_disks[i].device->lun << 5) & 0xe0;
+ memset ((void *) &cmd[2], 0, 8);
+ SCpnt->cmd_len = 0;
+ SCpnt->sense_buffer[0] = 0;
+ SCpnt->sense_buffer[2] = 0;
+
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ /* Mark as really busy again */
+ SCpnt->request.rq_status = RQ_SCSI_BUSY;
+ SCpnt->request.sem = &sem;
+ scsi_do_cmd (SCpnt,
+ (void *) cmd, (void *) buffer,
+ 512, sd_init_done, SD_TIMEOUT,
+ MAX_RETRIES);
+ down(&sem);
+ }
+
+ the_result = SCpnt->result;
+ retries++;
+ if( the_result == 0
+ || SCpnt->sense_buffer[2] != UNIT_ATTENTION)
+ break;
+ }
+
+ /* Look for non-removable devices that return NOT_READY.
+ * Issue command to spin up drive for these cases. */
+ if(the_result && !rscsi_disks[i].device->removable &&
+ SCpnt->sense_buffer[2] == NOT_READY) {
+ unsigned long time1;
+ if(!spintime){
+#ifdef MACH
+ printk( "sd%d: Spinning up disk...", i);
+#else
+ printk( "sd%c: Spinning up disk...", 'a' + i );
+#endif
+ cmd[0] = START_STOP;
+ cmd[1] = (rscsi_disks[i].device->lun << 5) & 0xe0;
+ cmd[1] |= 1; /* Return immediately */
+ memset ((void *) &cmd[2], 0, 8);
+ cmd[4] = 1; /* Start spin cycle */
+ SCpnt->cmd_len = 0;
+ SCpnt->sense_buffer[0] = 0;
+ SCpnt->sense_buffer[2] = 0;
+
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ /* Mark as really busy again */
+ SCpnt->request.rq_status = RQ_SCSI_BUSY;
+ SCpnt->request.sem = &sem;
+ scsi_do_cmd (SCpnt,
+ (void *) cmd, (void *) buffer,
+ 512, sd_init_done, SD_TIMEOUT,
+ MAX_RETRIES);
+ down(&sem);
+ }
+
+ spintime = jiffies;
+ }
+
+ time1 = jiffies + HZ;
+ while(jiffies < time1); /* Wait 1 second for next try */
+ printk( "." );
+ }
+ } while(the_result && spintime && spintime+100*HZ > jiffies);
+ if (spintime) {
+ if (the_result)
+ printk( "not responding...\n" );
+ else
+ printk( "ready\n" );
+ }
+
+ retries = 3;
+ do {
+ cmd[0] = READ_CAPACITY;
+ cmd[1] = (rscsi_disks[i].device->lun << 5) & 0xe0;
+ memset ((void *) &cmd[2], 0, 8);
+ memset ((void *) buffer, 0, 8);
+ SCpnt->cmd_len = 0;
+ SCpnt->sense_buffer[0] = 0;
+ SCpnt->sense_buffer[2] = 0;
+
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ /* Mark as really busy again */
+ SCpnt->request.rq_status = RQ_SCSI_BUSY;
+ SCpnt->request.sem = &sem;
+ scsi_do_cmd (SCpnt,
+ (void *) cmd, (void *) buffer,
+ 8, sd_init_done, SD_TIMEOUT,
+ MAX_RETRIES);
+ down(&sem); /* sleep until it is ready */
+ }
+
+ the_result = SCpnt->result;
+ retries--;
+
+ } while(the_result && retries);
+
+ SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */
+
+ wake_up(&SCpnt->device->device_wait);
+
+ /* Wake up a process waiting for device */
+
+ /*
+ * The SCSI standard says:
+ * "READ CAPACITY is necessary for self configuring software"
+ * While not mandatory, support of READ CAPACITY is strongly encouraged.
+ * We used to die if we couldn't successfully do a READ CAPACITY.
+ * But, now we go on about our way. The side effects of this are
+ *
+ * 1. We can't know block size with certainty. I have said "512 bytes
+ * is it" as this is most common.
+ *
+ * 2. Recovery from when some one attempts to read past the end of the
+ * raw device will be slower.
+ */
+
+ if (the_result)
+ {
+#ifdef MACH
+ printk ("sd%d : READ CAPACITY failed.\n"
+ "sd%d : status = %x, message = %02x, host = %d, driver = %02x \n",
+ i, i,
+#else
+ printk ("sd%c : READ CAPACITY failed.\n"
+ "sd%c : status = %x, message = %02x, host = %d, driver = %02x \n",
+ 'a' + i, 'a' + i,
+#endif
+ status_byte(the_result),
+ msg_byte(the_result),
+ host_byte(the_result),
+ driver_byte(the_result)
+ );
+ if (driver_byte(the_result) & DRIVER_SENSE)
+#ifdef MACH
+ printk("sd%d : extended sense code = %1x \n",
+ i, SCpnt->sense_buffer[2] & 0xf);
+#else
+ printk("sd%c : extended sense code = %1x \n",
+ 'a' + i, SCpnt->sense_buffer[2] & 0xf);
+#endif
+ else
+#ifdef MACH
+ printk("sd%d : sense not available. \n", i);
+#else
+ printk("sd%c : sense not available. \n", 'a' + i);
+#endif
+
+#ifdef MACH
+ printk("sd%d : block size assumed to be 512 bytes, disk size 1GB. \n",
+ i);
+#else
+ printk("sd%c : block size assumed to be 512 bytes, disk size 1GB. \n",
+ 'a' + i);
+#endif
+ rscsi_disks[i].capacity = 0x1fffff;
+ rscsi_disks[i].sector_size = 512;
+
+ /* Set dirty bit for removable devices if not ready - sometimes drives
+ * will not report this properly. */
+ if(rscsi_disks[i].device->removable &&
+ SCpnt->sense_buffer[2] == NOT_READY)
+ rscsi_disks[i].device->changed = 1;
+
+ }
+ else
+ {
+ /*
+ * FLOPTICAL , if read_capa is ok , drive is assumed to be ready
+ */
+ rscsi_disks[i].ready = 1;
+
+ rscsi_disks[i].capacity = 1 + ((buffer[0] << 24) |
+ (buffer[1] << 16) |
+ (buffer[2] << 8) |
+ buffer[3]);
+
+ rscsi_disks[i].sector_size = (buffer[4] << 24) |
+ (buffer[5] << 16) | (buffer[6] << 8) | buffer[7];
+
+ if (rscsi_disks[i].sector_size == 0) {
+ rscsi_disks[i].sector_size = 512;
+#ifdef MACH
+ printk("sd%d : sector size 0 reported, assuming 512.\n", i);
+#else
+ printk("sd%c : sector size 0 reported, assuming 512.\n", 'a' + i);
+#endif
+ }
+
+
+ if (rscsi_disks[i].sector_size != 512 &&
+ rscsi_disks[i].sector_size != 1024 &&
+ rscsi_disks[i].sector_size != 256)
+ {
+#ifdef MACH
+ printk ("sd%d : unsupported sector size %d.\n",
+ i, rscsi_disks[i].sector_size);
+#else
+ printk ("sd%c : unsupported sector size %d.\n",
+ 'a' + i, rscsi_disks[i].sector_size);
+#endif
+ if(rscsi_disks[i].device->removable){
+ rscsi_disks[i].capacity = 0;
+ } else {
+ printk ("scsi : deleting disk entry.\n");
+ rscsi_disks[i].device = NULL;
+ sd_template.nr_dev--;
+ sd_gendisk.nr_real--;
+ return i;
+ }
+ }
+ {
+ /*
+ * The msdos fs needs to know the hardware sector size
+ * So I have created this table. See ll_rw_blk.c
+ * Jacques Gelinas (Jacques@solucorp.qc.ca)
+ */
+ int m, mb;
+ int sz_quot, sz_rem;
+ int hard_sector = rscsi_disks[i].sector_size;
+ /* There are 16 minors allocated for each major device */
+ for (m=i<<4; m<((i+1)<<4); m++){
+ sd_hardsizes[m] = hard_sector;
+ }
+ mb = rscsi_disks[i].capacity / 1024 * hard_sector / 1024;
+ /* sz = div(m/100, 10); this seems to not be in the libr */
+ m = (mb + 50) / 100;
+ sz_quot = m / 10;
+ sz_rem = m - (10 * sz_quot);
+#ifdef MACH
+ printk ("SCSI device sd%d: hdwr sector= %d bytes."
+ " Sectors= %d [%d MB] [%d.%1d GB]\n",
+ i, hard_sector, rscsi_disks[i].capacity,
+ mb, sz_quot, sz_rem);
+#else
+ printk ("SCSI device sd%c: hdwr sector= %d bytes."
+ " Sectors= %d [%d MB] [%d.%1d GB]\n",
+ i+'a', hard_sector, rscsi_disks[i].capacity,
+ mb, sz_quot, sz_rem);
+#endif
+ }
+ if(rscsi_disks[i].sector_size == 1024)
+ rscsi_disks[i].capacity <<= 1; /* Change into 512 byte sectors */
+ if(rscsi_disks[i].sector_size == 256)
+ rscsi_disks[i].capacity >>= 1; /* Change into 512 byte sectors */
+ }
+
+
+ /*
+ * Unless otherwise specified, this is not write protected.
+ */
+ rscsi_disks[i].write_prot = 0;
+ if ( rscsi_disks[i].device->removable && rscsi_disks[i].ready ) {
+ /* FLOPTICAL */
+
+ /*
+ * for removable scsi disk ( FLOPTICAL ) we have to recognise
+ * the Write Protect Flag. This flag is kept in the Scsi_Disk struct
+ * and tested at open !
+ * Daniel Roche ( dan@lectra.fr )
+ */
+
+ memset ((void *) &cmd[0], 0, 8);
+ cmd[0] = MODE_SENSE;
+ cmd[1] = (rscsi_disks[i].device->lun << 5) & 0xe0;
+ cmd[2] = 1; /* page code 1 ?? */
+ cmd[4] = 12;
+ SCpnt->cmd_len = 0;
+ SCpnt->sense_buffer[0] = 0;
+ SCpnt->sense_buffer[2] = 0;
+
+ /* same code as READCAPA !! */
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ SCpnt->request.rq_status = RQ_SCSI_BUSY; /* Mark as really busy again */
+ SCpnt->request.sem = &sem;
+ scsi_do_cmd (SCpnt,
+ (void *) cmd, (void *) buffer,
+ 512, sd_init_done, SD_TIMEOUT,
+ MAX_RETRIES);
+ down(&sem);
+ }
+
+ the_result = SCpnt->result;
+ SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */
+ wake_up(&SCpnt->device->device_wait);
+
+ if ( the_result ) {
+#ifdef MACH
+ printk ("sd%d: test WP failed, assume Write Protected\n",i);
+#else
+ printk ("sd%c: test WP failed, assume Write Protected\n",i+'a');
+#endif
+ rscsi_disks[i].write_prot = 1;
+ } else {
+ rscsi_disks[i].write_prot = ((buffer[2] & 0x80) != 0);
+#ifdef MACH
+ printk ("sd%d: Write Protect is %s\n",i,
+ rscsi_disks[i].write_prot ? "on" : "off");
+#else
+ printk ("sd%c: Write Protect is %s\n",i+'a',
+ rscsi_disks[i].write_prot ? "on" : "off");
+#endif
+ }
+
+ } /* check for write protect */
+
+ rscsi_disks[i].ten = 1;
+ rscsi_disks[i].remap = 1;
+ scsi_free(buffer, 512);
+ return i;
+}
+
+/*
+ * The sd_init() function looks at all SCSI drives present, determines
+ * their size, and reads partition table entries for them.
+ */
+
+static int sd_registered = 0;
+
+static int sd_init()
+{
+ int i;
+
+ if (sd_template.dev_noticed == 0) return 0;
+
+ if(!sd_registered) {
+ if (register_blkdev(MAJOR_NR,"sd",&sd_fops)) {
+ printk("Unable to get major %d for SCSI disk\n",MAJOR_NR);
+ return 1;
+ }
+ sd_registered++;
+ }
+
+ /* We do not support attaching loadable devices yet. */
+ if(rscsi_disks) return 0;
+
+ sd_template.dev_max = sd_template.dev_noticed + SD_EXTRA_DEVS;
+
+ rscsi_disks = (Scsi_Disk *)
+ scsi_init_malloc(sd_template.dev_max * sizeof(Scsi_Disk), GFP_ATOMIC);
+ memset(rscsi_disks, 0, sd_template.dev_max * sizeof(Scsi_Disk));
+
+ sd_sizes = (int *) scsi_init_malloc((sd_template.dev_max << 4) *
+ sizeof(int), GFP_ATOMIC);
+ memset(sd_sizes, 0, (sd_template.dev_max << 4) * sizeof(int));
+
+ sd_blocksizes = (int *) scsi_init_malloc((sd_template.dev_max << 4) *
+ sizeof(int), GFP_ATOMIC);
+
+ sd_hardsizes = (int *) scsi_init_malloc((sd_template.dev_max << 4) *
+ sizeof(int), GFP_ATOMIC);
+
+ for(i=0;i<(sd_template.dev_max << 4);i++){
+ sd_blocksizes[i] = 1024;
+ sd_hardsizes[i] = 512;
+ }
+ blksize_size[MAJOR_NR] = sd_blocksizes;
+ hardsect_size[MAJOR_NR] = sd_hardsizes;
+ sd = (struct hd_struct *) scsi_init_malloc((sd_template.dev_max << 4) *
+ sizeof(struct hd_struct),
+ GFP_ATOMIC);
+
+
+ sd_gendisk.max_nr = sd_template.dev_max;
+ sd_gendisk.part = sd;
+ sd_gendisk.sizes = sd_sizes;
+ sd_gendisk.real_devices = (void *) rscsi_disks;
+ return 0;
+}
+
+static void sd_finish(void)
+{
+ struct gendisk *gendisk;
+ int i;
+
+ blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+
+ for (gendisk = gendisk_head; gendisk != NULL; gendisk = gendisk->next)
+ if (gendisk == &sd_gendisk)
+ break;
+ if (gendisk == NULL)
+ {
+ sd_gendisk.next = gendisk_head;
+ gendisk_head = &sd_gendisk;
+ }
+
+ for (i = 0; i < sd_template.dev_max; ++i)
+ if (!rscsi_disks[i].capacity &&
+ rscsi_disks[i].device)
+ {
+ if (MODULE_FLAG
+ && !rscsi_disks[i].has_part_table) {
+ sd_sizes[i << 4] = rscsi_disks[i].capacity;
+ /* revalidate does sd_init_onedisk via MAYBE_REINIT*/
+ revalidate_scsidisk(MKDEV(MAJOR_NR, i << 4), 0);
+ }
+ else
+ i=sd_init_onedisk(i);
+ rscsi_disks[i].has_part_table = 1;
+ }
+
+ /* If our host adapter is capable of scatter-gather, then we increase
+ * the read-ahead to 16 blocks (32 sectors). If not, we use
+ * a two block (4 sector) read ahead.
+ */
+ if(rscsi_disks[0].device && rscsi_disks[0].device->host->sg_tablesize)
+ read_ahead[MAJOR_NR] = 120; /* 120 sector read-ahead */
+ else
+ read_ahead[MAJOR_NR] = 4; /* 4 sector read-ahead */
+
+ return;
+}
+
+static int sd_detect(Scsi_Device * SDp){
+ if(SDp->type != TYPE_DISK && SDp->type != TYPE_MOD) return 0;
+
+#ifdef MACH
+ printk("Detected scsi %sdisk sd%d at scsi%d, channel %d, id %d, lun %d\n",
+ SDp->removable ? "removable " : "",
+ sd_template.dev_noticed++,
+ SDp->host->host_no, SDp->channel, SDp->id, SDp->lun);
+#else
+ printk("Detected scsi %sdisk sd%c at scsi%d, channel %d, id %d, lun %d\n",
+ SDp->removable ? "removable " : "",
+ 'a'+ (sd_template.dev_noticed++),
+ SDp->host->host_no, SDp->channel, SDp->id, SDp->lun);
+#endif
+
+ return 1;
+}
+
+static int sd_attach(Scsi_Device * SDp){
+ Scsi_Disk * dpnt;
+ int i;
+
+ if(SDp->type != TYPE_DISK && SDp->type != TYPE_MOD) return 0;
+
+ if(sd_template.nr_dev >= sd_template.dev_max) {
+ SDp->attached--;
+ return 1;
+ }
+
+ for(dpnt = rscsi_disks, i=0; i<sd_template.dev_max; i++, dpnt++)
+ if(!dpnt->device) break;
+
+ if(i >= sd_template.dev_max) panic ("scsi_devices corrupt (sd)");
+
+ SDp->scsi_request_fn = do_sd_request;
+ rscsi_disks[i].device = SDp;
+ rscsi_disks[i].has_part_table = 0;
+ sd_template.nr_dev++;
+ sd_gendisk.nr_real++;
+ return 0;
+}
+
+#define DEVICE_BUSY rscsi_disks[target].device->busy
+#define USAGE rscsi_disks[target].device->access_count
+#define CAPACITY rscsi_disks[target].capacity
+#define MAYBE_REINIT sd_init_onedisk(target)
+#define GENDISK_STRUCT sd_gendisk
+
+/* This routine is called to flush all partitions and partition tables
+ * for a changed scsi disk, and then re-read the new partition table.
+ * If we are revalidating a disk because of a media change, then we
+ * enter with usage == 0. If we are using an ioctl, we automatically have
+ * usage == 1 (we need an open channel to use an ioctl :-), so this
+ * is our limit.
+ */
+int revalidate_scsidisk(kdev_t dev, int maxusage){
+ int target;
+ struct gendisk * gdev;
+ unsigned long flags;
+ int max_p;
+ int start;
+ int i;
+
+ target = DEVICE_NR(dev);
+ gdev = &GENDISK_STRUCT;
+
+ save_flags(flags);
+ cli();
+ if (DEVICE_BUSY || USAGE > maxusage) {
+ restore_flags(flags);
+ printk("Device busy for revalidation (usage=%d)\n", USAGE);
+ return -EBUSY;
+ }
+ DEVICE_BUSY = 1;
+ restore_flags(flags);
+
+ max_p = gdev->max_p;
+ start = target << gdev->minor_shift;
+
+ for (i=max_p - 1; i >=0 ; i--) {
+ int minor = start+i;
+ kdev_t devi = MKDEV(MAJOR_NR, minor);
+ sync_dev(devi);
+ invalidate_inodes(devi);
+ invalidate_buffers(devi);
+ gdev->part[minor].start_sect = 0;
+ gdev->part[minor].nr_sects = 0;
+ /*
+ * Reset the blocksize for everything so that we can read
+ * the partition table.
+ */
+ blksize_size[MAJOR_NR][minor] = 1024;
+ }
+
+#ifdef MAYBE_REINIT
+ MAYBE_REINIT;
+#endif
+
+ gdev->part[start].nr_sects = CAPACITY;
+ resetup_one_dev(gdev, target);
+
+ DEVICE_BUSY = 0;
+ return 0;
+}
+
+static int fop_revalidate_scsidisk(kdev_t dev){
+ return revalidate_scsidisk(dev, 0);
+}
+
+
+static void sd_detach(Scsi_Device * SDp)
+{
+ Scsi_Disk * dpnt;
+ int i;
+ int max_p;
+ int start;
+
+ for(dpnt = rscsi_disks, i=0; i<sd_template.dev_max; i++, dpnt++)
+ if(dpnt->device == SDp) {
+
+ /* If we are disconnecting a disk driver, sync and invalidate
+ * everything */
+ max_p = sd_gendisk.max_p;
+ start = i << sd_gendisk.minor_shift;
+
+ for (i=max_p - 1; i >=0 ; i--) {
+ int minor = start+i;
+ kdev_t devi = MKDEV(MAJOR_NR, minor);
+ sync_dev(devi);
+ invalidate_inodes(devi);
+ invalidate_buffers(devi);
+ sd_gendisk.part[minor].start_sect = 0;
+ sd_gendisk.part[minor].nr_sects = 0;
+ sd_sizes[minor] = 0;
+ }
+
+ dpnt->has_part_table = 0;
+ dpnt->device = NULL;
+ dpnt->capacity = 0;
+ SDp->attached--;
+ sd_template.dev_noticed--;
+ sd_template.nr_dev--;
+ sd_gendisk.nr_real--;
+ return;
+ }
+ return;
+}
+
+#ifdef MODULE
+
+int init_module(void) {
+ sd_template.usage_count = &mod_use_count_;
+ return scsi_register_module(MODULE_SCSI_DEV, &sd_template);
+}
+
+void cleanup_module( void)
+{
+ struct gendisk * prev_sdgd;
+ struct gendisk * sdgd;
+
+ scsi_unregister_module(MODULE_SCSI_DEV, &sd_template);
+ unregister_blkdev(SCSI_DISK_MAJOR, "sd");
+ sd_registered--;
+ if( rscsi_disks != NULL )
+ {
+ scsi_init_free((char *) rscsi_disks,
+ (sd_template.dev_noticed + SD_EXTRA_DEVS)
+ * sizeof(Scsi_Disk));
+
+ scsi_init_free((char *) sd_sizes, sd_template.dev_max * sizeof(int));
+ scsi_init_free((char *) sd_blocksizes, sd_template.dev_max * sizeof(int));
+ scsi_init_free((char *) sd_hardsizes, sd_template.dev_max * sizeof(int));
+ scsi_init_free((char *) sd,
+ (sd_template.dev_max << 4) * sizeof(struct hd_struct));
+ /*
+ * Now remove sd_gendisk from the linked list
+ */
+ sdgd = gendisk_head;
+ prev_sdgd = NULL;
+ while(sdgd != &sd_gendisk)
+ {
+ prev_sdgd = sdgd;
+ sdgd = sdgd->next;
+ }
+
+ if(sdgd != &sd_gendisk)
+ printk("sd_gendisk not in disk chain.\n");
+ else {
+ if(prev_sdgd != NULL)
+ prev_sdgd->next = sdgd->next;
+ else
+ gendisk_head = sdgd->next;
+ }
+ }
+
+ blksize_size[MAJOR_NR] = NULL;
+ blk_dev[MAJOR_NR].request_fn = NULL;
+ blk_size[MAJOR_NR] = NULL;
+ hardsect_size[MAJOR_NR] = NULL;
+ read_ahead[MAJOR_NR] = 0;
+ sd_template.dev_max = 0;
+}
+#endif /* MODULE */
+
+/*
+ * Overrides for Emacs so that we almost follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 4
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */
diff --git a/linux/dev/drivers/scsi/sd_ioctl.c b/linux/dev/drivers/scsi/sd_ioctl.c
new file mode 100644
index 0000000..4c58f04
--- /dev/null
+++ b/linux/dev/drivers/scsi/sd_ioctl.c
@@ -0,0 +1,128 @@
+/*
+ * drivers/scsi/sd_ioctl.c
+ *
+ * ioctl handling for SCSI disks
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include <linux/hdreg.h>
+#include <linux/errno.h>
+
+#include <asm/segment.h>
+
+#include <linux/blk.h>
+#include "scsi.h"
+#include <scsi/scsi_ioctl.h>
+#include "hosts.h"
+#include "sd.h"
+#include <scsi/scsicam.h> /* must follow "hosts.h" */
+
+int sd_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg)
+{
+ kdev_t dev = inode->i_rdev;
+ int error;
+ struct Scsi_Host * host;
+ int diskinfo[4];
+ struct hd_geometry *loc = (struct hd_geometry *) arg;
+
+ switch (cmd) {
+ case HDIO_GETGEO: /* Return BIOS disk parameters */
+ if (!loc) return -EINVAL;
+#ifndef MACH
+ error = verify_area(VERIFY_WRITE, loc, sizeof(*loc));
+ if (error)
+ return error;
+#endif
+ host = rscsi_disks[MINOR(dev) >> 4].device->host;
+
+/* default to most commonly used values */
+
+ diskinfo[0] = 0x40;
+ diskinfo[1] = 0x20;
+ diskinfo[2] = rscsi_disks[MINOR(dev) >> 4].capacity >> 11;
+
+/* override with calculated, extended default, or driver values */
+
+ if(host->hostt->bios_param != NULL)
+ host->hostt->bios_param(&rscsi_disks[MINOR(dev) >> 4],
+ dev,
+ &diskinfo[0]);
+ else scsicam_bios_param(&rscsi_disks[MINOR(dev) >> 4],
+ dev, &diskinfo[0]);
+
+#ifdef MACH
+ loc->heads = diskinfo[0];
+ loc->sectors = diskinfo[1];
+ loc->cylinders = diskinfo[2];
+ loc->start = sd[MINOR(inode->i_rdev)].start_sect;
+#else
+ put_user(diskinfo[0], &loc->heads);
+ put_user(diskinfo[1], &loc->sectors);
+ put_user(diskinfo[2], &loc->cylinders);
+ put_user(sd[MINOR(inode->i_rdev)].start_sect, &loc->start);
+#endif
+ return 0;
+ case BLKGETSIZE: /* Return device size */
+ if (!arg) return -EINVAL;
+ error = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
+ if (error)
+ return error;
+ put_user(sd[MINOR(inode->i_rdev)].nr_sects,
+ (long *) arg);
+ return 0;
+
+ case BLKRASET:
+ if (!suser())
+ return -EACCES;
+ if(!(inode->i_rdev)) return -EINVAL;
+ if(arg > 0xff) return -EINVAL;
+ read_ahead[MAJOR(inode->i_rdev)] = arg;
+ return 0;
+
+ case BLKRAGET:
+ if (!arg)
+ return -EINVAL;
+ error = verify_area(VERIFY_WRITE, (int *) arg, sizeof(int));
+ if (error)
+ return error;
+ put_user(read_ahead[MAJOR(inode->i_rdev)], (int *) arg);
+ return 0;
+
+ case BLKFLSBUF:
+ if(!suser()) return -EACCES;
+ if(!(inode->i_rdev)) return -EINVAL;
+ fsync_dev(inode->i_rdev);
+ invalidate_buffers(inode->i_rdev);
+ return 0;
+
+ case BLKRRPART: /* Re-read partition tables */
+ return revalidate_scsidisk(dev, 1);
+
+ RO_IOCTLS(dev, arg);
+
+ default:
+ return scsi_ioctl(rscsi_disks[MINOR(dev) >> 4].device , cmd, (void *) arg);
+ }
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 4
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */
diff --git a/linux/dev/drivers/scsi/seagate.c b/linux/dev/drivers/scsi/seagate.c
new file mode 100644
index 0000000..a5acbee
--- /dev/null
+++ b/linux/dev/drivers/scsi/seagate.c
@@ -0,0 +1,1761 @@
+/*
+ * seagate.c Copyright (C) 1992, 1993 Drew Eckhardt
+ * low level scsi driver for ST01/ST02, Future Domain TMC-885,
+ * TMC-950 by
+ *
+ * Drew Eckhardt
+ *
+ * <drew@colorado.edu>
+ *
+ * Note : TMC-880 boards don't work because they have two bits in
+ * the status register flipped, I'll fix this "RSN"
+ *
+ * This card does all the I/O via memory mapped I/O, so there is no need
+ * to check or allocate a region of the I/O address space.
+ */
+
+/*
+ * Configuration :
+ * To use without BIOS -DOVERRIDE=base_address -DCONTROLLER=FD or SEAGATE
+ * -DIRQ will override the default of 5.
+ * Note: You can now set these options from the kernel's "command line".
+ * The syntax is:
+ *
+ * st0x=ADDRESS,IRQ (for a Seagate controller)
+ * or:
+ * tmc8xx=ADDRESS,IRQ (for a TMC-8xx or TMC-950 controller)
+ * eg:
+ * tmc8xx=0xC8000,15
+ *
+ * will configure the driver for a TMC-8xx style controller using IRQ 15
+ * with a base address of 0xC8000.
+ *
+ * -DFAST or -DFAST32 will use blind transfers where possible
+ *
+ * -DARBITRATE will cause the host adapter to arbitrate for the
+ * bus for better SCSI-II compatibility, rather than just
+ * waiting for BUS FREE and then doing its thing. Should
+ * let us do one command per Lun when I integrate my
+ * reorganization changes into the distribution sources.
+ *
+ * -DSLOW_HANDSHAKE will allow compatibility with broken devices that don't
+ * handshake fast enough (ie, some CD ROM's) for the Seagate
+ * code.
+ *
+ * -DSLOW_RATE=x, x some number will let you specify a default
+ * transfer rate if handshaking isn't working correctly.
+ */
+
+#ifdef MACH
+#define ARBITRATE
+#define SLOW_HANDSHAKE
+#define FAST32
+#endif
+
+#include <linux/module.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/config.h>
+#include <linux/proc_fs.h>
+
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+#include "seagate.h"
+#include "constants.h"
+#include<linux/stat.h>
+
+struct proc_dir_entry proc_scsi_seagate = {
+ PROC_SCSI_SEAGATE, 7, "seagate",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+
+#ifndef IRQ
+#define IRQ 5
+#endif
+
+#if (defined(FAST32) && !defined(FAST))
+#define FAST
+#endif
+
+#if defined(SLOW_RATE) && !defined(SLOW_HANDSHAKE)
+#define SLOW_HANDSHAKE
+#endif
+
+#if defined(SLOW_HANDSHAKE) && !defined(SLOW_RATE)
+#define SLOW_RATE 50
+#endif
+
+
+#if defined(LINKED)
+#undef LINKED /* Linked commands are currently broken ! */
+#endif
+
+static int internal_command(unsigned char target, unsigned char lun,
+ const void *cmnd,
+ void *buff, int bufflen, int reselect);
+
+static int incommand; /*
+ set if arbitration has finished and we are
+ in some command phase.
+ */
+
+static const void *base_address = NULL; /*
+ Where the card ROM starts,
+ used to calculate memory mapped
+ register location.
+ */
+#ifdef notyet
+static volatile int abort_confirm = 0;
+#endif
+
+static volatile void *st0x_cr_sr; /*
+ control register write,
+ status register read.
+ 256 bytes in length.
+
+ Read is status of SCSI BUS,
+ as per STAT masks.
+
+ */
+
+
+static volatile void *st0x_dr; /*
+ data register, read write
+ 256 bytes in length.
+ */
+
+
+static volatile int st0x_aborted=0; /*
+ set when we are aborted, ie by a time out, etc.
+ */
+
+static unsigned char controller_type = 0; /* set to SEAGATE for ST0x boards or FD for TMC-8xx boards */
+static unsigned char irq = IRQ;
+
+#define retcode(result) (((result) << 16) | (message << 8) | status)
+#define STATUS (*(volatile unsigned char *) st0x_cr_sr)
+#define CONTROL STATUS
+#define DATA (*(volatile unsigned char *) st0x_dr)
+
+void st0x_setup (char *str, int *ints) {
+ controller_type = SEAGATE;
+ base_address = (void *) ints[1];
+ irq = ints[2];
+}
+
+void tmc8xx_setup (char *str, int *ints) {
+ controller_type = FD;
+ base_address = (void *) ints[1];
+ irq = ints[2];
+}
+
+
+#ifndef OVERRIDE
+static const char * seagate_bases[] = {
+ (char *) 0xc8000, (char *) 0xca000, (char *) 0xcc000,
+ (char *) 0xce000, (char *) 0xdc000, (char *) 0xde000
+};
+
+typedef struct {
+ const char *signature ;
+ unsigned offset;
+ unsigned length;
+ unsigned char type;
+} Signature;
+
+static const Signature signatures[] = {
+#ifdef CONFIG_SCSI_SEAGATE
+{"ST01 v1.7 (C) Copyright 1987 Seagate", 15, 37, SEAGATE},
+{"SCSI BIOS 2.00 (C) Copyright 1987 Seagate", 15, 40, SEAGATE},
+
+/*
+ * The following two lines are NOT mistakes. One detects ROM revision
+ * 3.0.0, the other 3.2. Since seagate has only one type of SCSI adapter,
+ * and this is not going to change, the "SEAGATE" and "SCSI" together
+ * are probably "good enough"
+ */
+
+{"SEAGATE SCSI BIOS ",16, 17, SEAGATE},
+{"SEAGATE SCSI BIOS ",17, 17, SEAGATE},
+
+/*
+ * However, future domain makes several incompatible SCSI boards, so specific
+ * signatures must be used.
+ */
+
+{"FUTURE DOMAIN CORP. (C) 1986-1989 V5.0C2/14/89", 5, 46, FD},
+{"FUTURE DOMAIN CORP. (C) 1986-1989 V6.0A7/28/89", 5, 46, FD},
+{"FUTURE DOMAIN CORP. (C) 1986-1990 V6.0105/31/90",5, 47, FD},
+{"FUTURE DOMAIN CORP. (C) 1986-1990 V6.0209/18/90",5, 47, FD},
+{"FUTURE DOMAIN CORP. (C) 1986-1990 V7.009/18/90", 5, 46, FD},
+{"FUTURE DOMAIN CORP. (C) 1992 V8.00.004/02/92", 5, 44, FD},
+{"IBM F1 BIOS V1.1004/30/92", 5, 25, FD},
+{"FUTURE DOMAIN TMC-950", 5, 21, FD},
+#endif /* CONFIG_SCSI_SEAGATE */
+}
+;
+
+#define NUM_SIGNATURES (sizeof(signatures) / sizeof(Signature))
+#endif /* n OVERRIDE */
+
+/*
+ * hostno stores the hostnumber, as told to us by the init routine.
+ */
+
+static int hostno = -1;
+static void seagate_reconnect_intr(int, void *, struct pt_regs *);
+
+#ifdef FAST
+static int fast = 1;
+#endif
+
+#ifdef SLOW_HANDSHAKE
+/*
+ * Support for broken devices :
+ * The Seagate board has a handshaking problem. Namely, a lack
+ * thereof for slow devices. You can blast 600K/second through
+ * it if you are polling for each byte, more if you do a blind
+ * transfer. In the first case, with a fast device, REQ will
+ * transition high-low or high-low-high before your loop restarts
+ * and you'll have no problems. In the second case, the board
+ * will insert wait states for up to 13.2 usecs for REQ to
+ * transition low->high, and everything will work.
+ *
+ * However, there's nothing in the state machine that says
+ * you *HAVE* to see a high-low-high set of transitions before
+ * sending the next byte, and slow things like the Trantor CD ROMS
+ * will break because of this.
+ *
+ * So, we need to slow things down, which isn't as simple as it
+ * seems. We can't slow things down period, because then people
+ * who don't recompile their kernels will shoot me for ruining
+ * their performance. We need to do it on a case per case basis.
+ *
+ * The best for performance will be to, only for borken devices
+ * (this is stored on a per-target basis in the scsi_devices array)
+ *
+ * Wait for a low->high transition before continuing with that
+ * transfer. If we timeout, continue anyways. We don't need
+ * a long timeout, because REQ should only be asserted until the
+ * corresponding ACK is received and processed.
+ *
+ * Note that we can't use the system timer for this, because of
+ * resolution, and we *really* can't use the timer chip since
+ * gettimeofday() and the beeper routines use that. So,
+ * the best thing for us to do will be to calibrate a timing
+ * loop in the initialization code using the timer chip before
+ * gettimeofday() can screw with it.
+ */
+
+static int borken_calibration = 0;
+static void borken_init (void) {
+ register int count = 0, start = jiffies + 1, stop = start + 25;
+
+ while (jiffies < start);
+ for (;jiffies < stop; ++count);
+
+/*
+ * Ok, we now have a count for .25 seconds. Convert to a
+ * count per second and divide by transfer rate in K.
+ */
+
+ borken_calibration = (count * 4) / (SLOW_RATE*1024);
+
+ if (borken_calibration < 1)
+ borken_calibration = 1;
+#if (DEBUG & DEBUG_BORKEN)
+ printk("scsi%d : borken calibrated to %dK/sec, %d cycles per transfer\n",
+ hostno, BORKEN_RATE, borken_calibration);
+#endif
+}
+
+static inline void borken_wait(void) {
+ register int count;
+ for (count = borken_calibration; count && (STATUS & STAT_REQ);
+ --count);
+#if (DEBUG & DEBUG_BORKEN)
+ if (count)
+ printk("scsi%d : borken timeout\n", hostno);
+#endif
+}
+
+#endif /* def SLOW_HANDSHAKE */
+
+int seagate_st0x_detect (Scsi_Host_Template * tpnt)
+ {
+ struct Scsi_Host *instance;
+#ifndef OVERRIDE
+ int i,j;
+#endif
+
+ tpnt->proc_dir = &proc_scsi_seagate;
+/*
+ * First, we try for the manual override.
+ */
+#ifdef DEBUG
+ printk("Autodetecting ST0x / TMC-8xx\n");
+#endif
+
+ if (hostno != -1)
+ {
+ printk ("ERROR : seagate_st0x_detect() called twice.\n");
+ return 0;
+ }
+
+ /* If the user specified the controller type from the command line,
+ controller_type will be non-zero, so don't try to detect one */
+
+ if (!controller_type) {
+#ifdef OVERRIDE
+ base_address = (void *) OVERRIDE;
+
+/* CONTROLLER is used to override controller (SEAGATE or FD). PM: 07/01/93 */
+#ifdef CONTROLLER
+ controller_type = CONTROLLER;
+#else
+#error Please use -DCONTROLLER=SEAGATE or -DCONTROLLER=FD to override controller type
+#endif /* CONTROLLER */
+#ifdef DEBUG
+ printk("Base address overridden to %x, controller type is %s\n",
+ base_address,controller_type == SEAGATE ? "SEAGATE" : "FD");
+#endif
+#else /* OVERRIDE */
+/*
+ * To detect this card, we simply look for the signature
+ * from the BIOS version notice in all the possible locations
+ * of the ROM's. This has a nice side effect of not trashing
+ * any register locations that might be used by something else.
+ *
+ * XXX - note that we probably should be probing the address
+ * space for the on-board RAM instead.
+ */
+
+ for (i = 0; i < (sizeof (seagate_bases) / sizeof (char * )); ++i)
+ for (j = 0; !base_address && j < NUM_SIGNATURES; ++j)
+ if (!memcmp ((const void *) (seagate_bases[i] +
+ signatures[j].offset), (const void *) signatures[j].signature,
+ signatures[j].length)) {
+ base_address = (const void *) seagate_bases[i];
+ controller_type = signatures[j].type;
+ }
+#endif /* OVERRIDE */
+ } /* (! controller_type) */
+
+ tpnt->this_id = (controller_type == SEAGATE) ? 7 : 6;
+ tpnt->name = (controller_type == SEAGATE) ? ST0X_ID_STR : FD_ID_STR;
+
+ if (base_address)
+ {
+ st0x_cr_sr =(void *) (((const unsigned char *) base_address) + (controller_type == SEAGATE ? 0x1a00 : 0x1c00));
+ st0x_dr = (void *) (((const unsigned char *) base_address ) + (controller_type == SEAGATE ? 0x1c00 : 0x1e00));
+#ifdef DEBUG
+ printk("%s detected. Base address = %x, cr = %x, dr = %x\n", tpnt->name, base_address, st0x_cr_sr, st0x_dr);
+#endif
+/*
+ * At all times, we will use IRQ 5. Should also check for IRQ3 if we
+ * loose our first interrupt.
+ */
+ instance = scsi_register(tpnt, 0);
+ hostno = instance->host_no;
+ if (request_irq((int) irq, seagate_reconnect_intr, SA_INTERRUPT,
+ (controller_type == SEAGATE) ? "seagate" : "tmc-8xx", NULL)) {
+ printk("scsi%d : unable to allocate IRQ%d\n",
+ hostno, (int) irq);
+ return 0;
+ }
+ instance->irq = irq;
+ instance->io_port = (unsigned int) base_address;
+#ifdef SLOW_HANDSHAKE
+ borken_init();
+#endif
+
+ printk("%s options:"
+#ifdef ARBITRATE
+ " ARBITRATE"
+#endif
+#ifdef SLOW_HANDSHAKE
+ " SLOW_HANDSHAKE"
+#endif
+#ifdef FAST
+#ifdef FAST32
+ " FAST32"
+#else
+ " FAST"
+#endif
+#endif
+#ifdef LINKED
+ " LINKED"
+#endif
+ "\n", tpnt->name);
+ return 1;
+ }
+ else
+ {
+#ifdef DEBUG
+ printk("ST0x / TMC-8xx not detected.\n");
+#endif
+ return 0;
+ }
+ }
+
+const char *seagate_st0x_info(struct Scsi_Host * shpnt) {
+ static char buffer[64];
+ sprintf(buffer, "%s at irq %d, address 0x%05X",
+ (controller_type == SEAGATE) ? ST0X_ID_STR : FD_ID_STR,
+ irq, (unsigned int)base_address);
+ return buffer;
+}
+
+int seagate_st0x_proc_info(char *buffer, char **start, off_t offset,
+ int length, int hostno, int inout)
+{
+ const char *info = seagate_st0x_info(NULL);
+ int len;
+ int pos;
+ int begin;
+
+ if (inout) return(-ENOSYS);
+
+ begin = 0;
+ strcpy(buffer,info);
+ strcat(buffer,"\n");
+
+ pos = len = strlen(buffer);
+
+ if (pos<offset) {
+ len = 0;
+ begin = pos;
+ }
+
+ *start = buffer + (offset - begin);
+ len -= (offset - begin);
+ if ( len > length ) len = length;
+ return(len);
+}
+
+/*
+ * These are our saved pointers for the outstanding command that is
+ * waiting for a reconnect
+ */
+
+static unsigned char current_target, current_lun;
+static unsigned char *current_cmnd, *current_data;
+static int current_nobuffs;
+static struct scatterlist *current_buffer;
+static int current_bufflen;
+
+#ifdef LINKED
+
+/*
+ * linked_connected indicates whether or not we are currently connected to
+ * linked_target, linked_lun and in an INFORMATION TRANSFER phase,
+ * using linked commands.
+ */
+
+static int linked_connected = 0;
+static unsigned char linked_target, linked_lun;
+#endif
+
+
+static void (*done_fn)(Scsi_Cmnd *) = NULL;
+static Scsi_Cmnd * SCint = NULL;
+
+/*
+ * These control whether or not disconnect / reconnect will be attempted,
+ * or are being attempted.
+ */
+
+#define NO_RECONNECT 0
+#define RECONNECT_NOW 1
+#define CAN_RECONNECT 2
+
+#ifdef LINKED
+
+/*
+ * LINKED_RIGHT indicates that we are currently connected to the correct target
+ * for this command, LINKED_WRONG indicates that we are connected to the wrong
+ * target. Note that these imply CAN_RECONNECT.
+ */
+
+#define LINKED_RIGHT 3
+#define LINKED_WRONG 4
+#endif
+
+/*
+ * This determines if we are expecting to reconnect or not.
+ */
+
+static int should_reconnect = 0;
+
+/*
+ * The seagate_reconnect_intr routine is called when a target reselects the
+ * host adapter. This occurs on the interrupt triggered by the target
+ * asserting SEL.
+ */
+
+static void seagate_reconnect_intr(int irq, void *dev_id, struct pt_regs *regs)
+ {
+ int temp;
+ Scsi_Cmnd * SCtmp;
+
+/* enable all other interrupts. */
+ sti();
+#if (DEBUG & PHASE_RESELECT)
+ printk("scsi%d : seagate_reconnect_intr() called\n", hostno);
+#endif
+
+ if (!should_reconnect)
+ printk("scsi%d: unexpected interrupt.\n", hostno);
+ else {
+ should_reconnect = 0;
+
+#if (DEBUG & PHASE_RESELECT)
+ printk("scsi%d : internal_command("
+ "%d, %08x, %08x, %d, RECONNECT_NOW\n", hostno,
+ current_target, current_data, current_bufflen);
+#endif
+
+ temp = internal_command (current_target, current_lun,
+ current_cmnd, current_data, current_bufflen,
+ RECONNECT_NOW);
+
+ if (msg_byte(temp) != DISCONNECT) {
+ if (done_fn) {
+#if (DEBUG & PHASE_RESELECT)
+ printk("scsi%d : done_fn(%d,%08x)", hostno,
+ hostno, temp);
+#endif
+ if(!SCint) panic("SCint == NULL in seagate");
+ SCtmp = SCint;
+ SCint = NULL;
+ SCtmp->result = temp;
+ done_fn (SCtmp);
+ } else
+ printk("done_fn() not defined.\n");
+ }
+ }
+ }
+
+/*
+ * The seagate_st0x_queue_command() function provides a queued interface
+ * to the seagate SCSI driver. Basically, it just passes control onto the
+ * seagate_command() function, after fixing it so that the done_fn()
+ * is set to the one passed to the function. We have to be very careful,
+ * because there are some commands on some devices that do not disconnect,
+ * and if we simply call the done_fn when the command is done then another
+ * command is started and queue_command is called again... We end up
+ * overflowing the kernel stack, and this tends not to be such a good idea.
+ */
+
+static int recursion_depth = 0;
+
+int seagate_st0x_queue_command (Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
+ {
+ int result, reconnect;
+ Scsi_Cmnd * SCtmp;
+
+ done_fn = done;
+ current_target = SCpnt->target;
+ current_lun = SCpnt->lun;
+ (const void *) current_cmnd = SCpnt->cmnd;
+ current_data = (unsigned char *) SCpnt->request_buffer;
+ current_bufflen = SCpnt->request_bufflen;
+ SCint = SCpnt;
+ if(recursion_depth) {
+ return 0;
+ };
+ recursion_depth++;
+ do{
+#ifdef LINKED
+/*
+ * Set linked command bit in control field of SCSI command.
+ */
+
+ current_cmnd[SCpnt->cmd_len] |= 0x01;
+ if (linked_connected) {
+#if (DEBUG & DEBUG_LINKED)
+ printk("scsi%d : using linked commands, current I_T_L nexus is ",
+ hostno);
+#endif
+ if ((linked_target == current_target) &&
+ (linked_lun == current_lun)) {
+#if (DEBUG & DEBUG_LINKED)
+ printk("correct\n");
+#endif
+ reconnect = LINKED_RIGHT;
+ } else {
+#if (DEBUG & DEBUG_LINKED)
+ printk("incorrect\n");
+#endif
+ reconnect = LINKED_WRONG;
+ }
+ } else
+#endif /* LINKED */
+ reconnect = CAN_RECONNECT;
+
+
+
+
+
+ result = internal_command (SCint->target, SCint->lun, SCint->cmnd, SCint->request_buffer,
+ SCint->request_bufflen,
+ reconnect);
+ if (msg_byte(result) == DISCONNECT) break;
+ SCtmp = SCint;
+ SCint = NULL;
+ SCtmp->result = result;
+ done_fn (SCtmp);
+ } while(SCint);
+ recursion_depth--;
+ return 0;
+ }
+
+int seagate_st0x_command (Scsi_Cmnd * SCpnt) {
+ return internal_command (SCpnt->target, SCpnt->lun, SCpnt->cmnd, SCpnt->request_buffer,
+ SCpnt->request_bufflen,
+ (int) NO_RECONNECT);
+}
+
+static int internal_command(unsigned char target, unsigned char lun, const void *cmnd,
+ void *buff, int bufflen, int reselect) {
+ int len = 0;
+ unsigned char *data = NULL;
+ struct scatterlist *buffer = NULL;
+ int nobuffs = 0;
+ int clock;
+ int temp;
+#ifdef SLOW_HANDSHAKE
+ int borken; /* Does the current target require Very Slow I/O ? */
+#endif
+
+
+#if (DEBUG & PHASE_DATAIN) || (DEBUG & PHASE_DATOUT)
+ int transfered = 0;
+#endif
+
+#if (((DEBUG & PHASE_ETC) == PHASE_ETC) || (DEBUG & PRINT_COMMAND) || \
+ (DEBUG & PHASE_EXIT))
+ int i;
+#endif
+
+#if ((DEBUG & PHASE_ETC) == PHASE_ETC)
+ int phase=0, newphase;
+#endif
+
+ int done = 0;
+ unsigned char status = 0;
+ unsigned char message = 0;
+ register unsigned char status_read;
+
+ unsigned transfersize = 0, underflow = 0;
+
+ incommand = 0;
+ st0x_aborted = 0;
+
+#ifdef SLOW_HANDSHAKE
+ borken = (int) SCint->device->borken;
+#endif
+
+#if (DEBUG & PRINT_COMMAND)
+ printk ("scsi%d : target = %d, command = ", hostno, target);
+ print_command((unsigned char *) cmnd);
+ printk("\n");
+#endif
+
+#if (DEBUG & PHASE_RESELECT)
+ switch (reselect) {
+ case RECONNECT_NOW :
+ printk("scsi%d : reconnecting\n", hostno);
+ break;
+#ifdef LINKED
+ case LINKED_RIGHT :
+ printk("scsi%d : connected, can reconnect\n", hostno);
+ break;
+ case LINKED_WRONG :
+ printk("scsi%d : connected to wrong target, can reconnect\n",
+ hostno);
+ break;
+#endif
+ case CAN_RECONNECT :
+ printk("scsi%d : allowed to reconnect\n", hostno);
+ break;
+ default :
+ printk("scsi%d : not allowed to reconnect\n", hostno);
+ }
+#endif
+
+
+ if (target == (controller_type == SEAGATE ? 7 : 6))
+ return DID_BAD_TARGET;
+
+/*
+ * We work it differently depending on if this is "the first time,"
+ * or a reconnect. If this is a reselect phase, then SEL will
+ * be asserted, and we must skip selection / arbitration phases.
+ */
+
+ switch (reselect) {
+ case RECONNECT_NOW:
+#if (DEBUG & PHASE_RESELECT)
+ printk("scsi%d : phase RESELECT \n", hostno);
+#endif
+
+/*
+ * At this point, we should find the logical or of our ID and the original
+ * target's ID on the BUS, with BSY, SEL, and I/O signals asserted.
+ *
+ * After ARBITRATION phase is completed, only SEL, BSY, and the
+ * target ID are asserted. A valid initiator ID is not on the bus
+ * until IO is asserted, so we must wait for that.
+ */
+ clock = jiffies + 10;
+ for (;;) {
+ temp = STATUS;
+ if ((temp & STAT_IO) && !(temp & STAT_BSY))
+ break;
+
+ if (jiffies > clock) {
+#if (DEBUG & PHASE_RESELECT)
+ printk("scsi%d : RESELECT timed out while waiting for IO .\n",
+ hostno);
+#endif
+ return (DID_BAD_INTR << 16);
+ }
+ }
+
+/*
+ * After I/O is asserted by the target, we can read our ID and its
+ * ID off of the BUS.
+ */
+
+ if (!((temp = DATA) & (controller_type == SEAGATE ? 0x80 : 0x40)))
+ {
+#if (DEBUG & PHASE_RESELECT)
+ printk("scsi%d : detected reconnect request to different target.\n"
+ "\tData bus = %d\n", hostno, temp);
+#endif
+ return (DID_BAD_INTR << 16);
+ }
+
+ if (!(temp & (1 << current_target)))
+ {
+ printk("scsi%d : Unexpected reselect interrupt. Data bus = %d\n",
+ hostno, temp);
+ return (DID_BAD_INTR << 16);
+ }
+
+ buffer=current_buffer;
+ cmnd=current_cmnd; /* WDE add */
+ data=current_data; /* WDE add */
+ len=current_bufflen; /* WDE add */
+ nobuffs=current_nobuffs;
+
+/*
+ * We have determined that we have been selected. At this point,
+ * we must respond to the reselection by asserting BSY ourselves
+ */
+
+#if 1
+ CONTROL = (BASE_CMD | CMD_DRVR_ENABLE | CMD_BSY);
+#else
+ CONTROL = (BASE_CMD | CMD_BSY);
+#endif
+
+/*
+ * The target will drop SEL, and raise BSY, at which time we must drop
+ * BSY.
+ */
+
+ for (clock = jiffies + 10; (jiffies < clock) && (STATUS & STAT_SEL););
+
+ if (jiffies >= clock)
+ {
+ CONTROL = (BASE_CMD | CMD_INTR);
+#if (DEBUG & PHASE_RESELECT)
+ printk("scsi%d : RESELECT timed out while waiting for SEL.\n",
+ hostno);
+#endif
+ return (DID_BAD_INTR << 16);
+ }
+
+ CONTROL = BASE_CMD;
+
+/*
+ * At this point, we have connected with the target and can get
+ * on with our lives.
+ */
+ break;
+ case CAN_RECONNECT:
+
+#ifdef LINKED
+/*
+ * This is a bletcherous hack, just as bad as the Unix #! interpreter stuff.
+ * If it turns out we are using the wrong I_T_L nexus, the easiest way to deal
+ * with it is to go into our INFORMATION TRANSFER PHASE code, send a ABORT
+ * message on MESSAGE OUT phase, and then loop back to here.
+ */
+
+connect_loop :
+
+#endif
+
+#if (DEBUG & PHASE_BUS_FREE)
+ printk ("scsi%d : phase = BUS FREE \n", hostno);
+#endif
+
+/*
+ * BUS FREE PHASE
+ *
+ * On entry, we make sure that the BUS is in a BUS FREE
+ * phase, by insuring that both BSY and SEL are low for
+ * at least one bus settle delay. Several reads help
+ * eliminate wire glitch.
+ */
+
+ clock = jiffies + ST0X_BUS_FREE_DELAY;
+
+#if !defined (ARBITRATE)
+ while (((STATUS | STATUS | STATUS) &
+ (STAT_BSY | STAT_SEL)) &&
+ (!st0x_aborted) && (jiffies < clock));
+
+ if (jiffies > clock)
+ return retcode(DID_BUS_BUSY);
+ else if (st0x_aborted)
+ return retcode(st0x_aborted);
+#endif
+
+#if (DEBUG & PHASE_SELECTION)
+ printk("scsi%d : phase = SELECTION\n", hostno);
+#endif
+
+ clock = jiffies + ST0X_SELECTION_DELAY;
+
+/*
+ * Arbitration/selection procedure :
+ * 1. Disable drivers
+ * 2. Write HOST adapter address bit
+ * 3. Set start arbitration.
+ * 4. We get either ARBITRATION COMPLETE or SELECT at this
+ * point.
+ * 5. OR our ID and targets on bus.
+ * 6. Enable SCSI drivers and asserted SEL and ATTN
+ */
+
+#if defined(ARBITRATE)
+ cli();
+ CONTROL = 0;
+ DATA = (controller_type == SEAGATE) ? 0x80 : 0x40;
+ CONTROL = CMD_START_ARB;
+ sti();
+ while (!((status_read = STATUS) & (STAT_ARB_CMPL | STAT_SEL)) &&
+ (jiffies < clock) && !st0x_aborted);
+
+ if (!(status_read & STAT_ARB_CMPL)) {
+#if (DEBUG & PHASE_SELECTION)
+ if (status_read & STAT_SEL)
+ printk("scsi%d : arbitration lost\n", hostno);
+ else
+ printk("scsi%d : arbitration timeout.\n", hostno);
+#endif
+ CONTROL = BASE_CMD;
+ return retcode(DID_NO_CONNECT);
+ };
+
+#if (DEBUG & PHASE_SELECTION)
+ printk("scsi%d : arbitration complete\n", hostno);
+#endif
+#endif
+
+
+/*
+ * When the SCSI device decides that we're gawking at it, it will
+ * respond by asserting BUSY on the bus.
+ *
+ * Note : the Seagate ST-01/02 product manual says that we should
+ * twiddle the DATA register before the control register. However,
+ * this does not work reliably so we do it the other way around.
+ *
+ * Probably could be a problem with arbitration too, we really should
+ * try this with a SCSI protocol or logic analyzer to see what is
+ * going on.
+ */
+ cli();
+ DATA = (unsigned char) ((1 << target) | (controller_type == SEAGATE ? 0x80 : 0x40));
+ CONTROL = BASE_CMD | CMD_DRVR_ENABLE | CMD_SEL |
+ (reselect ? CMD_ATTN : 0);
+ sti();
+ while (!((status_read = STATUS) & STAT_BSY) &&
+ (jiffies < clock) && !st0x_aborted)
+
+#if 0 && (DEBUG & PHASE_SELECTION)
+ {
+ temp = clock - jiffies;
+
+ if (!(jiffies % 5))
+ printk("seagate_st0x_timeout : %d \r",temp);
+
+ }
+ printk("Done. \n");
+ printk("scsi%d : status = %02x, seagate_st0x_timeout = %d, aborted = %02x \n",
+ hostno, status_read, temp, st0x_aborted);
+#else
+ ;
+#endif
+
+
+ if ((jiffies >= clock) && !(status_read & STAT_BSY))
+ {
+#if (DEBUG & PHASE_SELECTION)
+ printk ("scsi%d : NO CONNECT with target %d, status = %x \n",
+ hostno, target, STATUS);
+#endif
+ return retcode(DID_NO_CONNECT);
+ }
+
+/*
+ * If we have been aborted, and we have a command in progress, IE the
+ * target still has BSY asserted, then we will reset the bus, and
+ * notify the midlevel driver to expect sense.
+ */
+
+ if (st0x_aborted) {
+ CONTROL = BASE_CMD;
+ if (STATUS & STAT_BSY) {
+ printk("scsi%d : BST asserted after we've been aborted.\n",
+ hostno);
+ seagate_st0x_reset(NULL, 0);
+ return retcode(DID_RESET);
+ }
+ return retcode(st0x_aborted);
+ }
+
+/* Establish current pointers. Take into account scatter / gather */
+
+ if ((nobuffs = SCint->use_sg)) {
+#if (DEBUG & DEBUG_SG)
+ {
+ int i;
+ printk("scsi%d : scatter gather requested, using %d buffers.\n",
+ hostno, nobuffs);
+ for (i = 0; i < nobuffs; ++i)
+ printk("scsi%d : buffer %d address = %08x length = %d\n",
+ hostno, i, buffer[i].address, buffer[i].length);
+ }
+#endif
+
+ buffer = (struct scatterlist *) SCint->buffer;
+ len = buffer->length;
+ data = (unsigned char *) buffer->address;
+ } else {
+#if (DEBUG & DEBUG_SG)
+ printk("scsi%d : scatter gather not requested.\n", hostno);
+#endif
+ buffer = NULL;
+ len = SCint->request_bufflen;
+ data = (unsigned char *) SCint->request_buffer;
+ }
+
+#if (DEBUG & (PHASE_DATAIN | PHASE_DATAOUT))
+ printk("scsi%d : len = %d\n", hostno, len);
+#endif
+
+ break;
+#ifdef LINKED
+ case LINKED_RIGHT:
+ break;
+ case LINKED_WRONG:
+ break;
+#endif
+ }
+
+/*
+ * There are several conditions under which we wish to send a message :
+ * 1. When we are allowing disconnect / reconnect, and need to establish
+ * the I_T_L nexus via an IDENTIFY with the DiscPriv bit set.
+ *
+ * 2. When we are doing linked commands, are have the wrong I_T_L nexus
+ * established and want to send an ABORT message.
+ */
+
+
+ CONTROL = BASE_CMD | CMD_DRVR_ENABLE |
+ (((reselect == CAN_RECONNECT)
+#ifdef LINKED
+ || (reselect == LINKED_WRONG)
+#endif
+ ) ? CMD_ATTN : 0) ;
+
+/*
+ * INFORMATION TRANSFER PHASE
+ *
+ * The nasty looking read / write inline assembler loops we use for
+ * DATAIN and DATAOUT phases are approximately 4-5 times as fast as
+ * the 'C' versions - since we're moving 1024 bytes of data, this
+ * really adds up.
+ */
+
+#if ((DEBUG & PHASE_ETC) == PHASE_ETC)
+ printk("scsi%d : phase = INFORMATION TRANSFER\n", hostno);
+#endif
+
+ incommand = 1;
+ transfersize = SCint->transfersize;
+ underflow = SCint->underflow;
+
+
+/*
+ * Now, we poll the device for status information,
+ * and handle any requests it makes. Note that since we are unsure of
+ * how much data will be flowing across the system, etc and cannot
+ * make reasonable timeouts, that we will instead have the midlevel
+ * driver handle any timeouts that occur in this phase.
+ */
+
+ while (((status_read = STATUS) & STAT_BSY) && !st0x_aborted && !done)
+ {
+#ifdef PARITY
+ if (status_read & STAT_PARITY)
+ {
+ printk("scsi%d : got parity error\n", hostno);
+ st0x_aborted = DID_PARITY;
+ }
+#endif
+
+ if (status_read & STAT_REQ)
+ {
+#if ((DEBUG & PHASE_ETC) == PHASE_ETC)
+ if ((newphase = (status_read & REQ_MASK)) != phase)
+ {
+ phase = newphase;
+ switch (phase)
+ {
+ case REQ_DATAOUT:
+ printk("scsi%d : phase = DATA OUT\n",
+ hostno);
+ break;
+ case REQ_DATAIN :
+ printk("scsi%d : phase = DATA IN\n",
+ hostno);
+ break;
+ case REQ_CMDOUT :
+ printk("scsi%d : phase = COMMAND OUT\n",
+ hostno);
+ break;
+ case REQ_STATIN :
+ printk("scsi%d : phase = STATUS IN\n",
+ hostno);
+ break;
+ case REQ_MSGOUT :
+ printk("scsi%d : phase = MESSAGE OUT\n",
+ hostno);
+ break;
+ case REQ_MSGIN :
+ printk("scsi%d : phase = MESSAGE IN\n",
+ hostno);
+ break;
+ default :
+ printk("scsi%d : phase = UNKNOWN\n",
+ hostno);
+ st0x_aborted = DID_ERROR;
+ }
+ }
+#endif
+ switch (status_read & REQ_MASK)
+ {
+ case REQ_DATAOUT :
+/*
+ * If we are in fast mode, then we simply splat the data out
+ * in word-sized chunks as fast as we can.
+ */
+
+#ifdef FAST
+if (!len) {
+#if 0
+ printk("scsi%d: underflow to target %d lun %d \n",
+ hostno, target, lun);
+ st0x_aborted = DID_ERROR;
+ fast = 0;
+#endif
+ break;
+}
+
+if (fast && transfersize && !(len % transfersize) && (len >= transfersize)
+#ifdef FAST32
+ && !(transfersize % 4)
+#endif
+ ) {
+#if (DEBUG & DEBUG_FAST)
+ printk("scsi%d : FAST transfer, underflow = %d, transfersize = %d\n"
+ " len = %d, data = %08x\n", hostno, SCint->underflow,
+ SCint->transfersize, len, data);
+#endif
+
+ __asm__("
+ cld;
+"
+#ifdef FAST32
+" shr $2, %%ecx;
+1: lodsl;
+ movl %%eax, (%%edi);
+"
+#else
+"1: lodsb;
+ movb %%al, (%%edi);
+"
+#endif
+" loop 1b;" : :
+ /* input */
+ "D" (st0x_dr), "S" (data), "c" (SCint->transfersize) :
+ /* clobbered */
+ "eax", "ecx", "esi" );
+
+ len -= transfersize;
+ data += transfersize;
+
+#if (DEBUG & DEBUG_FAST)
+ printk("scsi%d : FAST transfer complete len = %d data = %08x\n",
+ hostno, len, data);
+#endif
+
+
+} else
+#endif
+
+{
+/*
+ * We loop as long as we are in a data out phase, there is data to send,
+ * and BSY is still active.
+ */
+ __asm__ (
+
+/*
+ Local variables :
+ len = ecx
+ data = esi
+ st0x_cr_sr = ebx
+ st0x_dr = edi
+
+ Test for any data here at all.
+*/
+ "\torl %%ecx, %%ecx
+ jz 2f
+
+ cld
+
+ movl " SYMBOL_NAME_STR(st0x_cr_sr) ", %%ebx
+ movl " SYMBOL_NAME_STR(st0x_dr) ", %%edi
+
+1: movb (%%ebx), %%al\n"
+/*
+ Test for BSY
+*/
+
+ "\ttest $1, %%al
+ jz 2f\n"
+
+/*
+ Test for data out phase - STATUS & REQ_MASK should be REQ_DATAOUT, which is 0.
+*/
+ "\ttest $0xe, %%al
+ jnz 2f \n"
+/*
+ Test for REQ
+*/
+ "\ttest $0x10, %%al
+ jz 1b
+ lodsb
+ movb %%al, (%%edi)
+ loop 1b
+
+2:
+ ":
+/* output */
+"=S" (data), "=c" (len) :
+/* input */
+"0" (data), "1" (len) :
+/* clobbered */
+"eax", "ebx", "edi");
+}
+
+ if (!len && nobuffs) {
+ --nobuffs;
+ ++buffer;
+ len = buffer->length;
+ data = (unsigned char *) buffer->address;
+#if (DEBUG & DEBUG_SG)
+ printk("scsi%d : next scatter-gather buffer len = %d address = %08x\n",
+ hostno, len, data);
+#endif
+ }
+ break;
+
+ case REQ_DATAIN :
+#ifdef SLOW_HANDSHAKE
+ if (borken) {
+#if (DEBUG & (PHASE_DATAIN))
+ transfered += len;
+#endif
+ for (; len && (STATUS & (REQ_MASK | STAT_REQ)) == (REQ_DATAIN |
+ STAT_REQ); --len) {
+ *data++ = DATA;
+ borken_wait();
+}
+#if (DEBUG & (PHASE_DATAIN))
+ transfered -= len;
+#endif
+ } else
+#endif
+#ifdef FAST
+if (fast && transfersize && !(len % transfersize) && (len >= transfersize)
+#ifdef FAST32
+ && !(transfersize % 4)
+#endif
+ ) {
+#if (DEBUG & DEBUG_FAST)
+ printk("scsi%d : FAST transfer, underflow = %d, transfersize = %d\n"
+ " len = %d, data = %08x\n", hostno, SCint->underflow,
+ SCint->transfersize, len, data);
+#endif
+ __asm__("
+ cld;
+"
+#ifdef FAST32
+" shr $2, %%ecx;
+1: movl (%%esi), %%eax;
+ stosl;
+"
+#else
+"1: movb (%%esi), %%al;
+ stosb;
+"
+#endif
+
+" loop 1b;" : :
+ /* input */
+ "S" (st0x_dr), "D" (data), "c" (SCint->transfersize) :
+ /* clobbered */
+ "eax", "ecx", "edi");
+
+ len -= transfersize;
+ data += transfersize;
+
+#if (DEBUG & PHASE_DATAIN)
+ printk("scsi%d: transfered += %d\n", hostno, transfersize);
+ transfered += transfersize;
+#endif
+
+#if (DEBUG & DEBUG_FAST)
+ printk("scsi%d : FAST transfer complete len = %d data = %08x\n",
+ hostno, len, data);
+#endif
+
+} else
+#endif
+{
+
+#if (DEBUG & PHASE_DATAIN)
+ printk("scsi%d: transfered += %d\n", hostno, len);
+ transfered += len; /* Assume we'll transfer it all, then
+ subtract what we *didn't* transfer */
+#endif
+
+/*
+ * We loop as long as we are in a data in phase, there is room to read,
+ * and BSY is still active
+ */
+
+ __asm__ (
+/*
+ Local variables :
+ ecx = len
+ edi = data
+ esi = st0x_cr_sr
+ ebx = st0x_dr
+
+ Test for room to read
+*/
+ "\torl %%ecx, %%ecx
+ jz 2f
+
+ cld
+ movl " SYMBOL_NAME_STR(st0x_cr_sr) ", %%esi
+ movl " SYMBOL_NAME_STR(st0x_dr) ", %%ebx
+
+1: movb (%%esi), %%al\n"
+/*
+ Test for BSY
+*/
+
+ "\ttest $1, %%al
+ jz 2f\n"
+
+/*
+ Test for data in phase - STATUS & REQ_MASK should be REQ_DATAIN, = STAT_IO, which is 4.
+*/
+ "\tmovb $0xe, %%ah
+ andb %%al, %%ah
+ cmpb $0x04, %%ah
+ jne 2f\n"
+
+/*
+ Test for REQ
+*/
+ "\ttest $0x10, %%al
+ jz 1b
+
+ movb (%%ebx), %%al
+ stosb
+ loop 1b\n"
+
+"2:\n"
+ :
+/* output */
+"=D" (data), "=c" (len) :
+/* input */
+"0" (data), "1" (len) :
+/* clobbered */
+"eax","ebx", "esi");
+
+#if (DEBUG & PHASE_DATAIN)
+ printk("scsi%d: transfered -= %d\n", hostno, len);
+ transfered -= len; /* Since we assumed all of Len got
+ * transfered, correct our mistake */
+#endif
+}
+
+ if (!len && nobuffs) {
+ --nobuffs;
+ ++buffer;
+ len = buffer->length;
+ data = (unsigned char *) buffer->address;
+#if (DEBUG & DEBUG_SG)
+ printk("scsi%d : next scatter-gather buffer len = %d address = %08x\n",
+ hostno, len, data);
+#endif
+ }
+
+ break;
+
+ case REQ_CMDOUT :
+ while (((status_read = STATUS) & STAT_BSY) &&
+ ((status_read & REQ_MASK) == REQ_CMDOUT))
+ if (status_read & STAT_REQ) {
+ DATA = *(const unsigned char *) cmnd;
+ cmnd = 1+(const unsigned char *) cmnd;
+#ifdef SLOW_HANDSHAKE
+ if (borken)
+ borken_wait();
+#endif
+ }
+ break;
+
+ case REQ_STATIN :
+ status = DATA;
+ break;
+
+ case REQ_MSGOUT :
+/*
+ * We can only have sent a MSG OUT if we requested to do this
+ * by raising ATTN. So, we must drop ATTN.
+ */
+
+ CONTROL = BASE_CMD | CMD_DRVR_ENABLE;
+/*
+ * If we are reconnecting, then we must send an IDENTIFY message in
+ * response to MSGOUT.
+ */
+ switch (reselect) {
+ case CAN_RECONNECT:
+ DATA = IDENTIFY(1, lun);
+
+#if (DEBUG & (PHASE_RESELECT | PHASE_MSGOUT))
+ printk("scsi%d : sent IDENTIFY message.\n", hostno);
+#endif
+ break;
+#ifdef LINKED
+ case LINKED_WRONG:
+ DATA = ABORT;
+ linked_connected = 0;
+ reselect = CAN_RECONNECT;
+ goto connect_loop;
+#if (DEBUG & (PHASE_MSGOUT | DEBUG_LINKED))
+ printk("scsi%d : sent ABORT message to cancel incorrect I_T_L nexus.\n", hostno);
+#endif
+#endif /* LINKED */
+#if (DEBUG & DEBUG_LINKED)
+ printk("correct\n");
+#endif
+ default:
+ DATA = NOP;
+ printk("scsi%d : target %d requested MSGOUT, sent NOP message.\n", hostno, target);
+ }
+ break;
+
+ case REQ_MSGIN :
+ switch (message = DATA) {
+ case DISCONNECT :
+ should_reconnect = 1;
+ current_data = data; /* WDE add */
+ current_buffer = buffer;
+ current_bufflen = len; /* WDE add */
+ current_nobuffs = nobuffs;
+#ifdef LINKED
+ linked_connected = 0;
+#endif
+ done=1;
+#if (DEBUG & (PHASE_RESELECT | PHASE_MSGIN))
+ printk("scsi%d : disconnected.\n", hostno);
+#endif
+ break;
+
+#ifdef LINKED
+ case LINKED_CMD_COMPLETE:
+ case LINKED_FLG_CMD_COMPLETE:
+#endif
+ case COMMAND_COMPLETE :
+/*
+ * Note : we should check for underflow here.
+ */
+#if (DEBUG & PHASE_MSGIN)
+ printk("scsi%d : command complete.\n", hostno);
+#endif
+ done = 1;
+ break;
+ case ABORT :
+#if (DEBUG & PHASE_MSGIN)
+ printk("scsi%d : abort message.\n", hostno);
+#endif
+ done=1;
+ break;
+ case SAVE_POINTERS :
+ current_buffer = buffer;
+ current_bufflen = len; /* WDE add */
+ current_data = data; /* WDE mod */
+ current_nobuffs = nobuffs;
+#if (DEBUG & PHASE_MSGIN)
+ printk("scsi%d : pointers saved.\n", hostno);
+#endif
+ break;
+ case RESTORE_POINTERS:
+ buffer=current_buffer;
+ cmnd=current_cmnd;
+ data=current_data; /* WDE mod */
+ len=current_bufflen;
+ nobuffs=current_nobuffs;
+#if (DEBUG & PHASE_MSGIN)
+ printk("scsi%d : pointers restored.\n", hostno);
+#endif
+ break;
+ default:
+
+/*
+ * IDENTIFY distinguishes itself from the other messages by setting the
+ * high byte.
+ *
+ * Note : we need to handle at least one outstanding command per LUN,
+ * and need to hash the SCSI command for that I_T_L nexus based on the
+ * known ID (at this point) and LUN.
+ */
+
+ if (message & 0x80) {
+#if (DEBUG & PHASE_MSGIN)
+ printk("scsi%d : IDENTIFY message received from id %d, lun %d.\n",
+ hostno, target, message & 7);
+#endif
+ } else {
+
+/*
+ * We should go into a MESSAGE OUT phase, and send a MESSAGE_REJECT
+ * if we run into a message that we don't like. The seagate driver
+ * needs some serious restructuring first though.
+ */
+
+#if (DEBUG & PHASE_MSGIN)
+ printk("scsi%d : unknown message %d from target %d.\n",
+ hostno, message, target);
+#endif
+ }
+ }
+ break;
+
+ default :
+ printk("scsi%d : unknown phase.\n", hostno);
+ st0x_aborted = DID_ERROR;
+ }
+
+#ifdef SLOW_HANDSHAKE
+/*
+ * I really don't care to deal with borken devices in each single
+ * byte transfer case (ie, message in, message out, status), so
+ * I'll do the wait here if necessary.
+ */
+ if (borken)
+ borken_wait();
+#endif
+
+ } /* if ends */
+ } /* while ends */
+
+#if (DEBUG & (PHASE_DATAIN | PHASE_DATAOUT | PHASE_EXIT))
+ printk("scsi%d : Transfered %d bytes\n", hostno, transfered);
+#endif
+
+#if (DEBUG & PHASE_EXIT)
+#if 0 /* Doesn't work for scatter / gather */
+ printk("Buffer : \n");
+ for (i = 0; i < 20; ++i)
+ printk ("%02x ", ((unsigned char *) data)[i]); /* WDE mod */
+ printk("\n");
+#endif
+ printk("scsi%d : status = ", hostno);
+ print_status(status);
+ printk("message = %02x\n", message);
+#endif
+
+
+/* We shouldn't reach this until *after* BSY has been deasserted */
+#ifdef notyet
+ if (st0x_aborted) {
+ if (STATUS & STAT_BSY) {
+ seagate_st0x_reset(NULL);
+ st0x_aborted = DID_RESET;
+ }
+ abort_confirm = 1;
+ }
+#endif
+
+#ifdef LINKED
+else {
+/*
+ * Fix the message byte so that unsuspecting high level drivers don't
+ * puke when they see a LINKED COMMAND message in place of the COMMAND
+ * COMPLETE they may be expecting. Shouldn't be necessary, but it's
+ * better to be on the safe side.
+ *
+ * A non LINKED* message byte will indicate that the command completed,
+ * and we are now disconnected.
+ */
+
+ switch (message) {
+ case LINKED_CMD_COMPLETE :
+ case LINKED_FLG_CMD_COMPLETE :
+ message = COMMAND_COMPLETE;
+ linked_target = current_target;
+ linked_lun = current_lun;
+ linked_connected = 1;
+#if (DEBUG & DEBUG_LINKED)
+ printk("scsi%d : keeping I_T_L nexus established for linked command.\n",
+ hostno);
+#endif
+/*
+ * We also will need to adjust status to accommodate intermediate conditions.
+ */
+ if ((status == INTERMEDIATE_GOOD) ||
+ (status == INTERMEDIATE_C_GOOD))
+ status = GOOD;
+
+ break;
+/*
+ * We should also handle what are "normal" termination messages
+ * here (ABORT, BUS_DEVICE_RESET?, and COMMAND_COMPLETE individually,
+ * and flake if things aren't right.
+ */
+
+ default :
+#if (DEBUG & DEBUG_LINKED)
+ printk("scsi%d : closing I_T_L nexus.\n", hostno);
+#endif
+ linked_connected = 0;
+ }
+ }
+#endif /* LINKED */
+
+
+
+
+ if (should_reconnect) {
+#if (DEBUG & PHASE_RESELECT)
+ printk("scsi%d : exiting seagate_st0x_queue_command() with reconnect enabled.\n",
+ hostno);
+#endif
+ CONTROL = BASE_CMD | CMD_INTR ;
+ } else
+ CONTROL = BASE_CMD;
+
+ return retcode (st0x_aborted);
+ }
+
+int seagate_st0x_abort (Scsi_Cmnd * SCpnt)
+ {
+ st0x_aborted = DID_ABORT;
+
+ return SCSI_ABORT_PENDING;
+ }
+
+/*
+ the seagate_st0x_reset function resets the SCSI bus
+*/
+
+int seagate_st0x_reset (Scsi_Cmnd * SCpnt, unsigned int reset_flags)
+ {
+ unsigned clock;
+ /*
+ No timeouts - this command is going to fail because
+ it was reset.
+ */
+
+#ifdef DEBUG
+ printk("In seagate_st0x_reset()\n");
+#endif
+
+
+ /* assert RESET signal on SCSI bus. */
+
+ CONTROL = BASE_CMD | CMD_RST;
+ clock=jiffies+2;
+
+
+ /* Wait. */
+
+ while (jiffies < clock);
+
+ CONTROL = BASE_CMD;
+
+ st0x_aborted = DID_RESET;
+
+#ifdef DEBUG
+ printk("SCSI bus reset.\n");
+#endif
+ return SCSI_RESET_WAKEUP;
+ }
+
+#include <asm/segment.h>
+#include "sd.h"
+#include <scsi/scsi_ioctl.h>
+
+int seagate_st0x_biosparam(Disk * disk, kdev_t dev, int* ip) {
+ unsigned char buf[256 + sizeof(int) * 2], cmd[6], *data, *page;
+ int *sizes, result, formatted_sectors, total_sectors;
+ int cylinders, heads, sectors;
+ int capacity;
+
+/*
+ * Only SCSI-I CCS drives and later implement the necessary mode sense
+ * pages.
+ */
+
+ if (disk->device->scsi_level < 2)
+ return -1;
+
+ sizes = (int *) buf;
+ data = (unsigned char *) (sizes + 2);
+
+ cmd[0] = MODE_SENSE;
+ cmd[1] = (disk->device->lun << 5) & 0xe5;
+ cmd[2] = 0x04; /* Read page 4, rigid disk geometry page current values */
+ cmd[3] = 0;
+ cmd[4] = 255;
+ cmd[5] = 0;
+
+/*
+ * We are transferring 0 bytes in the out direction, and expect to get back
+ * 24 bytes for each mode page.
+ */
+
+ sizes[0] = 0;
+ sizes[1] = 256;
+
+ memcpy (data, cmd, 6);
+
+ if (!(result = kernel_scsi_ioctl (disk->device, SCSI_IOCTL_SEND_COMMAND, (void *) buf))) {
+/*
+ * The mode page lies beyond the MODE SENSE header, with length 4, and
+ * the BLOCK DESCRIPTOR, with length header[3].
+ */
+
+ page = data + 4 + data[3];
+ heads = (int) page[5];
+ cylinders = (page[2] << 16) | (page[3] << 8) | page[4];
+
+ cmd[2] = 0x03; /* Read page 3, format page current values */
+ memcpy (data, cmd, 6);
+
+ if (!(result = kernel_scsi_ioctl (disk->device, SCSI_IOCTL_SEND_COMMAND, (void *) buf))) {
+ page = data + 4 + data[3];
+ sectors = (page[10] << 8) | page[11];
+
+
+/*
+ * Get the total number of formatted sectors from the block descriptor,
+ * so we can tell how many are being used for alternates.
+ */
+
+ formatted_sectors = (data[4 + 1] << 16) | (data[4 + 2] << 8) |
+ data[4 + 3] ;
+
+ total_sectors = (heads * cylinders * sectors);
+
+/*
+ * Adjust the real geometry by subtracting
+ * (spare sectors / (heads * tracks)) cylinders from the number of cylinders.
+ *
+ * It appears that the CE cylinder CAN be a partial cylinder.
+ */
+
+
+printk("scsi%d : heads = %d cylinders = %d sectors = %d total = %d formatted = %d\n",
+ hostno, heads, cylinders, sectors, total_sectors, formatted_sectors);
+
+ if (!heads || !sectors || !cylinders)
+ result = -1;
+ else
+ cylinders -= ((total_sectors - formatted_sectors) / (heads * sectors));
+
+/*
+ * Now, we need to do a sanity check on the geometry to see if it is
+ * BIOS compatible. The maximum BIOS geometry is 1024 cylinders *
+ * 256 heads * 64 sectors.
+ */
+
+ if ((cylinders > 1024) || (sectors > 64)) {
+ /* The Seagate's seem to have some mapping
+ * Multiple heads * sectors * cyl to get capacity
+ * Then start rounding down. */
+ capacity = heads * sectors * cylinders;
+ sectors = 17; /* Old MFM Drives use this, so does the Seagate */
+ heads = 2;
+ capacity = capacity / sectors;
+ while (cylinders > 1024)
+ {
+ heads *= 2; /* For some reason, they go in multiples */
+ cylinders = capacity / heads;
+ }
+ }
+ ip[0] = heads;
+ ip[1] = sectors;
+ ip[2] = cylinders;
+
+/*
+ * There should be an alternate mapping for things the seagate doesn't
+ * understand, but I couldn't say what it is with reasonable certainty.
+ */
+
+ }
+ }
+
+ return result;
+}
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = SEAGATE_ST0X;
+
+#include "scsi_module.c"
+#endif
diff --git a/linux/dev/drivers/scsi/sr.c b/linux/dev/drivers/scsi/sr.c
new file mode 100644
index 0000000..8f44246
--- /dev/null
+++ b/linux/dev/drivers/scsi/sr.c
@@ -0,0 +1,1290 @@
+/*
+ * sr.c Copyright (C) 1992 David Giller
+ * Copyright (C) 1993, 1994, 1995 Eric Youngdale
+ *
+ * adapted from:
+ * sd.c Copyright (C) 1992 Drew Eckhardt
+ * Linux scsi disk driver by
+ * Drew Eckhardt <drew@colorado.edu>
+ *
+ * Modified by Eric Youngdale ericy@cais.com to
+ * add scatter-gather, multiple outstanding request, and other
+ * enhancements.
+ *
+ * Modified by Eric Youngdale eric@aib.com to support loadable
+ * low-level scsi drivers.
+ *
+ * Modified by Thomas Quinot thomas@melchior.cuivre.fdn.fr to
+ * provide auto-eject.
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/cdrom.h>
+#include <linux/interrupt.h>
+#include <asm/system.h>
+
+#define MAJOR_NR SCSI_CDROM_MAJOR
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+#include "sr.h"
+#include <scsi/scsi_ioctl.h> /* For the door lock/unlock commands */
+#include "constants.h"
+
+#define MAX_RETRIES 3
+#define SR_TIMEOUT (30 * HZ)
+
+static int sr_init(void);
+static void sr_finish(void);
+static int sr_attach(Scsi_Device *);
+static int sr_detect(Scsi_Device *);
+static void sr_detach(Scsi_Device *);
+
+struct Scsi_Device_Template sr_template = {NULL, "cdrom", "sr", NULL, TYPE_ROM,
+ SCSI_CDROM_MAJOR, 0, 0, 0, 1,
+ sr_detect, sr_init,
+ sr_finish, sr_attach, sr_detach};
+
+Scsi_CD * scsi_CDs = NULL;
+static int * sr_sizes;
+
+static int * sr_blocksizes;
+
+static int sr_open(struct inode *, struct file *);
+void get_sectorsize(int);
+void sr_photocd(struct inode *);
+
+extern int sr_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
+
+void requeue_sr_request (Scsi_Cmnd * SCpnt);
+static int check_cdrom_media_change(kdev_t);
+
+static void sr_release(struct inode * inode, struct file * file)
+{
+ sync_dev(inode->i_rdev);
+ if(! --scsi_CDs[MINOR(inode->i_rdev)].device->access_count)
+ {
+ sr_ioctl(inode, NULL, SCSI_IOCTL_DOORUNLOCK, 0);
+ if (scsi_CDs[MINOR(inode->i_rdev)].auto_eject)
+ sr_ioctl(inode, NULL, CDROMEJECT, 0);
+ }
+ if (scsi_CDs[MINOR(inode->i_rdev)].device->host->hostt->usage_count)
+ (*scsi_CDs[MINOR(inode->i_rdev)].device->host->hostt->usage_count)--;
+ if(sr_template.usage_count) (*sr_template.usage_count)--;
+}
+
+static struct file_operations sr_fops =
+{
+ NULL, /* lseek - default */
+ block_read, /* read - general block-dev read */
+ block_write, /* write - general block-dev write */
+ NULL, /* readdir - bad */
+ NULL, /* select */
+ sr_ioctl, /* ioctl */
+ NULL, /* mmap */
+ sr_open, /* special open code */
+ sr_release, /* release */
+ NULL, /* fsync */
+ NULL, /* fasync */
+ check_cdrom_media_change, /* Disk change */
+ NULL /* revalidate */
+};
+
+/*
+ * This function checks to see if the media has been changed in the
+ * CDROM drive. It is possible that we have already sensed a change,
+ * or the drive may have sensed one and not yet reported it. We must
+ * be ready for either case. This function always reports the current
+ * value of the changed bit. If flag is 0, then the changed bit is reset.
+ * This function could be done as an ioctl, but we would need to have
+ * an inode for that to work, and we do not always have one.
+ */
+
+int check_cdrom_media_change(kdev_t full_dev){
+ int retval, target;
+ struct inode inode;
+ int flag = 0;
+
+ target = MINOR(full_dev);
+
+ if (target >= sr_template.nr_dev) {
+ printk("CD-ROM request error: invalid device.\n");
+ return 0;
+ };
+
+ inode.i_rdev = full_dev; /* This is all we really need here */
+ retval = sr_ioctl(&inode, NULL, SCSI_IOCTL_TEST_UNIT_READY, 0);
+
+ if(retval){ /* Unable to test, unit probably not ready. This usually
+ * means there is no disc in the drive. Mark as changed,
+ * and we will figure it out later once the drive is
+ * available again. */
+
+ scsi_CDs[target].device->changed = 1;
+ return 1; /* This will force a flush, if called from
+ * check_disk_change */
+ };
+
+ retval = scsi_CDs[target].device->changed;
+ if(!flag) {
+ scsi_CDs[target].device->changed = 0;
+ /* If the disk changed, the capacity will now be different,
+ * so we force a re-read of this information */
+ if (retval) scsi_CDs[target].needs_sector_size = 1;
+ };
+ return retval;
+}
+
+/*
+ * rw_intr is the interrupt routine for the device driver. It will be notified on the
+ * end of a SCSI read / write, and will take on of several actions based on success or failure.
+ */
+
+static void rw_intr (Scsi_Cmnd * SCpnt)
+{
+ int result = SCpnt->result;
+ int this_count = SCpnt->this_count;
+ int good_sectors = (result == 0 ? this_count : 0);
+ int block_sectors = 0;
+
+#ifdef DEBUG
+ printk("sr.c done: %x %x\n",result, SCpnt->request.bh->b_data);
+#endif
+ /*
+ Handle MEDIUM ERRORs or VOLUME OVERFLOWs that indicate partial success.
+ Since this is a relatively rare error condition, no care is taken to
+ avoid unnecessary additional work such as memcpy's that could be avoided.
+ */
+
+ if (driver_byte(result) != 0 && /* An error occurred */
+ SCpnt->sense_buffer[0] == 0xF0 && /* Sense data is valid */
+ (SCpnt->sense_buffer[2] == MEDIUM_ERROR ||
+ SCpnt->sense_buffer[2] == VOLUME_OVERFLOW ||
+ SCpnt->sense_buffer[2] == ILLEGAL_REQUEST))
+ {
+ long error_sector = (SCpnt->sense_buffer[3] << 24) |
+ (SCpnt->sense_buffer[4] << 16) |
+ (SCpnt->sense_buffer[5] << 8) |
+ SCpnt->sense_buffer[6];
+ int device_nr = DEVICE_NR(SCpnt->request.rq_dev);
+ if (SCpnt->request.bh != NULL)
+ block_sectors = SCpnt->request.bh->b_size >> 9;
+ if (block_sectors < 4) block_sectors = 4;
+ if (scsi_CDs[device_nr].sector_size == 2048)
+ error_sector <<= 2;
+ error_sector &= ~ (block_sectors - 1);
+ good_sectors = error_sector - SCpnt->request.sector;
+ if (good_sectors < 0 || good_sectors >= this_count)
+ good_sectors = 0;
+ /*
+ The SCSI specification allows for the value returned by READ
+ CAPACITY to be up to 75 2K sectors past the last readable
+ block. Therefore, if we hit a medium error within the last
+ 75 2K sectors, we decrease the saved size value.
+ */
+ if ((error_sector >> 1) < sr_sizes[device_nr] &&
+ scsi_CDs[device_nr].capacity - error_sector < 4*75)
+ sr_sizes[device_nr] = error_sector >> 1;
+ }
+
+ if (good_sectors > 0)
+ { /* Some sectors were read successfully. */
+ if (SCpnt->use_sg == 0) {
+ if (SCpnt->buffer != SCpnt->request.buffer)
+ {
+ int offset;
+ offset = (SCpnt->request.sector % 4) << 9;
+ memcpy((char *)SCpnt->request.buffer,
+ (char *)SCpnt->buffer + offset,
+ good_sectors << 9);
+ /* Even though we are not using scatter-gather, we look
+ * ahead and see if there is a linked request for the
+ * other half of this buffer. If there is, then satisfy
+ * it. */
+ if((offset == 0) && good_sectors == 2 &&
+ SCpnt->request.nr_sectors > good_sectors &&
+ SCpnt->request.bh &&
+ SCpnt->request.bh->b_reqnext &&
+ SCpnt->request.bh->b_reqnext->b_size == 1024) {
+ memcpy((char *)SCpnt->request.bh->b_reqnext->b_data,
+ (char *)SCpnt->buffer + 1024,
+ 1024);
+ good_sectors += 2;
+ };
+
+ scsi_free(SCpnt->buffer, 2048);
+ }
+ } else {
+ struct scatterlist * sgpnt;
+ int i;
+ sgpnt = (struct scatterlist *) SCpnt->buffer;
+ for(i=0; i<SCpnt->use_sg; i++) {
+ if (sgpnt[i].alt_address) {
+ if (sgpnt[i].alt_address != sgpnt[i].address) {
+ memcpy(sgpnt[i].alt_address, sgpnt[i].address, sgpnt[i].length);
+ };
+ scsi_free(sgpnt[i].address, sgpnt[i].length);
+ };
+ };
+ scsi_free(SCpnt->buffer, SCpnt->sglist_len); /* Free list of scatter-gather pointers */
+ if(SCpnt->request.sector % 4) good_sectors -= 2;
+ /* See if there is a padding record at the end that needs to be removed */
+ if(good_sectors > SCpnt->request.nr_sectors)
+ good_sectors -= 2;
+ };
+
+#ifdef DEBUG
+ printk("(%x %x %x) ",SCpnt->request.bh, SCpnt->request.nr_sectors,
+ good_sectors);
+#endif
+ if (SCpnt->request.nr_sectors > this_count)
+ {
+ SCpnt->request.errors = 0;
+ if (!SCpnt->request.bh)
+ panic("sr.c: linked page request (%lx %x)",
+ SCpnt->request.sector, this_count);
+ }
+
+ SCpnt = end_scsi_request(SCpnt, 1, good_sectors); /* All done */
+ if (result == 0)
+ {
+ requeue_sr_request(SCpnt);
+ return;
+ }
+ }
+
+ if (good_sectors == 0) {
+ /* We only come through here if no sectors were read successfully. */
+
+ /* Free up any indirection buffers we allocated for DMA purposes. */
+ if (SCpnt->use_sg) {
+ struct scatterlist * sgpnt;
+ int i;
+ sgpnt = (struct scatterlist *) SCpnt->buffer;
+ for(i=0; i<SCpnt->use_sg; i++) {
+ if (sgpnt[i].alt_address) {
+ scsi_free(sgpnt[i].address, sgpnt[i].length);
+ }
+ }
+ scsi_free(SCpnt->buffer, SCpnt->sglist_len); /* Free list of scatter-gather pointers */
+ } else {
+ if (SCpnt->buffer != SCpnt->request.buffer)
+ scsi_free(SCpnt->buffer, SCpnt->bufflen);
+ }
+
+ }
+
+ if (driver_byte(result) != 0) {
+ if ((SCpnt->sense_buffer[0] & 0x7f) == 0x70) {
+ if ((SCpnt->sense_buffer[2] & 0xf) == UNIT_ATTENTION) {
+ /* detected disc change. set a bit and quietly refuse
+ * further access. */
+
+ scsi_CDs[DEVICE_NR(SCpnt->request.rq_dev)].device->changed = 1;
+ SCpnt = end_scsi_request(SCpnt, 0, this_count);
+ requeue_sr_request(SCpnt);
+ return;
+ }
+ }
+
+ if (SCpnt->sense_buffer[2] == ILLEGAL_REQUEST) {
+ printk("CD-ROM error: ");
+ print_sense("sr", SCpnt);
+ printk("command was: ");
+ print_command(SCpnt->cmnd);
+ if (scsi_CDs[DEVICE_NR(SCpnt->request.rq_dev)].ten) {
+ scsi_CDs[DEVICE_NR(SCpnt->request.rq_dev)].ten = 0;
+ requeue_sr_request(SCpnt);
+ result = 0;
+ return;
+ } else {
+ SCpnt = end_scsi_request(SCpnt, 0, this_count);
+ requeue_sr_request(SCpnt); /* Do next request */
+ return;
+ }
+
+ }
+
+ if (SCpnt->sense_buffer[2] == NOT_READY) {
+ printk("CD-ROM not ready. Make sure you have a disc in the drive.\n");
+ SCpnt = end_scsi_request(SCpnt, 0, this_count);
+ requeue_sr_request(SCpnt); /* Do next request */
+ return;
+ }
+
+ if (SCpnt->sense_buffer[2] == MEDIUM_ERROR) {
+ printk("scsi%d: MEDIUM ERROR on "
+ "channel %d, id %d, lun %d, CDB: ",
+ SCpnt->host->host_no, (int) SCpnt->channel,
+ (int) SCpnt->target, (int) SCpnt->lun);
+ print_command(SCpnt->cmnd);
+ print_sense("sr", SCpnt);
+ SCpnt = end_scsi_request(SCpnt, 0, block_sectors);
+ requeue_sr_request(SCpnt);
+ return;
+ }
+
+ if (SCpnt->sense_buffer[2] == VOLUME_OVERFLOW) {
+ printk("scsi%d: VOLUME OVERFLOW on "
+ "channel %d, id %d, lun %d, CDB: ",
+ SCpnt->host->host_no, (int) SCpnt->channel,
+ (int) SCpnt->target, (int) SCpnt->lun);
+ print_command(SCpnt->cmnd);
+ print_sense("sr", SCpnt);
+ SCpnt = end_scsi_request(SCpnt, 0, block_sectors);
+ requeue_sr_request(SCpnt);
+ return;
+ }
+ }
+
+ /* We only get this far if we have an error we have not recognized */
+ if(result) {
+ printk("SCSI CD error : host %d id %d lun %d return code = %03x\n",
+ scsi_CDs[DEVICE_NR(SCpnt->request.rq_dev)].device->host->host_no,
+ scsi_CDs[DEVICE_NR(SCpnt->request.rq_dev)].device->id,
+ scsi_CDs[DEVICE_NR(SCpnt->request.rq_dev)].device->lun,
+ result);
+
+ if (status_byte(result) == CHECK_CONDITION)
+ print_sense("sr", SCpnt);
+
+ SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.current_nr_sectors);
+ requeue_sr_request(SCpnt);
+ }
+}
+
+/*
+ * Here I tried to implement support for multisession-CD's
+ *
+ * Much of this has do be done with vendor-specific SCSI-commands, because
+ * multisession is newer than the SCSI-II standard.
+ * So I have to complete it step by step. Useful information is welcome.
+ *
+ * Actually works:
+ * - NEC: Detection and support of multisession CD's. Special handling
+ * for XA-disks is not necessary.
+ *
+ * - TOSHIBA: setting density is done here now, mounting PhotoCD's should
+ * work now without running the program "set_density"
+ * Multisession CD's are supported too.
+ *
+ * Gerd Knorr <kraxel@cs.tu-berlin.de>
+ */
+/*
+ * 19950704 operator@melchior.cuivre.fdn.fr (Thomas Quinot)
+ *
+ * - SONY: Same as Nec.
+ *
+ * - PIONEER: works with SONY code (may be others too ?)
+ */
+
+void sr_photocd(struct inode *inode)
+{
+ unsigned long sector,min,sec,frame;
+ unsigned char buf[40]; /* the buffer for the ioctl */
+ unsigned char *cmd; /* the scsi-command */
+ unsigned char *send; /* the data we send to the drive ... */
+ unsigned char *rec; /* ... and get back */
+ int rc,is_xa,no_multi;
+
+ if (scsi_CDs[MINOR(inode->i_rdev)].xa_flags & 0x02) {
+#ifdef DEBUG
+ printk(KERN_DEBUG "sr_photocd: CDROM and/or driver do not support multisession CD's");
+#endif
+ return;
+ }
+
+ if (!suser()) {
+ /* I'm not the superuser, so SCSI_IOCTL_SEND_COMMAND isn't allowed
+ * for me. That's why mpcd_sector will be initialized with zero,
+ * because I'm not able to get the right value. Necessary only if
+ * access_count is 1, else no disk change happened since the last
+ * call of this function and we can keep the old value.
+ */
+ if (1 == scsi_CDs[MINOR(inode->i_rdev)].device->access_count) {
+ scsi_CDs[MINOR(inode->i_rdev)].mpcd_sector = 0;
+ scsi_CDs[MINOR(inode->i_rdev)].xa_flags &= ~0x01;
+ }
+ return;
+ }
+
+ sector = 0;
+ is_xa = 0;
+ no_multi = 0;
+ cmd = rec = &buf[8];
+
+ switch(scsi_CDs[MINOR(inode->i_rdev)].device->manufacturer) {
+
+ case SCSI_MAN_NEC:
+#ifdef DEBUG
+ printk(KERN_DEBUG "sr_photocd: use NEC code\n");
+#endif
+ memset(buf,0,40);
+ *((unsigned int*)buf) = 0x0; /* we send nothing... */
+ *((unsigned int*)buf+1) = 0x16; /* and receive 0x16 bytes */
+ cmd[0] = 0xde;
+ cmd[1] = 0x03;
+ cmd[2] = 0xb0;
+ rc = kernel_scsi_ioctl(scsi_CDs[MINOR(inode->i_rdev)].device,
+ SCSI_IOCTL_SEND_COMMAND, buf);
+ if (rc != 0) {
+ if (rc != 0x28000002) /* drop "not ready" */
+ printk(KERN_WARNING"sr_photocd: ioctl error (NEC): 0x%x\n",rc);
+ break;
+ }
+ if (rec[14] != 0 && rec[14] != 0xb0) {
+ printk(KERN_INFO"sr_photocd: (NEC) Hmm, seems the CDROM doesn't support multisession CD's\n");
+ no_multi = 1;
+ break;
+ }
+ min = (unsigned long) rec[15]/16*10 + (unsigned long) rec[15]%16;
+ sec = (unsigned long) rec[16]/16*10 + (unsigned long) rec[16]%16;
+ frame = (unsigned long) rec[17]/16*10 + (unsigned long) rec[17]%16;
+ sector = min*CD_SECS*CD_FRAMES + sec*CD_FRAMES + frame;
+ is_xa = (rec[14] == 0xb0);
+#ifdef DEBUG
+ if (sector) {
+ printk(KERN_DEBUG "sr_photocd: multisession CD detected. start: %lu\n",sector);
+ }
+#endif
+ break;
+
+ case SCSI_MAN_TOSHIBA:
+#ifdef DEBUG
+ printk(KERN_DEBUG "sr_photocd: use TOSHIBA code\n");
+#endif
+
+ /* we request some disc information (is it a XA-CD ?,
+ * where starts the last session ?) */
+ memset(buf,0,40);
+ *((unsigned int*)buf) = (unsigned int) 0;
+ *((unsigned int*)buf+1) = (unsigned int) 4; /* receive 4 bytes */
+ cmd[0] = (unsigned char) 0x00c7;
+ cmd[1] = (unsigned char) 3;
+ rc = kernel_scsi_ioctl(scsi_CDs[MINOR(inode->i_rdev)].device,
+ SCSI_IOCTL_SEND_COMMAND, buf);
+ if (rc != 0) {
+ if (rc == 0x28000002) {
+ /* Got a "not ready" - error. No chance to find out if this is
+ * because there is no CD in the drive or because the drive
+ * don't knows multisession CD's. So I need to do an extra
+ * check... */
+ if (!kernel_scsi_ioctl(scsi_CDs[MINOR(inode->i_rdev)].device,
+ SCSI_IOCTL_TEST_UNIT_READY, NULL)) {
+ printk(KERN_INFO "sr_photocd: (TOSHIBA) Hmm, seems the CDROM doesn't support multisession CD's\n");
+ no_multi = 1;
+ }
+ } else
+ printk(KERN_INFO"sr_photocd: ioctl error (TOSHIBA #1): 0x%x\n",rc);
+ break; /* if the first ioctl fails, we don't call the second one */
+ }
+ is_xa = (rec[0] == 0x20);
+ min = (unsigned long) rec[1]/16*10 + (unsigned long) rec[1]%16;
+ sec = (unsigned long) rec[2]/16*10 + (unsigned long) rec[2]%16;
+ frame = (unsigned long) rec[3]/16*10 + (unsigned long) rec[3]%16;
+ sector = min*CD_SECS*CD_FRAMES + sec*CD_FRAMES + frame;
+ if (sector) {
+ sector -= CD_BLOCK_OFFSET;
+#ifdef DEBUG
+ printk(KERN_DEBUG "sr_photocd: multisession CD detected: start: %lu\n",sector);
+#endif
+ }
+
+ /* now we do a get_density... */
+ memset(buf,0,40);
+ *((unsigned int*)buf) = (unsigned int) 0;
+ *((unsigned int*)buf+1) = (unsigned int) 12;
+ cmd[0] = (unsigned char) MODE_SENSE;
+ cmd[2] = (unsigned char) 1;
+ cmd[4] = (unsigned char) 12;
+ rc = kernel_scsi_ioctl(scsi_CDs[MINOR(inode->i_rdev)].device,
+ SCSI_IOCTL_SEND_COMMAND, buf);
+ if (rc != 0) {
+ printk(KERN_WARNING "sr_photocd: ioctl error (TOSHIBA #2): 0x%x\n",rc);
+ break;
+ }
+#ifdef DEBUG
+ printk(KERN_DEBUG "sr_photocd: get_density: 0x%x\n",rec[4]);
+#endif
+
+ /* ...and only if necessary a set_density */
+ if ((rec[4] != 0x81 && is_xa) || (rec[4] != 0 && !is_xa)) {
+#ifdef DEBUG
+ printk(KERN_DEBUG "sr_photocd: doing set_density\n");
+#endif
+ memset(buf,0,40);
+ *((unsigned int*)buf) = (unsigned int) 12; /* send 12 bytes */
+ *((unsigned int*)buf+1) = (unsigned int) 0;
+ cmd[0] = (unsigned char) MODE_SELECT;
+ cmd[1] = (unsigned char) (1 << 4);
+ cmd[4] = (unsigned char) 12;
+ send = &cmd[6]; /* this is a 6-Byte command */
+ send[ 3] = (unsigned char) 0x08; /* data for cmd */
+ /* density 0x81 for XA, 0 else */
+ send[ 4] = (is_xa) ?
+ (unsigned char) 0x81 : (unsigned char) 0;
+ send[10] = (unsigned char) 0x08;
+ rc = kernel_scsi_ioctl(scsi_CDs[MINOR(inode->i_rdev)].device,
+ SCSI_IOCTL_SEND_COMMAND, buf);
+ if (rc != 0) {
+ printk(KERN_WARNING "sr_photocd: ioctl error (TOSHIBA #3): 0x%x\n",rc);
+ }
+ /* The set_density command may have changed the
+ * sector size or capacity. */
+ scsi_CDs[MINOR(inode->i_rdev)].needs_sector_size = 1;
+ }
+ break;
+
+ case SCSI_MAN_SONY: /* Thomas QUINOT <thomas@melchior.cuivre.fdn.fr> */
+ case SCSI_MAN_PIONEER:
+ case SCSI_MAN_UNKNOWN:
+#ifdef DEBUG
+ printk(KERN_DEBUG "sr_photocd: use SONY/PIONEER code\n");
+#endif
+ get_sectorsize(MINOR(inode->i_rdev)); /* spinup (avoid timeout) */
+ memset(buf,0,40);
+ *((unsigned int*)buf) = 0x0; /* we send nothing... */
+ *((unsigned int*)buf+1) = 0x0c; /* and receive 0x0c bytes */
+ cmd[0] = READ_TOC;
+ cmd[8] = 0x0c;
+ cmd[9] = 0x40;
+ rc = kernel_scsi_ioctl(scsi_CDs[MINOR(inode->i_rdev)].device,
+ SCSI_IOCTL_SEND_COMMAND, buf);
+
+ if (rc != 0) {
+ if (rc != 0x28000002) /* drop "not ready" */
+ printk(KERN_WARNING "sr_photocd: ioctl error (SONY/PIONEER): 0x%x\n",rc);
+ break;
+ }
+ if ((rec[0] << 8) + rec[1] < 0x0a) {
+ printk(KERN_INFO "sr_photocd: (SONY/PIONEER) Hmm, seems the CDROM doesn't support multisession CD's\n");
+ no_multi = 1;
+ break;
+ }
+ sector = rec[11] + (rec[10] << 8) + (rec[9] << 16) + (rec[8] << 24);
+ is_xa = !!sector;
+#ifdef DEBUG
+ if (sector)
+ printk (KERN_DEBUG "sr_photocd: multisession CD detected. start: %lu\n",sector);
+#endif
+ break;
+
+ case SCSI_MAN_NEC_OLDCDR:
+ default:
+ sector = 0;
+ no_multi = 1;
+ break; }
+
+ scsi_CDs[MINOR(inode->i_rdev)].mpcd_sector = sector;
+ if (is_xa)
+ scsi_CDs[MINOR(inode->i_rdev)].xa_flags |= 0x01;
+ else
+ scsi_CDs[MINOR(inode->i_rdev)].xa_flags &= ~0x01;
+ if (no_multi)
+ scsi_CDs[MINOR(inode->i_rdev)].xa_flags |= 0x02;
+ return;
+}
+
+static int sr_open(struct inode * inode, struct file * filp)
+{
+ if(MINOR(inode->i_rdev) >= sr_template.nr_dev ||
+ !scsi_CDs[MINOR(inode->i_rdev)].device) return -ENXIO; /* No such device */
+
+ if (filp->f_mode & 2)
+ return -EROFS;
+
+ if(sr_template.usage_count) (*sr_template.usage_count)++;
+
+ sr_ioctl(inode,filp,CDROMCLOSETRAY,0);
+ check_disk_change(inode->i_rdev);
+
+ if(!scsi_CDs[MINOR(inode->i_rdev)].device->access_count++)
+ sr_ioctl(inode, NULL, SCSI_IOCTL_DOORLOCK, 0);
+ if (scsi_CDs[MINOR(inode->i_rdev)].device->host->hostt->usage_count)
+ (*scsi_CDs[MINOR(inode->i_rdev)].device->host->hostt->usage_count)++;
+
+ sr_photocd(inode);
+
+ /* If this device did not have media in the drive at boot time, then
+ * we would have been unable to get the sector size. Check to see if
+ * this is the case, and try again.
+ */
+
+ if(scsi_CDs[MINOR(inode->i_rdev)].needs_sector_size)
+ get_sectorsize(MINOR(inode->i_rdev));
+
+ return 0;
+}
+
+
+/*
+ * do_sr_request() is the request handler function for the sr driver.
+ * Its function in life is to take block device requests, and
+ * translate them to SCSI commands.
+ */
+
+static void do_sr_request (void)
+{
+ Scsi_Cmnd * SCpnt = NULL;
+ struct request * req = NULL;
+ Scsi_Device * SDev;
+ unsigned long flags;
+ int flag = 0;
+
+ while (1==1){
+ save_flags(flags);
+ cli();
+ if (CURRENT != NULL && CURRENT->rq_status == RQ_INACTIVE) {
+ restore_flags(flags);
+ return;
+ };
+
+ INIT_SCSI_REQUEST;
+
+ SDev = scsi_CDs[DEVICE_NR(CURRENT->rq_dev)].device;
+
+ /*
+ * I am not sure where the best place to do this is. We need
+ * to hook in a place where we are likely to come if in user
+ * space.
+ */
+ if( SDev->was_reset )
+ {
+ /*
+ * We need to relock the door, but we might
+ * be in an interrupt handler. Only do this
+ * from user space, since we do not want to
+ * sleep from an interrupt.
+ */
+ if( SDev->removable && !intr_count )
+ {
+ scsi_ioctl(SDev, SCSI_IOCTL_DOORLOCK, 0);
+ }
+ SDev->was_reset = 0;
+ }
+
+ if (flag++ == 0)
+ SCpnt = allocate_device(&CURRENT,
+ scsi_CDs[DEVICE_NR(CURRENT->rq_dev)].device, 0);
+ else SCpnt = NULL;
+ restore_flags(flags);
+
+ /* This is a performance enhancement. We dig down into the request list and
+ * try to find a queueable request (i.e. device not busy, and host able to
+ * accept another command. If we find one, then we queue it. This can
+ * make a big difference on systems with more than one disk drive. We want
+ * to have the interrupts off when monkeying with the request list, because
+ * otherwise the kernel might try to slip in a request in between somewhere. */
+
+ if (!SCpnt && sr_template.nr_dev > 1){
+ struct request *req1;
+ req1 = NULL;
+ save_flags(flags);
+ cli();
+ req = CURRENT;
+ while(req){
+ SCpnt = request_queueable(req,
+ scsi_CDs[DEVICE_NR(req->rq_dev)].device);
+ if(SCpnt) break;
+ req1 = req;
+ req = req->next;
+ };
+ if (SCpnt && req->rq_status == RQ_INACTIVE) {
+ if (req == CURRENT)
+ CURRENT = CURRENT->next;
+ else
+ req1->next = req->next;
+ };
+ restore_flags(flags);
+ };
+
+ if (!SCpnt)
+ return; /* Could not find anything to do */
+
+ wake_up(&wait_for_request);
+
+ /* Queue command */
+ requeue_sr_request(SCpnt);
+ }; /* While */
+}
+
+void requeue_sr_request (Scsi_Cmnd * SCpnt)
+{
+ unsigned int dev, block, realcount;
+ unsigned char cmd[10], *buffer, tries;
+ int this_count, start, end_rec;
+
+ tries = 2;
+
+ repeat:
+ if(!SCpnt || SCpnt->request.rq_status == RQ_INACTIVE) {
+ do_sr_request();
+ return;
+ }
+
+ dev = MINOR(SCpnt->request.rq_dev);
+ block = SCpnt->request.sector;
+ buffer = NULL;
+ this_count = 0;
+
+ if (dev >= sr_template.nr_dev) {
+ /* printk("CD-ROM request error: invalid device.\n"); */
+ SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.nr_sectors);
+ tries = 2;
+ goto repeat;
+ }
+
+ if (!scsi_CDs[dev].use) {
+ /* printk("CD-ROM request error: device marked not in use.\n"); */
+ SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.nr_sectors);
+ tries = 2;
+ goto repeat;
+ }
+
+ if (scsi_CDs[dev].device->changed) {
+ /*
+ * quietly refuse to do anything to a changed disc
+ * until the changed bit has been reset
+ */
+ /* printk("CD-ROM has been changed. Prohibiting further I/O.\n"); */
+ SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.nr_sectors);
+ tries = 2;
+ goto repeat;
+ }
+
+ switch (SCpnt->request.cmd)
+ {
+ case WRITE:
+ SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.nr_sectors);
+ goto repeat;
+ break;
+ case READ :
+ cmd[0] = READ_6;
+ break;
+ default :
+ panic ("Unknown sr command %d\n", SCpnt->request.cmd);
+ }
+
+ cmd[1] = (SCpnt->lun << 5) & 0xe0;
+
+ /*
+ * Now do the grungy work of figuring out which sectors we need, and
+ * where in memory we are going to put them.
+ *
+ * The variables we need are:
+ *
+ * this_count= number of 512 byte sectors being read
+ * block = starting cdrom sector to read.
+ * realcount = # of cdrom sectors to read
+ *
+ * The major difference between a scsi disk and a scsi cdrom
+ * is that we will always use scatter-gather if we can, because we can
+ * work around the fact that the buffer cache has a block size of 1024,
+ * and we have 2048 byte sectors. This code should work for buffers that
+ * are any multiple of 512 bytes long.
+ */
+
+ SCpnt->use_sg = 0;
+
+ if (SCpnt->host->sg_tablesize > 0 &&
+ (!need_isa_buffer ||
+ dma_free_sectors >= 10)) {
+ struct buffer_head * bh;
+ struct scatterlist * sgpnt;
+ int count, this_count_max;
+ bh = SCpnt->request.bh;
+ this_count = 0;
+ count = 0;
+ this_count_max = (scsi_CDs[dev].ten ? 0xffff : 0xff) << 4;
+ /* Calculate how many links we can use. First see if we need
+ * a padding record at the start */
+ this_count = SCpnt->request.sector % 4;
+ if(this_count) count++;
+ while(bh && count < SCpnt->host->sg_tablesize) {
+ if ((this_count + (bh->b_size >> 9)) > this_count_max) break;
+ this_count += (bh->b_size >> 9);
+ count++;
+ bh = bh->b_reqnext;
+ };
+ /* Fix up in case of an odd record at the end */
+ end_rec = 0;
+ if(this_count % 4) {
+ if (count < SCpnt->host->sg_tablesize) {
+ count++;
+ end_rec = (4 - (this_count % 4)) << 9;
+ this_count += 4 - (this_count % 4);
+ } else {
+ count--;
+ this_count -= (this_count % 4);
+ };
+ };
+ SCpnt->use_sg = count; /* Number of chains */
+ /* scsi_malloc can only allocate in chunks of 512 bytes */
+ count = (SCpnt->use_sg * sizeof(struct scatterlist) + 511) & ~511;
+
+ SCpnt->sglist_len = count;
+ sgpnt = (struct scatterlist * ) scsi_malloc(count);
+ if (!sgpnt) {
+ printk("Warning - running *really* short on DMA buffers\n");
+ SCpnt->use_sg = 0; /* No memory left - bail out */
+ } else {
+ buffer = (unsigned char *) sgpnt;
+ count = 0;
+ bh = SCpnt->request.bh;
+ if(SCpnt->request.sector % 4) {
+ sgpnt[count].length = (SCpnt->request.sector % 4) << 9;
+ sgpnt[count].address = (char *) scsi_malloc(sgpnt[count].length);
+ if(!sgpnt[count].address) panic("SCSI DMA pool exhausted.");
+ sgpnt[count].alt_address = sgpnt[count].address; /* Flag to delete
+ if needed */
+ count++;
+ };
+ for(bh = SCpnt->request.bh; count < SCpnt->use_sg;
+ count++, bh = bh->b_reqnext) {
+ if (bh) { /* Need a placeholder at the end of the record? */
+ sgpnt[count].address = bh->b_data;
+ sgpnt[count].length = bh->b_size;
+ sgpnt[count].alt_address = NULL;
+ } else {
+ sgpnt[count].address = (char *) scsi_malloc(end_rec);
+ if(!sgpnt[count].address) panic("SCSI DMA pool exhausted.");
+ sgpnt[count].length = end_rec;
+ sgpnt[count].alt_address = sgpnt[count].address;
+ if (count+1 != SCpnt->use_sg) panic("Bad sr request list");
+ break;
+ };
+ if (((long) sgpnt[count].address) + sgpnt[count].length - 1 >
+ ISA_DMA_THRESHOLD && SCpnt->host->unchecked_isa_dma) {
+ sgpnt[count].alt_address = sgpnt[count].address;
+ /* We try to avoid exhausting the DMA pool, since it is easier
+ * to control usage here. In other places we might have a more
+ * pressing need, and we would be screwed if we ran out */
+ if(dma_free_sectors < (sgpnt[count].length >> 9) + 5) {
+ sgpnt[count].address = NULL;
+ } else {
+ sgpnt[count].address = (char *) scsi_malloc(sgpnt[count].length);
+ };
+ /* If we start running low on DMA buffers, we abort the scatter-gather
+ * operation, and free all of the memory we have allocated. We want to
+ * ensure that all scsi operations are able to do at least a non-scatter/gather
+ * operation */
+ if(sgpnt[count].address == NULL){ /* Out of dma memory */
+ printk("Warning: Running low on SCSI DMA buffers");
+ /* Try switching back to a non scatter-gather operation. */
+ while(--count >= 0){
+ if(sgpnt[count].alt_address)
+ scsi_free(sgpnt[count].address, sgpnt[count].length);
+ };
+ SCpnt->use_sg = 0;
+ scsi_free(buffer, SCpnt->sglist_len);
+ break;
+ }; /* if address == NULL */
+ }; /* if need DMA fixup */
+ }; /* for loop to fill list */
+#ifdef DEBUG
+ printk("SR: %d %d %d %d %d *** ",SCpnt->use_sg, SCpnt->request.sector,
+ this_count,
+ SCpnt->request.current_nr_sectors,
+ SCpnt->request.nr_sectors);
+ for(count=0; count<SCpnt->use_sg; count++)
+ printk("SGlist: %d %x %x %x\n", count,
+ sgpnt[count].address,
+ sgpnt[count].alt_address,
+ sgpnt[count].length);
+#endif
+ }; /* Able to allocate scatter-gather list */
+ };
+
+ if (SCpnt->use_sg == 0){
+ /* We cannot use scatter-gather. Do this the old fashion way */
+ if (!SCpnt->request.bh)
+ this_count = SCpnt->request.nr_sectors;
+ else
+ this_count = (SCpnt->request.bh->b_size >> 9);
+
+ start = block % 4;
+ if (start)
+ {
+ this_count = ((this_count > 4 - start) ?
+ (4 - start) : (this_count));
+ buffer = (unsigned char *) scsi_malloc(2048);
+ }
+ else if (this_count < 4)
+ {
+ buffer = (unsigned char *) scsi_malloc(2048);
+ }
+ else
+ {
+ this_count -= this_count % 4;
+ buffer = (unsigned char *) SCpnt->request.buffer;
+ if (((long) buffer) + (this_count << 9) > ISA_DMA_THRESHOLD &&
+ SCpnt->host->unchecked_isa_dma)
+ buffer = (unsigned char *) scsi_malloc(this_count << 9);
+ }
+ };
+
+ if (scsi_CDs[dev].sector_size == 2048)
+ block = block >> 2; /* These are the sectors that the cdrom uses */
+ else
+ block = block & 0xfffffffc;
+
+ realcount = (this_count + 3) / 4;
+
+ if (scsi_CDs[dev].sector_size == 512) realcount = realcount << 2;
+
+ if (((realcount > 0xff) || (block > 0x1fffff)) && scsi_CDs[dev].ten)
+ {
+ if (realcount > 0xffff)
+ {
+ realcount = 0xffff;
+ this_count = realcount * (scsi_CDs[dev].sector_size >> 9);
+ }
+
+ cmd[0] += READ_10 - READ_6 ;
+ cmd[2] = (unsigned char) (block >> 24) & 0xff;
+ cmd[3] = (unsigned char) (block >> 16) & 0xff;
+ cmd[4] = (unsigned char) (block >> 8) & 0xff;
+ cmd[5] = (unsigned char) block & 0xff;
+ cmd[6] = cmd[9] = 0;
+ cmd[7] = (unsigned char) (realcount >> 8) & 0xff;
+ cmd[8] = (unsigned char) realcount & 0xff;
+ }
+ else
+ {
+ if (realcount > 0xff)
+ {
+ realcount = 0xff;
+ this_count = realcount * (scsi_CDs[dev].sector_size >> 9);
+ }
+
+ cmd[1] |= (unsigned char) ((block >> 16) & 0x1f);
+ cmd[2] = (unsigned char) ((block >> 8) & 0xff);
+ cmd[3] = (unsigned char) block & 0xff;
+ cmd[4] = (unsigned char) realcount;
+ cmd[5] = 0;
+ }
+
+#ifdef DEBUG
+ {
+ int i;
+ printk("ReadCD: %d %d %d %d\n",block, realcount, buffer, this_count);
+ printk("Use sg: %d\n", SCpnt->use_sg);
+ printk("Dumping command: ");
+ for(i=0; i<12; i++) printk("%2.2x ", cmd[i]);
+ printk("\n");
+ };
+#endif
+
+ /* Some dumb host adapters can speed transfers by knowing the
+ * minimum transfersize in advance.
+ *
+ * We shouldn't disconnect in the middle of a sector, but the cdrom
+ * sector size can be larger than the size of a buffer and the
+ * transfer may be split to the size of a buffer. So it's safe to
+ * assume that we can at least transfer the minimum of the buffer
+ * size (1024) and the sector size between each connect / disconnect.
+ */
+
+ SCpnt->transfersize = (scsi_CDs[dev].sector_size > 1024) ?
+ 1024 : scsi_CDs[dev].sector_size;
+
+ SCpnt->this_count = this_count;
+ scsi_do_cmd (SCpnt, (void *) cmd, buffer,
+ realcount * scsi_CDs[dev].sector_size,
+ rw_intr, SR_TIMEOUT, MAX_RETRIES);
+}
+
+static int sr_detect(Scsi_Device * SDp){
+
+ if(SDp->type != TYPE_ROM && SDp->type != TYPE_WORM) return 0;
+
+#ifdef MACH
+ printk("Detected scsi CD-ROM cd%d at scsi%d, channel %d, id %d, lun %d\n",
+#else
+ printk("Detected scsi CD-ROM sr%d at scsi%d, channel %d, id %d, lun %d\n",
+#endif
+ sr_template.dev_noticed++,
+ SDp->host->host_no, SDp->channel, SDp->id, SDp->lun);
+
+ return 1;
+}
+
+static int sr_attach(Scsi_Device * SDp){
+ Scsi_CD * cpnt;
+ int i;
+
+ if(SDp->type != TYPE_ROM && SDp->type != TYPE_WORM) return 1;
+
+ if (sr_template.nr_dev >= sr_template.dev_max)
+ {
+ SDp->attached--;
+ return 1;
+ }
+
+ for(cpnt = scsi_CDs, i=0; i<sr_template.dev_max; i++, cpnt++)
+ if(!cpnt->device) break;
+
+ if(i >= sr_template.dev_max) panic ("scsi_devices corrupt (sr)");
+
+ SDp->scsi_request_fn = do_sr_request;
+ scsi_CDs[i].device = SDp;
+ sr_template.nr_dev++;
+ if(sr_template.nr_dev > sr_template.dev_max)
+ panic ("scsi_devices corrupt (sr)");
+ return 0;
+}
+
+
+static void sr_init_done (Scsi_Cmnd * SCpnt)
+{
+ struct request * req;
+
+ req = &SCpnt->request;
+ req->rq_status = RQ_SCSI_DONE; /* Busy, but indicate request done */
+
+ if (req->sem != NULL) {
+ up(req->sem);
+ }
+}
+
+void get_sectorsize(int i){
+ unsigned char cmd[10];
+ unsigned char *buffer;
+ int the_result, retries;
+ Scsi_Cmnd * SCpnt;
+
+ buffer = (unsigned char *) scsi_malloc(512);
+ SCpnt = allocate_device(NULL, scsi_CDs[i].device, 1);
+
+ retries = 3;
+ do {
+ cmd[0] = READ_CAPACITY;
+ cmd[1] = (scsi_CDs[i].device->lun << 5) & 0xe0;
+ memset ((void *) &cmd[2], 0, 8);
+ SCpnt->request.rq_status = RQ_SCSI_BUSY; /* Mark as really busy */
+ SCpnt->cmd_len = 0;
+
+ memset(buffer, 0, 8);
+
+ /* Do the command and wait.. */
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ SCpnt->request.sem = &sem;
+ scsi_do_cmd (SCpnt,
+ (void *) cmd, (void *) buffer,
+ 512, sr_init_done, SR_TIMEOUT,
+ MAX_RETRIES);
+ down(&sem);
+ }
+
+ the_result = SCpnt->result;
+ retries--;
+
+ } while(the_result && retries);
+
+ SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */
+
+ wake_up(&SCpnt->device->device_wait);
+
+ if (the_result) {
+ scsi_CDs[i].capacity = 0x1fffff;
+ scsi_CDs[i].sector_size = 2048; /* A guess, just in case */
+ scsi_CDs[i].needs_sector_size = 1;
+ } else {
+ scsi_CDs[i].capacity = 1 + ((buffer[0] << 24) |
+ (buffer[1] << 16) |
+ (buffer[2] << 8) |
+ buffer[3]);
+ scsi_CDs[i].sector_size = (buffer[4] << 24) |
+ (buffer[5] << 16) | (buffer[6] << 8) | buffer[7];
+ switch (scsi_CDs[i].sector_size) {
+ /*
+ * HP 4020i CD-Recorder reports 2340 byte sectors
+ * Philips CD-Writers report 2352 byte sectors
+ *
+ * Use 2k sectors for them..
+ */
+ case 0: case 2340: case 2352:
+ scsi_CDs[i].sector_size = 2048;
+ /* fall through */
+ case 2048:
+ scsi_CDs[i].capacity *= 4;
+ /* fall through */
+ case 512:
+ break;
+ default:
+#ifdef MACH
+ printk ("cd%d : unsupported sector size %d.\n",
+ i, scsi_CDs[i].sector_size);
+#else
+ printk ("scd%d : unsupported sector size %d.\n",
+ i, scsi_CDs[i].sector_size);
+#endif
+ scsi_CDs[i].capacity = 0;
+ scsi_CDs[i].needs_sector_size = 1;
+ }
+ scsi_CDs[i].needs_sector_size = 0;
+ sr_sizes[i] = scsi_CDs[i].capacity >> (BLOCK_SIZE_BITS - 9);
+ };
+ scsi_free(buffer, 512);
+}
+
+static int sr_registered = 0;
+
+static int sr_init()
+{
+ int i;
+
+ if(sr_template.dev_noticed == 0) return 0;
+
+ if(!sr_registered) {
+ if (register_blkdev(MAJOR_NR,"sr",&sr_fops)) {
+ printk("Unable to get major %d for SCSI-CD\n",MAJOR_NR);
+ return 1;
+ }
+ sr_registered++;
+ }
+
+
+ if (scsi_CDs) return 0;
+ sr_template.dev_max = sr_template.dev_noticed + SR_EXTRA_DEVS;
+ scsi_CDs = (Scsi_CD *) scsi_init_malloc(sr_template.dev_max * sizeof(Scsi_CD), GFP_ATOMIC);
+ memset(scsi_CDs, 0, sr_template.dev_max * sizeof(Scsi_CD));
+
+ sr_sizes = (int *) scsi_init_malloc(sr_template.dev_max * sizeof(int), GFP_ATOMIC);
+ memset(sr_sizes, 0, sr_template.dev_max * sizeof(int));
+
+ sr_blocksizes = (int *) scsi_init_malloc(sr_template.dev_max *
+ sizeof(int), GFP_ATOMIC);
+ for(i=0;i<sr_template.dev_max;i++) sr_blocksizes[i] = 2048;
+ blksize_size[MAJOR_NR] = sr_blocksizes;
+ return 0;
+}
+
+void sr_finish()
+{
+ int i;
+
+ blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+ blk_size[MAJOR_NR] = sr_sizes;
+
+ for (i = 0; i < sr_template.nr_dev; ++i)
+ {
+ /* If we have already seen this, then skip it. Comes up
+ * with loadable modules. */
+ if (scsi_CDs[i].capacity) continue;
+ scsi_CDs[i].capacity = 0x1fffff;
+ scsi_CDs[i].sector_size = 2048; /* A guess, just in case */
+ scsi_CDs[i].needs_sector_size = 1;
+#if 0
+ /* seems better to leave this for later */
+ get_sectorsize(i);
+ printk("Scd sectorsize = %d bytes.\n", scsi_CDs[i].sector_size);
+#endif
+ scsi_CDs[i].use = 1;
+ scsi_CDs[i].ten = 1;
+ scsi_CDs[i].remap = 1;
+ scsi_CDs[i].auto_eject = 0; /* Default is not to eject upon unmount. */
+ sr_sizes[i] = scsi_CDs[i].capacity >> (BLOCK_SIZE_BITS - 9);
+ }
+
+
+ /* If our host adapter is capable of scatter-gather, then we increase
+ * the read-ahead to 16 blocks (32 sectors). If not, we use
+ * a two block (4 sector) read ahead. */
+ if(scsi_CDs[0].device && scsi_CDs[0].device->host->sg_tablesize)
+ read_ahead[MAJOR_NR] = 32; /* 32 sector read-ahead. Always removable. */
+ else
+ read_ahead[MAJOR_NR] = 4; /* 4 sector read-ahead */
+
+ return;
+}
+
+static void sr_detach(Scsi_Device * SDp)
+{
+ Scsi_CD * cpnt;
+ int i;
+
+ for(cpnt = scsi_CDs, i=0; i<sr_template.dev_max; i++, cpnt++)
+ if(cpnt->device == SDp) {
+ kdev_t devi = MKDEV(MAJOR_NR, i);
+
+ /*
+ * Since the cdrom is read-only, no need to sync the device.
+ * We should be kind to our buffer cache, however.
+ */
+ invalidate_inodes(devi);
+ invalidate_buffers(devi);
+
+ /*
+ * Reset things back to a sane state so that one can re-load a new
+ * driver (perhaps the same one).
+ */
+ cpnt->device = NULL;
+ cpnt->capacity = 0;
+ SDp->attached--;
+ sr_template.nr_dev--;
+ sr_template.dev_noticed--;
+ sr_sizes[i] = 0;
+ return;
+ }
+ return;
+}
+
+
+#ifdef MODULE
+
+int init_module(void) {
+ sr_template.usage_count = &mod_use_count_;
+ return scsi_register_module(MODULE_SCSI_DEV, &sr_template);
+}
+
+void cleanup_module( void)
+{
+ scsi_unregister_module(MODULE_SCSI_DEV, &sr_template);
+ unregister_blkdev(SCSI_CDROM_MAJOR, "sr");
+ sr_registered--;
+ if(scsi_CDs != NULL) {
+ scsi_init_free((char *) scsi_CDs,
+ (sr_template.dev_noticed + SR_EXTRA_DEVS)
+ * sizeof(Scsi_CD));
+
+ scsi_init_free((char *) sr_sizes, sr_template.dev_max * sizeof(int));
+ scsi_init_free((char *) sr_blocksizes, sr_template.dev_max * sizeof(int));
+ }
+
+ blksize_size[MAJOR_NR] = NULL;
+ blk_dev[MAJOR_NR].request_fn = NULL;
+ blk_size[MAJOR_NR] = NULL;
+ read_ahead[MAJOR_NR] = 0;
+
+ sr_template.dev_max = 0;
+}
+#endif /* MODULE */
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 4
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */