diff options
author | Thomas Bushnell <thomas@gnu.org> | 1999-04-26 05:44:30 +0000 |
---|---|---|
committer | Thomas Bushnell <thomas@gnu.org> | 1999-04-26 05:44:30 +0000 |
commit | 063bab9f9919bd7460386ed11a15e7f67673077e (patch) | |
tree | ead73696a2296ce80fd8374f18258eb255b29ef8 | |
parent | 1007688e7015f5e8d37743353ca153391120feb3 (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.
-rw-r--r-- | linux/dev/drivers/scsi/aha152x.c | 3281 | ||||
-rw-r--r-- | linux/dev/drivers/scsi/eata_dma.c | 1607 | ||||
-rw-r--r-- | linux/dev/drivers/scsi/g_NCR5380.c | 739 | ||||
-rw-r--r-- | linux/dev/drivers/scsi/hosts.c | 549 | ||||
-rw-r--r-- | linux/dev/drivers/scsi/scsi.c | 3585 | ||||
-rw-r--r-- | linux/dev/drivers/scsi/scsi.h | 650 | ||||
-rw-r--r-- | linux/dev/drivers/scsi/sd.c | 1691 | ||||
-rw-r--r-- | linux/dev/drivers/scsi/sd_ioctl.c | 128 | ||||
-rw-r--r-- | linux/dev/drivers/scsi/seagate.c | 1761 | ||||
-rw-r--r-- | linux/dev/drivers/scsi/sr.c | 1290 |
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: + */ |