summaryrefslogtreecommitdiffstats
path: root/drivers/scsi
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1997-12-06 23:51:34 +0000
committerRalf Baechle <ralf@linux-mips.org>1997-12-06 23:51:34 +0000
commit230e5ab6a084ed50470f101934782dbf54b0d06b (patch)
tree5dd821c8d33f450470588e7a543f74bf74306e9e /drivers/scsi
parentc9b1c8a64c6444d189856f1e26bdcb8b4cd0113a (diff)
Merge with Linux 2.1.67.
Diffstat (limited to 'drivers/scsi')
-rw-r--r--drivers/scsi/BusLogic.h7
-rw-r--r--drivers/scsi/ChangeLog.ncr53c8xx22
-rw-r--r--drivers/scsi/Config.in10
-rw-r--r--drivers/scsi/Makefile46
-rw-r--r--drivers/scsi/fdomain.c1
-rw-r--r--drivers/scsi/gdth.c3387
-rw-r--r--drivers/scsi/gdth.h720
-rw-r--r--drivers/scsi/gdth_ioctl.h86
-rw-r--r--drivers/scsi/gdth_proc.c635
-rw-r--r--drivers/scsi/gdth_proc.h24
-rw-r--r--drivers/scsi/hosts.c30
-rw-r--r--drivers/scsi/ibmmca.c1487
-rw-r--r--drivers/scsi/ibmmca.h8
-rw-r--r--drivers/scsi/ncr53c8xx.c145
-rw-r--r--drivers/scsi/ncr53c8xx.h2
-rw-r--r--drivers/scsi/pci2000.c660
-rw-r--r--drivers/scsi/pci2000.h226
-rw-r--r--drivers/scsi/pci2220i.c817
-rw-r--r--drivers/scsi/pci2220i.h345
-rw-r--r--drivers/scsi/ppa.c2255
-rw-r--r--drivers/scsi/ppa.h139
-rw-r--r--drivers/scsi/psi240i.c717
-rw-r--r--drivers/scsi/psi240i.h344
-rw-r--r--drivers/scsi/psi_chip.h195
-rw-r--r--drivers/scsi/psi_dale.h187
-rw-r--r--drivers/scsi/psi_roy.h314
-rw-r--r--drivers/scsi/scsi.c1
-rw-r--r--drivers/scsi/scsi.h3
-rw-r--r--drivers/scsi/scsi_ioctl.c5
-rw-r--r--drivers/scsi/scsi_module.c6
-rw-r--r--drivers/scsi/seagate.c22
-rw-r--r--drivers/scsi/sg.c27
-rw-r--r--drivers/scsi/sr.c108
-rw-r--r--drivers/scsi/sr.h19
-rw-r--r--drivers/scsi/sr_ioctl.c361
-rw-r--r--drivers/scsi/sr_vendor.c247
-rw-r--r--drivers/scsi/st.c84
-rw-r--r--drivers/scsi/st.h1
38 files changed, 11944 insertions, 1749 deletions
diff --git a/drivers/scsi/BusLogic.h b/drivers/scsi/BusLogic.h
index 515e84b64..87e44e758 100644
--- a/drivers/scsi/BusLogic.h
+++ b/drivers/scsi/BusLogic.h
@@ -1529,7 +1529,12 @@ static inline void BusLogic_Delay(int Seconds)
unsigned long ProcessorFlags;
save_flags(ProcessorFlags);
sti();
- while (--Seconds >= 0) udelay(1000000);
+ while (--Seconds >= 0) {
+ int i = 1000;
+ do {
+ udelay(1000);
+ } while (--i);
+ }
restore_flags(ProcessorFlags);
}
diff --git a/drivers/scsi/ChangeLog.ncr53c8xx b/drivers/scsi/ChangeLog.ncr53c8xx
index ecfd09676..3a110ce40 100644
--- a/drivers/scsi/ChangeLog.ncr53c8xx
+++ b/drivers/scsi/ChangeLog.ncr53c8xx
@@ -1,4 +1,24 @@
-Thu Aug 23 23:43 1997 Gerard Roudier (groudier@club-internet.fr)
+Sat Sep 20 21:00 1997 Gerard Roudier (groudier@club-internet.fr)
+ * revision 2.5c
+ - Several PCI configuration registers fix-ups for powerpc.
+ (Patch sent by Cort).
+
+Thu Aug 28 10:00 1997 Gerard Roudier (groudier@club-internet.fr)
+ * revision 2.5b
+ - Add 'ncr53c8xx' char pointer variable. This variable allows to
+ pass a boot command to the driver when it is loaded as a module.
+ Option separator is ' ' instead of ','. Example:
+ insmod <mod_path>/ncr53c8xx.o ncr53c8xx='verb:2 sync:0 specf:n'
+ - Always use 'driver_setup.settle_delay' for internal resets.
+ 2 seconds hardcoded is sometimes too short. Suggested by Richard W.
+ This delay may be shortenned in order to avoid spurious timeouts.
+ - Fix release module stuff that failed for more than 1 controller.
+ - For linux versions > 1.3.70, trust the 'dev_id' parameter passed
+ to the interrupt handler (dev_id = struct ncb *).
+ - Fix up in 'ncr_log_hard_error()' when the DSP points outside scripts.
+ Suggested by Stefan Esser.
+
+Tue Aug 23 23:43 1997 Gerard Roudier (groudier@club-internet.fr)
* revision 2.5a
- Update Configure.help for inclusion in linux-2.1.51/2/3
- Use BASE_2 address from PCI config space instead of some
diff --git a/drivers/scsi/Config.in b/drivers/scsi/Config.in
index de7a65d6c..3d3ee5695 100644
--- a/drivers/scsi/Config.in
+++ b/drivers/scsi/Config.in
@@ -49,6 +49,7 @@ dep_tristate 'EATA ISA/EISA/PCI (DPT and generic EATA/DMA-compliant boards) supp
int ' maximum number of queued commands' CONFIG_SCSI_EATA_MAX_TAGS 16
fi
dep_tristate 'Future Domain 16xx SCSI/AHA 2920 support' CONFIG_SCSI_FUTURE_DOMAIN $CONFIG_SCSI
+dep_tristate 'GDT SCSI Disk Array Controller support' CONFIG_SCSI_GDTH $CONFIG_SCSI
dep_tristate 'Generic NCR5380/53c400 SCSI support' CONFIG_SCSI_GENERIC_NCR5380 $CONFIG_SCSI
if [ "$CONFIG_SCSI_GENERIC_NCR5380" != "n" ]; then
bool ' Enable NCR53c400 extensions' CONFIG_SCSI_GENERIC_NCR53C400
@@ -82,16 +83,21 @@ if [ "$CONFIG_PCI" = "y" -a "$CONFIG_SCSI_NCR53C7xx" != "y" ]; then
fi
fi
if [ "$CONFIG_MCA" = "y" ]; then
- dep_tristate 'IBMMCA SCSI support' CONFIG_SCSI_IBMMCA $CONFIG_SCSI
+ dep_tristate 'IBMMCA SCSI support' CONFIG_SCSI_IBMMCA $CONFIG_SCSI
+ if [ "$CONFIG_SCSI_IBMMCA" != "n" ]; then
+ bool ' reset SCSI-devices while booting' SCSI_IBMMCA_DEV_RESET
+ fi
fi
if [ "$CONFIG_PARPORT" != "n" ]; then
dep_tristate 'IOMEGA Parallel Port ZIP drive SCSI support' CONFIG_SCSI_PPA $CONFIG_SCSI $CONFIG_PARPORT
if [ "$CONFIG_SCSI_PPA" != "n" ]; then
int ' Pedantic EPP-checking' CONFIG_SCSI_PPA_HAVE_PEDANTIC 2 0 3
- int ' EPP timeout' CONFIG_SCSI_PPA_EPP_TIME 128
fi
fi
dep_tristate 'PAS16 SCSI support' CONFIG_SCSI_PAS16 $CONFIG_SCSI
+dep_tristate 'PCI2000 support' CONFIG_SCSI_PCI2000 $CONFIG_SCSI
+dep_tristate 'PCI2220i support' CONFIG_SCSI_PCI2220I $CONFIG_SCSI
+dep_tristate 'PSI240i support' CONFIG_SCSI_PSI240I $CONFIG_SCSI
dep_tristate 'Qlogic FAS SCSI support' CONFIG_SCSI_QLOGIC_FAS $CONFIG_SCSI
if [ "$CONFIG_PCI" = "y" ]; then
dep_tristate 'Qlogic ISP SCSI support' CONFIG_SCSI_QLOGIC_ISP $CONFIG_SCSI
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index 9fb808309..eefb74fe4 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -15,6 +15,7 @@ MOD_LIST_NAME := SCSI_MODULES
SCSI_SRCS = $(wildcard $(L_OBJS:%.o=%.c))
AHA152X = -DDEBUG_AHA152X -DAUTOCONF
+GDTH = #-DDEBUG_GDTH=2 -D__SERIAL__ -D__COM2__ -DGDTH_STATISTICS
.SUFFIXES:
.SUFFIXES: .c .o .h .a
@@ -67,12 +68,8 @@ else
endif
endif
-ifeq ($(CONFIG_BLK_DEV_SR_VENDOR),y)
-SR_VENDOR = sr_vendor.o
-endif
-
ifeq ($(CONFIG_BLK_DEV_SR),y)
-L_OBJS += sr.o sr_ioctl.o $(SR_VENDOR)
+L_OBJS += sr.o sr_ioctl.o sr_vendor.o
else
ifeq ($(CONFIG_BLK_DEV_SR),m)
M_OBJS += sr_mod.o
@@ -95,6 +92,30 @@ else
endif
endif
+ifeq ($(CONFIG_SCSI_PCI2000),y)
+L_OBJS += pci2000.o
+else
+ ifeq ($(CONFIG_SCSI_PCI2000),m)
+ M_OBJS += pci2000.o
+ endif
+endif
+
+ifeq ($(CONFIG_SCSI_PCI2220I),y)
+L_OBJS += pci2220i.o
+else
+ ifeq ($(CONFIG_SCSI_PCI2220I),m)
+ M_OBJS += pci2220i.o
+ endif
+endif
+
+ifeq ($(CONFIG_SCSI_PSI240I),y)
+L_OBJS += psi240i.o
+else
+ ifeq ($(CONFIG_SCSI_PSI240I),m)
+ M_OBJS += psi240i.o
+ endif
+endif
+
ifeq ($(CONFIG_A4000T_SCSI),y)
L_OBJS += amiga7xx.o 53c7xx.o
else
@@ -297,6 +318,14 @@ else
endif
endif
+ifeq ($(CONFIG_SCSI_GDTH),y)
+L_OBJS += gdth.o
+else
+ ifeq ($(CONFIG_SCSI_GDTH),m)
+ M_OBJS += gdth.o
+ endif
+endif
+
ifeq ($(CONFIG_SCSI_DEBUG),y)
L_OBJS += scsi_debug.o
else
@@ -445,6 +474,9 @@ BusLogic.o: BusLogic.c FlashPoint.c
aha152x.o: aha152x.c
$(CC) $(CFLAGS) $(AHA152X) -c aha152x.c
+gdth.o: gdth.c gdth.h gdth_proc.c gdth_proc.h
+ $(CC) $(CFLAGS) $(GDTH) -c gdth.c
+
aic7xxx.o: aic7xxx.c aic7xxx_seq.h aic7xxx_reg.h
$(CC) $(CFLAGS) -c -o $@ aic7xxx.c
@@ -469,8 +501,8 @@ scsi_mod.o: $(MIX_OBJS) hosts.o scsi.o scsi_ioctl.o constants.o \
scsicam.o scsi_proc.o
$(LD) $(LD_RFLAG) -r -o $@ $(MIX_OBJS) hosts.o scsi.o scsi_ioctl.o constants.o scsicam.o scsi_proc.o
-sr_mod.o: sr.o sr_ioctl.o $(SR_VENDOR)
- $(LD) $(LD_RFLAG) -r -o $@ sr.o sr_ioctl.o $(SR_VENDOR)
+sr_mod.o: sr.o sr_ioctl.o sr_vendor.o
+ $(LD) $(LD_RFLAG) -r -o $@ sr.o sr_ioctl.o sr_vendor.o
sd_mod.o: sd.o sd_ioctl.o
$(LD) $(LD_RFLAG) -r -o $@ sd.o sd_ioctl.o
diff --git a/drivers/scsi/fdomain.c b/drivers/scsi/fdomain.c
index 43599d806..2f59d69f3 100644
--- a/drivers/scsi/fdomain.c
+++ b/drivers/scsi/fdomain.c
@@ -470,6 +470,7 @@ struct signature {
{ "Future Domain Corp. V1.0008/18/93", 5, 33, 3, 4, 0 },
{ "Future Domain Corp. V1.0008/18/93", 26, 33, 3, 4, 1 },
{ "Adaptec AHA-2920 PCI-SCSI Card", 42, 31, 3, -1, 1 },
+ { "IBM F1 P264/32", 5, 14, 3, -1, 1 },
/* This next signature may not be a 3.5 bios */
{ "Future Domain Corp. V2.0108/18/93", 5, 33, 3, 5, 0 },
{ "FUTURE DOMAIN CORP. V3.5008/18/93", 5, 34, 3, 5, 0 },
diff --git a/drivers/scsi/gdth.c b/drivers/scsi/gdth.c
new file mode 100644
index 000000000..77a9abacd
--- /dev/null
+++ b/drivers/scsi/gdth.c
@@ -0,0 +1,3387 @@
+/************************************************************************
+ * GDT ISA/EISA/PCI Disk Array Controller driver for Linux *
+ * *
+ * gdth.c *
+ * Copyright (C) 1995-97 ICP vortex Computersysteme GmbH, Achim Leubner *
+ * *
+ * <achim@vortex.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. *
+ * *
+ * Tested with Linux 1.2.13, ..., 2.1.61 *
+ * *
+ * $Log: gdth.c,v $
+ * Revision 1.10 1997/10/31 12:29:57 achim
+ * Read heads/sectors from host drive
+ *
+ * Revision 1.9 1997/09/04 10:07:25 achim
+ * IO-mapping with virt_to_bus(), readb(), writeb(), ...
+ * register_reboot_notifier() to get a notify on shutdown used
+ *
+ * Revision 1.8 1997/04/02 12:14:30 achim
+ * Version 1.00 (see gdth.h), tested with kernel 2.0.29
+ *
+ * Revision 1.7 1997/03/12 13:33:37 achim
+ * gdth_reset() changed, new async. events
+ *
+ * Revision 1.6 1997/03/04 14:01:11 achim
+ * Shutdown routine gdth_halt() implemented
+ *
+ * Revision 1.5 1997/02/21 09:08:36 achim
+ * New controller included (RP, RP1, RP2 series)
+ * IOCTL interface implemented
+ *
+ * Revision 1.4 1996/07/05 12:48:55 achim
+ * Function gdth_bios_param() implemented
+ * New constant GDTH_MAXC_P_L inserted
+ * GDT_WRITE_THR, GDT_EXT_INFO implemented
+ * Function gdth_reset() changed
+ *
+ * Revision 1.3 1996/05/10 09:04:41 achim
+ * Small changes for Linux 1.2.13
+ *
+ * Revision 1.2 1996/05/09 12:45:27 achim
+ * Loadable module support implemented
+ * /proc support corrections made
+ *
+ * Revision 1.1 1996/04/11 07:35:57 achim
+ * Initial revision
+ *
+ *
+ * $Id: gdth.c,v 1.10 1997/10/31 12:29:57 achim Exp $
+ ************************************************************************/
+
+#ifdef MODULE
+#include <linux/module.h>
+#endif
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/head.h>
+#include <linux/types.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/in.h>
+#include <linux/proc_fs.h>
+#include <linux/time.h>
+#include <linux/timer.h>
+#if LINUX_VERSION_CODE >= 0x020100
+#include <linux/reboot.h>
+#endif
+
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/io.h>
+
+#if LINUX_VERSION_CODE >= 0x010300
+#include <linux/blk.h>
+#else
+#include "../block/blk.h"
+#endif
+#include "scsi.h"
+#include "hosts.h"
+#include "sd.h"
+
+#include "gdth.h"
+
+#if LINUX_VERSION_CODE >= 0x010346
+static void gdth_interrupt(int irq,void *dev_id,struct pt_regs *regs);
+#else
+static void gdth_interrupt(int irq,struct pt_regs *regs);
+#endif
+static int gdth_sync_event(int hanum,int service,unchar index,Scsi_Cmnd *scp);
+static int gdth_async_event(int hanum,int service);
+
+static void gdth_putq(int hanum,Scsi_Cmnd *scp,unchar priority);
+static void gdth_next(int hanum);
+static int gdth_fill_raw_cmd(int hanum,Scsi_Cmnd *scp,unchar b);
+static int gdth_special_cmd(int hanum,Scsi_Cmnd *scp,unchar b);
+static gdth_evt_str *gdth_store_event(ushort source, ushort idx,
+ gdth_evt_data *evt);
+static int gdth_read_event(int handle, gdth_evt_str *estr);
+static void gdth_readapp_event(unchar application, gdth_evt_str *estr);
+static void gdth_clear_events(void);
+
+static void gdth_copy_internal_data(Scsi_Cmnd *scp,char *buffer,ushort count);
+static int gdth_internal_cache_cmd(int hanum,Scsi_Cmnd *scp,
+ unchar b,ulong *flags);
+static int gdth_fill_cache_cmd(int hanum,Scsi_Cmnd *scp,ushort hdrive);
+
+static int gdth_search_eisa(ushort eisa_adr);
+static int gdth_search_isa(ulong bios_adr);
+static int gdth_search_pci(ushort device_id,ushort index,gdth_pci_str *pcistr);
+static int gdth_init_eisa(ushort eisa_adr,gdth_ha_str *ha);
+static int gdth_init_isa(ulong bios_adr,gdth_ha_str *ha);
+static int gdth_init_pci(gdth_pci_str *pcistr,gdth_ha_str *ha);
+
+static void gdth_enable_int(int hanum);
+static int gdth_get_status(unchar *pIStatus,int irq);
+static int gdth_test_busy(int hanum);
+static int gdth_get_cmd_index(int hanum);
+static void gdth_release_event(int hanum);
+static int gdth_wait(int hanum,int index,ulong time);
+static int gdth_internal_cmd(int hanum,unchar service,ushort opcode,ulong p1,
+ ulong p2,ulong p3);
+static int gdth_search_drives(int hanum);
+
+static void *gdth_mmap(ulong paddr, ulong size);
+static void gdth_munmap(void *addr);
+
+static const char *gdth_ctr_name(int hanum);
+#if LINUX_VERSION_CODE >= 0x020100
+static int gdth_halt(struct notifier_block *nb, ulong event, void *buf);
+#else
+void gdth_halt(void);
+#endif
+
+#ifdef DEBUG_GDTH
+static unchar DebugState = DEBUG_GDTH;
+extern int sys_syslog(int,char*,int);
+#define LOGEN sys_syslog(7,NULL,0);
+#define WAITSEC(a) {ulong idx; for(idx=0;idx<a*1000L;++idx) udelay(1000);}
+
+#ifdef SLOWMOTION_GDTH
+#define SLOWM WAITSEC(2)
+#undef INIT_RETRIES
+#undef INIT_TIMEOUT
+#undef POLL_TIMEOUT
+#define INIT_RETRIES 15
+#define INIT_TIMEOUT 150
+#define POLL_TIMEOUT 150
+#else
+#define SLOWM
+#endif
+
+#ifdef __SERIAL__
+#define MAX_SERBUF 160
+static void ser_init(void);
+static void ser_puts(char *str);
+static void ser_putc(char c);
+static int ser_printk(const char *fmt, ...);
+static char strbuf[MAX_SERBUF+1];
+#ifdef __COM2__
+#define COM_BASE 0x2f8
+#else
+#define COM_BASE 0x3f8
+#endif
+static void ser_init()
+{
+ unsigned port=COM_BASE;
+
+ outb(0x80,port+3);
+ outb(0,port+1);
+ /* 19200 Baud, if 9600: outb(12,port) */
+ outb(6, port);
+ outb(3,port+3);
+ outb(0,port+1);
+ /*
+ ser_putc('I');
+ ser_putc(' ');
+ */
+}
+
+static void ser_puts(char *str)
+{
+ char *ptr;
+
+ ser_init();
+ for (ptr=str;*ptr;++ptr)
+ ser_putc(*ptr);
+}
+
+static void ser_putc(char c)
+{
+ unsigned port=COM_BASE;
+
+ while ((inb(port+5) & 0x20)==0);
+ outb(c,port);
+ if (c==0x0a)
+ {
+ while ((inb(port+5) & 0x20)==0);
+ outb(0x0d,port);
+ }
+}
+
+static int ser_printk(const char *fmt, ...)
+{
+ va_list args;
+ int i;
+
+ va_start(args,fmt);
+ i = vsprintf(strbuf,fmt,args);
+ ser_puts(strbuf);
+ va_end(args);
+ return i;
+}
+
+#define TRACE(a) {if (DebugState==1) {ser_printk a; SLOWM}}
+#define TRACE2(a) {if (DebugState==1 || DebugState==2) {ser_printk a; SLOWM}}
+#define TRACE3(a) {if (DebugState!=0) {ser_printk a; SLOWM}}
+
+#else /* !__SERIAL__ */
+#define TRACE(a) {if (DebugState==1) {LOGEN;printk a; SLOWM}}
+#define TRACE2(a) {if (DebugState==1 || DebugState==2) {LOGEN;printk a; SLOWM}}
+#define TRACE3(a) {if (DebugState!=0) {LOGEN;printk a; SLOWM}}
+#endif
+
+#else /* !DEBUG */
+#define TRACE(a)
+#define TRACE2(a)
+#define TRACE3(a)
+#endif
+
+#ifdef GDTH_STATISTICS
+static ulong max_rq=0, max_index=0, max_sg=0;
+static ulong act_ints=0, act_ios=0, act_stats=0, act_rq=0;
+#define GDTH_TIMER 31 /* see linux/timer.h ! */
+#endif
+
+#define PTR2USHORT(a) (ushort)(ulong)(a)
+#define JIFFYWAIT(a) {ulong gdtjf;gdtjf=jiffies+(a);while(gdtjf>jiffies);}
+#define GDTOFFSOF(a,b) (size_t)&(((a*)0)->b)
+#define INDEX_OK(i,t) ((i)<sizeof(t)/sizeof((t)[0]))
+
+#define NUMDATA(a) ( (gdth_num_str *)((a)->hostdata))
+#define HADATA(a) (&((gdth_ext_str *)((a)->hostdata))->haext)
+#define CMDDATA(a) (&((gdth_ext_str *)((a)->hostdata))->cmdext)
+#define DMADATA(a) (&((gdth_ext_str *)((a)->hostdata))->dmaext)
+
+
+#if LINUX_VERSION_CODE < 0x010300
+static void *gdth_mmap(ulong paddr, ulong size)
+{
+ if (paddr >= high_memory)
+ return NULL;
+ else
+ return (void *)paddr;
+}
+static void gdth_munmap(void *addr)
+{
+}
+inline ulong virt_to_phys(volatile void *addr)
+{
+ return (ulong)addr;
+}
+inline void *phys_to_virt(ulong addr)
+{
+ return (void *)addr;
+}
+#define virt_to_bus virt_to_phys
+#define bus_to_virt phys_to_virt
+#define readb(addr) (*(volatile unchar *)(addr))
+#define readw(addr) (*(volatile ushort *)(addr))
+#define readl(addr) (*(volatile ulong *)(addr))
+#define writeb(b,addr) (*(volatile unchar *)(addr) = (b))
+#define writew(b,addr) (*(volatile ushort *)(addr) = (b))
+#define writel(b,addr) (*(volatile ulong *)(addr) = (b))
+#define memset_io(a,b,c) memset((void *)(a),(b),(c))
+#define memcpy_fromio(a,b,c) memcpy((a),(void *)(b),(c))
+#define memcpy_toio(a,b,c) memcpy((void *)(a),(b),(c))
+
+#elif LINUX_VERSION_CODE < 0x020100
+static int remapped = FALSE;
+static void *gdth_mmap(ulong paddr, ulong size)
+{
+ if ( paddr >= high_memory) {
+ remapped = TRUE;
+ return vremap(paddr, size);
+ } else {
+ return (void *)paddr;
+ }
+}
+static void gdth_munmap(void *addr)
+{
+ if (remapped)
+ vfree(addr);
+ remapped = FALSE;
+}
+#else
+static void *gdth_mmap(ulong paddr, ulong size)
+{
+ return ioremap(paddr, size);
+}
+static void gdth_munmap(void *addr)
+{
+ return iounmap(addr);
+}
+#endif
+
+
+static unchar gdth_drq_tab[4] = {5,6,7,7}; /* DRQ table */
+static unchar gdth_irq_tab[6] = {0,10,11,12,14,0}; /* IRQ table */
+static unchar gdth_polling; /* polling if TRUE */
+static unchar gdth_from_wait = FALSE; /* gdth_wait() */
+static int wait_index,wait_hanum; /* gdth_wait() */
+static int gdth_ctr_count = 0; /* controller count */
+static int gdth_ctr_vcount = 0; /* virt. ctr. count */
+static struct Scsi_Host *gdth_ctr_tab[MAXHA]; /* controller table */
+static struct Scsi_Host *gdth_ctr_vtab[MAXHA*MAXBUS]; /* virt. ctr. table */
+static unchar gdth_write_through = FALSE; /* write through */
+static char *gdth_ioctl_tab[4][MAXHA]; /* ioctl buffer */
+static gdth_evt_str ebuffer[MAX_EVENTS]; /* event buffer */
+static int elastidx;
+static int eoldidx;
+
+static struct {
+ Scsi_Cmnd *cmnd; /* pending request */
+ ushort service; /* service */
+} gdth_cmd_tab[GDTH_MAXCMDS][MAXHA]; /* table of pend. requests */
+
+#define DIN 1 /* IN data direction */
+#define DOU 2 /* OUT data direction */
+#define DNO DIN /* no data transfer */
+#define DUN DIN /* unknown data direction */
+static unchar gdth_direction_tab[0x100] = {
+ DNO,DNO,DIN,DIN,DOU,DIN,DIN,DOU,DIN,DUN,DOU,DOU,DUN,DUN,DUN,DIN,
+ DNO,DIN,DIN,DOU,DIN,DOU,DNO,DNO,DOU,DNO,DIN,DNO,DIN,DOU,DNO,DUN,
+ DIN,DUN,DIN,DUN,DOU,DIN,DUN,DUN,DIN,DIN,DIN,DUN,DUN,DIN,DIN,DIN,
+ DIN,DIN,DIN,DNO,DIN,DNO,DNO,DIN,DIN,DIN,DIN,DIN,DIN,DIN,DIN,DIN,
+ DIN,DIN,DIN,DIN,DIN,DNO,DUN,DNO,DNO,DNO,DUN,DNO,DIN,DIN,DUN,DUN,
+ DUN,DUN,DUN,DUN,DUN,DIN,DUN,DUN,DUN,DUN,DIN,DUN,DUN,DUN,DUN,DUN,
+ DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,
+ DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,
+ DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,
+ DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,
+ DUN,DUN,DUN,DUN,DUN,DNO,DNO,DUN,DIN,DNO,DIN,DUN,DNO,DUN,DIN,DIN,
+ DIN,DIN,DIN,DNO,DUN,DIN,DIN,DIN,DIN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,
+ DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,
+ DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,
+ DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DOU,DUN,DUN,DUN,DUN,DUN,
+ DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN
+};
+
+/* LILO params: gdth=<IRQ>
+ *
+ * Where: <IRQ> is any of the valid IRQs for EISA controllers (10,11,12,14)
+ * Sets the IRQ of the GDT3000/3020 EISA controller to this value,
+ * if the IRQ can not automat. detect (controller BIOS disabled)
+ * See gdth_init_eisa()
+ *
+ * You can use the command line gdth=0 to disable the driver
+ */
+static unchar irqs[MAXHA] = {0xff};
+static unchar disable_gdth_scan = FALSE;
+
+/* /proc support */
+#if LINUX_VERSION_CODE >= 0x010300
+#include <linux/stat.h>
+struct proc_dir_entry proc_scsi_gdth = {
+ PROC_SCSI_GDTH, 4, "gdth",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+#include "gdth_proc.h"
+#include "gdth_proc.c"
+#endif
+
+#if LINUX_VERSION_CODE >= 0x020100
+/* notifier block to get a notify on system shutdown/halt/reboot */
+static struct notifier_block gdth_notifier = {
+ gdth_halt, NULL, 0
+};
+#endif
+
+/* controller search and initialization functions */
+
+static int gdth_search_eisa(ushort eisa_adr)
+{
+ ulong id;
+
+ TRACE(("gdth_search_eisa() adr. %x\n",eisa_adr));
+ id = inl(eisa_adr+ID0REG);
+ if (id == GDT3A_ID || id == GDT3B_ID) { /* GDT3000A or GDT3000B */
+ if ((inb(eisa_adr+EISAREG) & 8) == 0)
+ return 0; /* not EISA configured */
+ return 1;
+ }
+ if (id == GDT3_ID) /* GDT3000 */
+ return 1;
+
+ return 0;
+}
+
+
+static int gdth_search_isa(ulong bios_adr)
+{
+ void *addr;
+ ulong id;
+
+ TRACE(("gdth_search_isa() bios adr. %lx\n",bios_adr));
+ if ((addr = gdth_mmap(bios_adr+BIOS_ID_OFFS, sizeof(ulong))) != NULL) {
+ id = readl(addr);
+ gdth_munmap(addr);
+ if (id == GDT2_ID) /* GDT2000 */
+ return 1;
+ }
+ return 0;
+}
+
+
+static int gdth_search_pci(ushort device_id,ushort index,gdth_pci_str *pcistr)
+{
+ int error;
+ ulong base0,base1,base2;
+
+ TRACE(("gdth_search_pci() device_id %d, index %d\n",
+ device_id,index));
+
+ if (!pcibios_present())
+ return 0;
+
+ if (pcibios_find_device(PCI_VENDOR_ID_VORTEX,device_id,index,
+ &pcistr->bus,&pcistr->device_fn))
+ return 0;
+
+ /* GDT PCI controller found, now read resources from config space */
+#if LINUX_VERSION_CODE >= 0x010300
+#define GDTH_BASEP (int *)
+#else
+#define GDTH_BASEP
+#endif
+ if ((error = pcibios_read_config_dword(pcistr->bus,pcistr->device_fn,
+ PCI_BASE_ADDRESS_0,
+ GDTH_BASEP&base0)) ||
+ (error = pcibios_read_config_dword(pcistr->bus,pcistr->device_fn,
+ PCI_BASE_ADDRESS_1,
+ GDTH_BASEP&base1)) ||
+ (error = pcibios_read_config_dword(pcistr->bus,pcistr->device_fn,
+ PCI_BASE_ADDRESS_2,
+ GDTH_BASEP&base2)) ||
+ (error = pcibios_read_config_dword(pcistr->bus,pcistr->device_fn,
+ PCI_ROM_ADDRESS,
+ GDTH_BASEP&pcistr->bios)) ||
+ (error = pcibios_read_config_byte(pcistr->bus,pcistr->device_fn,
+ PCI_INTERRUPT_LINE,&pcistr->irq))) {
+ printk("GDT-PCI: error %s reading configuration space",
+ pcibios_strerror(error));
+ return -1;
+ }
+
+ pcistr->device_id = device_id;
+ if (device_id <= PCI_DEVICE_ID_VORTEX_GDT6000B || /* GDT6000 or GDT6000B */
+ device_id >= PCI_DEVICE_ID_VORTEX_GDT6x17RP) { /* MPR */
+ if ((base0 & PCI_BASE_ADDRESS_SPACE)!=PCI_BASE_ADDRESS_SPACE_MEMORY)
+ return -1;
+ pcistr->dpmem = base0 & PCI_BASE_ADDRESS_MEM_MASK;
+ } else { /* GDT6110, GDT6120, .. */
+ if ((base0 & PCI_BASE_ADDRESS_SPACE)!=PCI_BASE_ADDRESS_SPACE_MEMORY ||
+ (base2 & PCI_BASE_ADDRESS_SPACE)!=PCI_BASE_ADDRESS_SPACE_MEMORY ||
+ (base1 & PCI_BASE_ADDRESS_SPACE)!=PCI_BASE_ADDRESS_SPACE_IO)
+ return -1;
+ pcistr->dpmem = base2 & PCI_BASE_ADDRESS_MEM_MASK;
+ pcistr->io_mm = base0 & PCI_BASE_ADDRESS_MEM_MASK;
+ pcistr->io = base1 & PCI_BASE_ADDRESS_IO_MASK;
+ }
+ return 1;
+}
+
+
+static int gdth_init_eisa(ushort eisa_adr,gdth_ha_str *ha)
+{
+ ulong retries,id;
+ unchar prot_ver,eisacf,i,irq_found;
+
+ TRACE(("gdth_init_eisa() adr. %x\n",eisa_adr));
+
+ /* disable board interrupts, deinitialize services */
+ outb(0xff,eisa_adr+EDOORREG);
+ outb(0x00,eisa_adr+EDENABREG);
+ outb(0x00,eisa_adr+EINTENABREG);
+
+ outb(0xff,eisa_adr+LDOORREG);
+ retries = INIT_RETRIES;
+ JIFFYWAIT(2);
+ while (inb(eisa_adr+EDOORREG) != 0xff) {
+ if (--retries == 0) {
+ printk("GDT-EISA: Initialization error (DEINIT failed)\n");
+ return 0;
+ }
+ udelay(1000);
+ TRACE2(("wait for DEINIT: retries=%ld\n",retries));
+ }
+ prot_ver = inb(eisa_adr+MAILBOXREG);
+ outb(0xff,eisa_adr+EDOORREG);
+ if (prot_ver != PROTOCOL_VERSION) {
+ printk("GDT-EISA: Illegal protocol version\n");
+ return 0;
+ }
+ ha->bmic = eisa_adr;
+ ha->brd_phys = (ulong)eisa_adr >> 12;
+
+ outl(0,eisa_adr+MAILBOXREG);
+ outl(0,eisa_adr+MAILBOXREG+4);
+ outl(0,eisa_adr+MAILBOXREG+8);
+ outl(0,eisa_adr+MAILBOXREG+12);
+
+ /* detect IRQ */
+ if ((id = inl(eisa_adr+ID0REG)) == GDT3_ID) {
+ ha->type = GDT_EISA;
+ ha->stype = id;
+ outl(1,eisa_adr+MAILBOXREG+8);
+ outb(0xfe,eisa_adr+LDOORREG);
+ retries = INIT_RETRIES;
+ JIFFYWAIT(2);
+ while (inb(eisa_adr+EDOORREG) != 0xfe) {
+ if (--retries == 0) {
+ printk("GDT-EISA: Initialization error (get IRQ failed)\n");
+ return 0;
+ }
+ udelay(1000);
+ }
+ ha->irq = inb(eisa_adr+MAILBOXREG);
+ outb(0xff,eisa_adr+EDOORREG);
+ TRACE2(("GDT3000/3020: IRQ=%d\n",ha->irq));
+ /* check the result */
+ if (ha->irq == 0) {
+ TRACE2(("Unknown IRQ, check IRQ table from cmd line !\n"));
+ for (i=0,irq_found=FALSE; i<MAXHA && irqs[i]!=0xff; ++i) {
+ if (irqs[i]!=0) {
+ irq_found=TRUE;
+ break;
+ }
+ }
+ if (irq_found) {
+ ha->irq = irqs[i];
+ irqs[i] = 0;
+ printk("GDT-EISA: Can not detect controller IRQ,\n");
+ printk("Use IRQ setting from command line (IRQ = %d)\n",
+ ha->irq);
+ } else {
+ printk("GDT-EISA: Initialization error (unknown IRQ), Enable\n");
+ printk("the controller BIOS or use command line parameters\n");
+ return 0;
+ }
+ }
+ } else {
+ eisacf = inb(eisa_adr+EISAREG) & 7;
+ if (eisacf > 4) /* level triggered */
+ eisacf -= 4;
+ ha->irq = gdth_irq_tab[eisacf];
+ ha->type = GDT_EISA;
+ ha->stype= id;
+ }
+ return 1;
+}
+
+
+static int gdth_init_isa(ulong bios_adr,gdth_ha_str *ha)
+{
+ register gdt2_dpram_str *dp2_ptr;
+ int i;
+ unchar irq_drq,prot_ver;
+ ulong retries;
+
+ TRACE(("gdth_init_isa() bios adr. %lx\n",bios_adr));
+
+ ha->brd = gdth_mmap(bios_adr, sizeof(gdt2_dpram_str));
+ if (ha->brd == NULL) {
+ printk("GDT-ISA: Initialization error (DPMEM remap error)\n");
+ return 0;
+ }
+ dp2_ptr = (gdt2_dpram_str *)ha->brd;
+ writeb(1, &dp2_ptr->io.memlock); /* switch off write protection */
+ /* reset interface area */
+ memset_io((char *)&dp2_ptr->u,0,sizeof(dp2_ptr->u));
+
+ /* disable board interrupts, read DRQ and IRQ */
+ writeb(0xff, &dp2_ptr->io.irqdel);
+ writeb(0x00, &dp2_ptr->io.irqen);
+ writeb(0x00, &dp2_ptr->u.ic.S_Status);
+ writeb(0x00, &dp2_ptr->u.ic.Cmd_Index);
+
+ irq_drq = readb(&dp2_ptr->io.rq);
+ for (i=0; i<3; ++i) {
+ if ((irq_drq & 1)==0)
+ break;
+ irq_drq >>= 1;
+ }
+ ha->drq = gdth_drq_tab[i];
+
+ irq_drq = readb(&dp2_ptr->io.rq) >> 3;
+ for (i=1; i<5; ++i) {
+ if ((irq_drq & 1)==0)
+ break;
+ irq_drq >>= 1;
+ }
+ ha->irq = gdth_irq_tab[i];
+
+ /* deinitialize services */
+ writel(bios_adr, &dp2_ptr->u.ic.S_Info[0]);
+ writeb(0xff, &dp2_ptr->u.ic.S_Cmd_Indx);
+ writeb(0, &dp2_ptr->io.event);
+ retries = INIT_RETRIES;
+ JIFFYWAIT(2);
+ while (readb(&dp2_ptr->u.ic.S_Status) != 0xff) {
+ if (--retries == 0) {
+ printk("GDT-ISA: Initialization error (DEINIT failed)\n");
+ gdth_munmap(ha->brd);
+ return 0;
+ }
+ udelay(1000);
+ }
+ prot_ver = (unchar)readl(&dp2_ptr->u.ic.S_Info[0]);
+ writeb(0, &dp2_ptr->u.ic.Status);
+ writeb(0xff, &dp2_ptr->io.irqdel);
+ if (prot_ver != PROTOCOL_VERSION) {
+ printk("GDT-ISA: Illegal protocol version\n");
+ gdth_munmap(ha->brd);
+ return 0;
+ }
+
+ ha->type = GDT_ISA;
+ ha->ic_all_size = sizeof(dp2_ptr->u);
+ ha->stype= GDT2_ID;
+ ha->brd_phys = bios_adr >> 4;
+
+ /* special request to controller BIOS */
+ writel(0x00, &dp2_ptr->u.ic.S_Info[0]);
+ writel(0x00, &dp2_ptr->u.ic.S_Info[1]);
+ writel(0x01, &dp2_ptr->u.ic.S_Info[2]);
+ writel(0x00, &dp2_ptr->u.ic.S_Info[3]);
+ writeb(0xfe, &dp2_ptr->u.ic.S_Cmd_Indx);
+ writeb(0, &dp2_ptr->io.event);
+ retries = INIT_RETRIES;
+ JIFFYWAIT(2);
+ while (readb(&dp2_ptr->u.ic.S_Status) != 0xfe) {
+ if (--retries == 0) {
+ printk("GDT-ISA: Initialization error\n");
+ gdth_munmap(ha->brd);
+ return 0;
+ }
+ udelay(1000);
+ }
+ writeb(0, &dp2_ptr->u.ic.Status);
+ writeb(0xff, &dp2_ptr->io.irqdel);
+ return 1;
+}
+
+
+static int gdth_init_pci(gdth_pci_str *pcistr,gdth_ha_str *ha)
+{
+ register gdt6_dpram_str *dp6_ptr;
+ register gdt6c_dpram_str *dp6c_ptr;
+ register gdt6m_dpram_str *dp6m_ptr;
+ ulong retries;
+ unchar prot_ver;
+
+ TRACE(("gdth_init_pci()\n"));
+
+ ha->brd_phys = (pcistr->bus << 8) | (pcistr->device_fn & 0xf8);
+ ha->stype = (ulong)pcistr->device_id;
+ ha->irq = pcistr->irq;
+
+ if (ha->stype <= PCI_DEVICE_ID_VORTEX_GDT6000B) { /* GDT6000 or GDT6000B */
+ TRACE2(("init_pci() dpmem %lx irq %d\n",pcistr->dpmem,ha->irq));
+ ha->brd = gdth_mmap(pcistr->dpmem, sizeof(gdt6_dpram_str));
+ if (ha->brd == NULL) {
+ printk("GDT-PCI: Initialization error (DPMEM remap error)\n");
+ return 0;
+ }
+ dp6_ptr = (gdt6_dpram_str *)ha->brd;
+ /* reset interface area */
+ memset_io((char *)&dp6_ptr->u,0,sizeof(dp6_ptr->u));
+ if (readl(&dp6_ptr->u) != 0) {
+ printk("GDT-PCI: Initialization error (DPMEM write error)\n");
+ gdth_munmap(ha->brd);
+ return 0;
+ }
+
+ /* disable board interrupts, deinit services */
+ writeb(0xff, &dp6_ptr->io.irqdel);
+ writeb(0x00, &dp6_ptr->io.irqen);;
+ writeb(0x00, &dp6_ptr->u.ic.S_Status);
+ writeb(0x00, &dp6_ptr->u.ic.Cmd_Index);
+
+ writel(pcistr->dpmem, &dp6_ptr->u.ic.S_Info[0]);
+ writeb(0xff, &dp6_ptr->u.ic.S_Cmd_Indx);
+ writeb(0, &dp6_ptr->io.event);
+ retries = INIT_RETRIES;
+ JIFFYWAIT(2);
+ while (readb(&dp6_ptr->u.ic.S_Status) != 0xff) {
+ if (--retries == 0) {
+ printk("GDT-PCI: Initialization error (DEINIT failed)\n");
+ gdth_munmap(ha->brd);
+ return 0;
+ }
+ udelay(1000);
+ }
+ prot_ver = (unchar)readl(&dp6_ptr->u.ic.S_Info[0]);
+ writeb(0, &dp6_ptr->u.ic.S_Status);
+ writeb(0xff, &dp6_ptr->io.irqdel);
+ if (prot_ver != PROTOCOL_VERSION) {
+ printk("GDT-PCI: Illegal protocol version\n");
+ gdth_munmap(ha->brd);
+ return 0;
+ }
+
+ ha->type = GDT_PCI;
+ ha->ic_all_size = sizeof(dp6_ptr->u);
+
+ /* special command to controller BIOS */
+ writel(0x00, &dp6_ptr->u.ic.S_Info[0]);
+ writel(0x00, &dp6_ptr->u.ic.S_Info[1]);
+ writel(0x01, &dp6_ptr->u.ic.S_Info[2]);
+ writel(0x00, &dp6_ptr->u.ic.S_Info[3]);
+ writeb(0xfe, &dp6_ptr->u.ic.S_Cmd_Indx);
+ writeb(0, &dp6_ptr->io.event);
+ retries = INIT_RETRIES;
+ JIFFYWAIT(2);
+ while (readb(&dp6_ptr->u.ic.S_Status) != 0xfe) {
+ if (--retries == 0) {
+ printk("GDT-PCI: Initialization error\n");
+ gdth_munmap(ha->brd);
+ return 0;
+ }
+ udelay(1000);
+ }
+ writeb(0, &dp6_ptr->u.ic.S_Status);
+ writeb(0xff, &dp6_ptr->io.irqdel);
+
+ } else if (ha->stype <= PCI_DEVICE_ID_VORTEX_GDT6555) { /* GDT6110, GDT6120, .. */
+ ha->plx = (gdt6c_plx_regs *)pcistr->io;
+ TRACE2(("init_pci_new() dpmem %lx io %lx irq %d\n",
+ pcistr->dpmem,(ulong)ha->plx,ha->irq));
+ ha->brd = gdth_mmap(pcistr->dpmem, sizeof(gdt6c_dpram_str));
+ if (ha->brd == NULL) {
+ printk("GDT-PCI: Initialization error (DPMEM remap error)\n");
+ gdth_munmap(ha->brd);
+ return 0;
+ }
+ dp6c_ptr = (gdt6c_dpram_str *)ha->brd;
+ /* reset interface area */
+ memset_io((char *)&dp6c_ptr->u,0,sizeof(dp6c_ptr->u));
+ if (readl(&dp6c_ptr->u) != 0) {
+ printk("GDT-PCI: Initialization error (DPMEM write error)\n");
+ gdth_munmap(ha->brd);
+ return 0;
+ }
+
+ /* disable board interrupts, deinit services */
+ outb(0x00,PTR2USHORT(&ha->plx->control1));
+ outb(0xff,PTR2USHORT(&ha->plx->edoor_reg));
+
+ writeb(0x00, &dp6c_ptr->u.ic.S_Status);
+ writeb(0x00, &dp6c_ptr->u.ic.Cmd_Index);
+
+ writel(pcistr->dpmem, &dp6c_ptr->u.ic.S_Info[0]);
+ writeb(0xff, &dp6c_ptr->u.ic.S_Cmd_Indx);
+
+ outb(1,PTR2USHORT(&ha->plx->ldoor_reg));
+
+ retries = INIT_RETRIES;
+ JIFFYWAIT(2);
+ while (readb(&dp6c_ptr->u.ic.S_Status) != 0xff) {
+ if (--retries == 0) {
+ printk("GDT-PCI: Initialization error (DEINIT failed)\n");
+ gdth_munmap(ha->brd);
+ return 0;
+ }
+ udelay(1000);
+ }
+ prot_ver = (unchar)readl(&dp6c_ptr->u.ic.S_Info[0]);
+ writeb(0, &dp6c_ptr->u.ic.Status);
+ if (prot_ver != PROTOCOL_VERSION) {
+ printk("GDT-PCI: Illegal protocol version\n");
+ gdth_munmap(ha->brd);
+ return 0;
+ }
+
+ ha->type = GDT_PCINEW;
+ ha->ic_all_size = sizeof(dp6c_ptr->u);
+
+ /* special command to controller BIOS */
+ writel(0x00, &dp6c_ptr->u.ic.S_Info[0]);
+ writel(0x00, &dp6c_ptr->u.ic.S_Info[1]);
+ writel(0x01, &dp6c_ptr->u.ic.S_Info[2]);
+ writel(0x00, &dp6c_ptr->u.ic.S_Info[3]);
+ writeb(0xfe, &dp6c_ptr->u.ic.S_Cmd_Indx);
+
+ outb(1,PTR2USHORT(&ha->plx->ldoor_reg));
+
+ retries = INIT_RETRIES;
+ JIFFYWAIT(2);
+ while (readb(&dp6c_ptr->u.ic.S_Status) != 0xfe) {
+ if (--retries == 0) {
+ printk("GDT-PCI: Initialization error\n");
+ gdth_munmap(ha->brd);
+ return 0;
+ }
+ udelay(1000);
+ }
+ writeb(0, &dp6c_ptr->u.ic.S_Status);
+
+ } else { /* MPR */
+ TRACE2(("init_pci_mpr() dpmem %lx irq %d\n",pcistr->dpmem,ha->irq));
+ ha->brd = gdth_mmap(pcistr->dpmem, sizeof(gdt6m_dpram_str));
+ if (ha->brd == NULL) {
+ printk("GDT-PCI: Initialization error (DPMEM remap error)\n");
+ return 0;
+ }
+
+ dp6m_ptr = (gdt6m_dpram_str *)ha->brd;
+ /* reset interface area */
+ memset_io((char *)&dp6m_ptr->u,0,sizeof(dp6m_ptr->u));
+ if (readl(&dp6m_ptr->u) != 0) {
+ printk("GDT-PCI: Initialization error (DPMEM write error)\n");
+ gdth_munmap(ha->brd);
+ return 0;
+ }
+
+ /* disable board interrupts, deinit services */
+ writeb(readb(&dp6m_ptr->i960r.edoor_en_reg) | 4,
+ &dp6m_ptr->i960r.edoor_en_reg);
+ writeb(0xff, &dp6m_ptr->i960r.edoor_reg);
+ writeb(0x00, &dp6m_ptr->u.ic.S_Status);
+ writeb(0x00, &dp6m_ptr->u.ic.Cmd_Index);
+
+ writel(pcistr->dpmem, &dp6m_ptr->u.ic.S_Info[0]);
+ writeb(0xff, &dp6m_ptr->u.ic.S_Cmd_Indx);
+ writeb(1, &dp6m_ptr->i960r.ldoor_reg);
+ retries = INIT_RETRIES;
+ JIFFYWAIT(2);
+ while (readb(&dp6m_ptr->u.ic.S_Status) != 0xff) {
+ if (--retries == 0) {
+ printk("GDT-PCI: Initialization error (DEINIT failed)\n");
+ gdth_munmap(ha->brd);
+ return 0;
+ }
+ udelay(1000);
+ }
+ prot_ver = (unchar)readl(&dp6m_ptr->u.ic.S_Info[0]);
+ writeb(0, &dp6m_ptr->u.ic.S_Status);
+ if (prot_ver != PROTOCOL_VERSION) {
+ printk("GDT-PCI: Illegal protocol version\n");
+ gdth_munmap(ha->brd);
+ return 0;
+ }
+
+ ha->type = GDT_PCIMPR;
+ ha->ic_all_size = sizeof(dp6m_ptr->u);
+
+ /* special command to controller BIOS */
+ writel(0x00, &dp6m_ptr->u.ic.S_Info[0]);
+ writel(0x00, &dp6m_ptr->u.ic.S_Info[1]);
+ writel(0x01, &dp6m_ptr->u.ic.S_Info[2]);
+ writel(0x00, &dp6m_ptr->u.ic.S_Info[3]);
+ writeb(0xfe, &dp6m_ptr->u.ic.S_Cmd_Indx);
+ writeb(1, &dp6m_ptr->i960r.ldoor_reg);
+ retries = INIT_RETRIES;
+ JIFFYWAIT(2);
+ while (readb(&dp6m_ptr->u.ic.S_Status) != 0xfe) {
+ if (--retries == 0) {
+ printk("GDT-PCI: Initialization error\n");
+ gdth_munmap(ha->brd);
+ return 0;
+ }
+ udelay(1000);
+ }
+ writeb(0, &dp6m_ptr->u.ic.S_Status);
+ }
+
+ return 1;
+}
+
+
+/* controller protocol functions */
+
+static void gdth_enable_int(int hanum)
+{
+ gdth_ha_str *ha;
+ ulong flags;
+ gdt2_dpram_str *dp2_ptr;
+ gdt6_dpram_str *dp6_ptr;
+ gdt6m_dpram_str *dp6m_ptr;
+
+ TRACE(("gdth_enable_int() hanum %d\n",hanum));
+ ha = HADATA(gdth_ctr_tab[hanum]);
+
+ save_flags(flags);
+ cli();
+
+ if (ha->type == GDT_EISA) {
+ outb(0xff, ha->bmic + EDOORREG);
+ outb(0xff, ha->bmic + EDENABREG);
+ outb(0x01, ha->bmic + EINTENABREG);
+ } else if (ha->type == GDT_ISA) {
+ dp2_ptr = (gdt2_dpram_str *)ha->brd;
+ writeb(1, &dp2_ptr->io.irqdel);
+ writeb(0, &dp2_ptr->u.ic.Cmd_Index);
+ writeb(1, &dp2_ptr->io.irqen);
+ } else if (ha->type == GDT_PCI) {
+ dp6_ptr = (gdt6_dpram_str *)ha->brd;
+ writeb(1, &dp6_ptr->io.irqdel);
+ writeb(0, &dp6_ptr->u.ic.Cmd_Index);
+ writeb(1, &dp6_ptr->io.irqen);
+ } else if (ha->type == GDT_PCINEW) {
+ outb(0xff, PTR2USHORT(&ha->plx->edoor_reg));
+ outb(0x03, PTR2USHORT(&ha->plx->control1));
+ } else if (ha->type == GDT_PCIMPR) {
+ dp6m_ptr = (gdt6m_dpram_str *)ha->brd;
+ writeb(0xff, &dp6m_ptr->i960r.edoor_reg);
+ writeb(readb(&dp6m_ptr->i960r.edoor_en_reg) & ~4,
+ &dp6m_ptr->i960r.edoor_en_reg);
+ }
+ restore_flags(flags);
+}
+
+
+static int gdth_get_status(unchar *pIStatus,int irq)
+{
+ register gdth_ha_str *ha;
+ int i;
+
+ TRACE(("gdth_get_status() irq %d ctr_count %d\n",
+ irq,gdth_ctr_count));
+
+ *pIStatus = 0;
+ for (i=0; i<gdth_ctr_count; ++i) {
+ ha = HADATA(gdth_ctr_tab[i]);
+ if (ha->irq != (unchar)irq) /* check IRQ */
+ continue;
+ if (ha->type == GDT_EISA)
+ *pIStatus = inb((ushort)ha->bmic + EDOORREG);
+ else if (ha->type == GDT_ISA)
+ *pIStatus = readb(&((gdt2_dpram_str *)ha->brd)->u.ic.Cmd_Index);
+ else if (ha->type == GDT_PCI)
+ *pIStatus = readb(&((gdt6_dpram_str *)ha->brd)->u.ic.Cmd_Index);
+ else if (ha->type == GDT_PCINEW)
+ *pIStatus = inb(PTR2USHORT(&ha->plx->edoor_reg));
+ else if (ha->type == GDT_PCIMPR)
+ *pIStatus = readb(&((gdt6m_dpram_str *)ha->brd)->i960r.edoor_reg);
+
+ if (*pIStatus)
+ return i; /* board found */
+ }
+ return -1;
+}
+
+
+static int gdth_test_busy(int hanum)
+{
+ register gdth_ha_str *ha;
+ register int gdtsema0 = 0;
+
+ TRACE(("gdth_test_busy() hanum %d\n",hanum));
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ if (ha->type == GDT_EISA)
+ gdtsema0 = (int)inb(ha->bmic + SEMA0REG);
+ else if (ha->type == GDT_ISA)
+ gdtsema0 = (int)readb(&((gdt2_dpram_str *)ha->brd)->u.ic.Sema0);
+ else if (ha->type == GDT_PCI)
+ gdtsema0 = (int)readb(&((gdt6_dpram_str *)ha->brd)->u.ic.Sema0);
+ else if (ha->type == GDT_PCINEW)
+ gdtsema0 = (int)inb(PTR2USHORT(&ha->plx->sema0_reg));
+ else if (ha->type == GDT_PCIMPR)
+ gdtsema0 = (int)readb(&((gdt6m_dpram_str *)ha->brd)->i960r.sema0_reg);
+
+ return (gdtsema0 & 1);
+}
+
+
+static int gdth_get_cmd_index(int hanum)
+{
+ register gdth_ha_str *ha;
+ int i;
+
+ TRACE(("gdth_get_cmd_index() hanum %d\n",hanum));
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ for (i=0; i<GDTH_MAXCMDS; ++i) {
+ if (gdth_cmd_tab[i][hanum].cmnd == UNUSED_CMND) {
+ gdth_cmd_tab[i][hanum].cmnd = ha->pccb->RequestBuffer;
+ gdth_cmd_tab[i][hanum].service = ha->pccb->Service;
+ ha->pccb->CommandIndex = (ulong)i+2;
+ return (i+2);
+ }
+ }
+ return 0;
+}
+
+
+static void gdth_set_sema0(int hanum)
+{
+ register gdth_ha_str *ha;
+
+ TRACE(("gdth_set_sema0() hanum %d\n",hanum));
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ if (ha->type == GDT_EISA)
+ outb(1, ha->bmic + SEMA0REG);
+ else if (ha->type == GDT_ISA)
+ writeb(1, &((gdt2_dpram_str *)ha->brd)->u.ic.Sema0);
+ else if (ha->type == GDT_PCI)
+ writeb(1, &((gdt6_dpram_str *)ha->brd)->u.ic.Sema0);
+ else if (ha->type == GDT_PCINEW)
+ outb(1, PTR2USHORT(&ha->plx->sema0_reg));
+ else if (ha->type == GDT_PCIMPR)
+ writeb(1, &((gdt6m_dpram_str *)ha->brd)->i960r.sema0_reg);
+
+}
+
+
+static void gdth_copy_command(int hanum)
+{
+ register gdth_ha_str *ha;
+ register gdth_cmd_str *cmd_ptr;
+ register gdt6m_dpram_str *dp6m_ptr;
+ register gdt6c_dpram_str *dp6c_ptr;
+ gdt6_dpram_str *dp6_ptr;
+ gdt2_dpram_str *dp2_ptr;
+ ushort cp_count,dp_offset,cmd_no;
+
+ TRACE(("gdth_copy_command() hanum %d\n",hanum));
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ cp_count = ha->cmd_len;
+ dp_offset= ha->cmd_offs_dpmem;
+ cmd_no = ha->cmd_cnt;
+ cmd_ptr = ha->pccb;
+
+ ++ha->cmd_cnt;
+ if (ha->type == GDT_EISA)
+ return; /* no DPMEM, no copy */
+
+ /* set cpcount dword aligned */
+ if (cp_count & 3)
+ cp_count += (4 - (cp_count & 3));
+
+ ha->cmd_offs_dpmem += cp_count;
+
+ /* set offset and service, copy command to DPMEM */
+ if (ha->type == GDT_ISA) {
+ dp2_ptr = (gdt2_dpram_str *)ha->brd;
+ writew(dp_offset + DPMEM_COMMAND_OFFSET,
+ &dp2_ptr->u.ic.comm_queue[cmd_no].offset);
+ writew((ushort)cmd_ptr->Service,
+ &dp2_ptr->u.ic.comm_queue[cmd_no].serv_id);
+ memcpy_toio(&dp2_ptr->u.ic.gdt_dpr_cmd[dp_offset],cmd_ptr,cp_count);
+ } else if (ha->type == GDT_PCI) {
+ dp6_ptr = (gdt6_dpram_str *)ha->brd;
+ writew(dp_offset + DPMEM_COMMAND_OFFSET,
+ &dp6_ptr->u.ic.comm_queue[cmd_no].offset);
+ writew((ushort)cmd_ptr->Service,
+ &dp6_ptr->u.ic.comm_queue[cmd_no].serv_id);
+ memcpy_toio(&dp6_ptr->u.ic.gdt_dpr_cmd[dp_offset],cmd_ptr,cp_count);
+ } else if (ha->type == GDT_PCINEW) {
+ dp6c_ptr = (gdt6c_dpram_str *)ha->brd;
+ writew(dp_offset + DPMEM_COMMAND_OFFSET,
+ &dp6c_ptr->u.ic.comm_queue[cmd_no].offset);
+ writew((ushort)cmd_ptr->Service,
+ &dp6c_ptr->u.ic.comm_queue[cmd_no].serv_id);
+ memcpy_toio(&dp6c_ptr->u.ic.gdt_dpr_cmd[dp_offset],cmd_ptr,cp_count);
+ } else if (ha->type == GDT_PCIMPR) {
+ dp6m_ptr = (gdt6m_dpram_str *)ha->brd;
+ writew(dp_offset + DPMEM_COMMAND_OFFSET,
+ &dp6m_ptr->u.ic.comm_queue[cmd_no].offset);
+ writew((ushort)cmd_ptr->Service,
+ &dp6m_ptr->u.ic.comm_queue[cmd_no].serv_id);
+ memcpy_toio(&dp6m_ptr->u.ic.gdt_dpr_cmd[dp_offset],cmd_ptr,cp_count);
+ }
+}
+
+
+static void gdth_release_event(int hanum)
+{
+ register gdth_ha_str *ha;
+
+#ifdef GDTH_STATISTICS
+ ulong i,j;
+ for (i=0,j=0; j<GDTH_MAXCMDS; ++j) {
+ if (gdth_cmd_tab[j][hanum].cmnd != UNUSED_CMND)
+ ++i;
+ }
+ if (max_index < i) {
+ max_index = i;
+ TRACE3(("GDT: max_index = %d\n",(ushort)i));
+ }
+#endif
+
+ TRACE(("gdth_release_event() hanum %d\n",hanum));
+ ha = HADATA(gdth_ctr_tab[hanum]);
+
+ if (ha->pccb->OpCode == GDT_INIT)
+ ha->pccb->Service |= 0x80;
+
+ if (ha->type == GDT_EISA) {
+ outb(ha->pccb->Service, ha->bmic + LDOORREG);
+ if (ha->pccb->OpCode == GDT_INIT) /* store DMA buffer */
+ outl((ulong)ha->pccb, ha->bmic + MAILBOXREG);
+ } else if (ha->type == GDT_ISA)
+ writeb(0, &((gdt2_dpram_str *)ha->brd)->io.event);
+ else if (ha->type == GDT_PCI)
+ writeb(0, &((gdt6_dpram_str *)ha->brd)->io.event);
+ else if (ha->type == GDT_PCINEW)
+ outb(1, PTR2USHORT(&ha->plx->ldoor_reg));
+ else if (ha->type == GDT_PCIMPR)
+ writeb(1, &((gdt6m_dpram_str *)ha->brd)->i960r.ldoor_reg);
+}
+
+
+static int gdth_wait(int hanum,int index,ulong time)
+{
+ gdth_ha_str *ha;
+ int answer_found = FALSE;
+
+ TRACE(("gdth_wait() hanum %d index %d time %ld\n",hanum,index,time));
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ if (index == 0)
+ return 1; /* no wait required */
+
+ gdth_from_wait = TRUE;
+ do {
+#if LINUX_VERSION_CODE >= 0x010346
+ gdth_interrupt((int)ha->irq,NULL,NULL);
+#else
+ gdth_interrupt((int)ha->irq,NULL);
+#endif
+ if (wait_hanum==hanum && wait_index==index) {
+ answer_found = TRUE;
+ break;
+ }
+ udelay(1000);
+ } while (--time);
+ gdth_from_wait = FALSE;
+
+ while (gdth_test_busy(hanum))
+ udelay(1);
+
+ return (answer_found);
+}
+
+
+static int gdth_internal_cmd(int hanum,unchar service,ushort opcode,ulong p1,
+ ulong p2,ulong p3)
+{
+ register gdth_ha_str *ha;
+ register gdth_cmd_str *cmd_ptr;
+ int retries,index;
+
+ TRACE2(("gdth_internal_cmd() service %d opcode %d\n",service,opcode));
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ cmd_ptr = ha->pccb;
+ memset((char*)cmd_ptr,0,sizeof(gdth_cmd_str));
+
+ /* make command */
+ for (retries = INIT_RETRIES;;) {
+ cmd_ptr->Service = service;
+ cmd_ptr->RequestBuffer = INTERNAL_CMND;
+ if (!(index=gdth_get_cmd_index(hanum))) {
+ TRACE(("GDT: No free command index found\n"));
+ return 0;
+ }
+ gdth_set_sema0(hanum);
+ cmd_ptr->OpCode = opcode;
+ cmd_ptr->BoardNode = LOCALBOARD;
+ if (service == CACHESERVICE) {
+ if (opcode == GDT_IOCTL) {
+ cmd_ptr->u.ioctl.subfunc = p1;
+ cmd_ptr->u.ioctl.channel = p2;
+ cmd_ptr->u.ioctl.param_size = (ushort)p3;
+ cmd_ptr->u.ioctl.p_param = virt_to_bus(ha->pscratch);
+ } else {
+ cmd_ptr->u.cache.DeviceNo = (ushort)p1;
+ cmd_ptr->u.cache.BlockNo = p2;
+ }
+ } else if (service == SCSIRAWSERVICE) {
+ cmd_ptr->u.raw.direction = p1;
+ cmd_ptr->u.raw.bus = (unchar)p2;
+ cmd_ptr->u.raw.target = (unchar)p3;
+ cmd_ptr->u.raw.lun = 0;
+ }
+ ha->cmd_len = sizeof(gdth_cmd_str);
+ ha->cmd_offs_dpmem = 0;
+ ha->cmd_cnt = 0;
+ gdth_copy_command(hanum);
+ gdth_release_event(hanum);
+ JIFFYWAIT(2);
+ if (!gdth_wait(hanum,index,INIT_TIMEOUT)) {
+ printk("GDT: Initialization error (timeout service %d)\n",service);
+ return 0;
+ }
+ if (ha->status != S_BSY || --retries == 0)
+ break;
+ udelay(1000);
+ }
+
+ return (ha->status != S_OK ? 0:1);
+}
+
+
+/* search for devices */
+
+static int gdth_search_drives(int hanum)
+{
+ register gdth_ha_str *ha;
+ ushort cdev_cnt,i;
+ unchar b,t,pos_found;
+ ulong drv_cyls, drv_hds, drv_secs;
+ ulong bus_no;
+ gdth_getch_str *chn;
+
+ TRACE(("gdth_search_drives() hanum %d\n",hanum));
+ ha = HADATA(gdth_ctr_tab[hanum]);
+
+ /* initialize controller services, at first: screen service */
+ if (!gdth_internal_cmd(hanum,SCREENSERVICE,GDT_INIT,0,0,0)) {
+ printk("GDT: Initialization error screen service (code %d)\n",
+ ha->status);
+ return 0;
+ }
+ TRACE2(("gdth_search_drives(): SCREENSERVICE initialized\n"));
+
+ /* initialize cache service */
+ if (!gdth_internal_cmd(hanum,CACHESERVICE,GDT_INIT,LINUX_OS,0,0)) {
+ printk("GDT: Initialization error cache service (code %d)\n",
+ ha->status);
+ return 0;
+ }
+ TRACE2(("gdth_search_drives(): CACHESERVICE initialized\n"));
+ cdev_cnt = (ushort)ha->info;
+
+ /* mount all cache devices */
+ gdth_internal_cmd(hanum,CACHESERVICE,GDT_MOUNT,0xffff,1,0);
+ TRACE2(("gdth_search_drives(): mountall CACHESERVICE OK\n"));
+
+ /* initialize cache service after mountall */
+ if (!gdth_internal_cmd(hanum,CACHESERVICE,GDT_INIT,LINUX_OS,0,0)) {
+ printk("GDT: Initialization error cache service (code %d)\n",
+ ha->status);
+ return 0;
+ }
+ TRACE2(("gdth_search_drives() CACHES. init. after mountall\n"));
+ cdev_cnt = (ushort)ha->info;
+
+ /* detect number of SCSI buses */
+ chn = (gdth_getch_str *)DMADATA(gdth_ctr_tab[hanum]);
+ for (bus_no=0; bus_no<MAXBUS; ++bus_no) {
+ chn->channel_no = bus_no;
+ if (!gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL,
+ SCSI_CHAN_CNT | L_CTRL_PATTERN,
+ IO_CHANNEL | INVALID_CHANNEL,
+ sizeof(gdth_getch_str))) {
+ if (bus_no == 0) {
+ printk("GDT: Error detecting SCSI channel count (0x%x)\n",
+ ha->status);
+ return 0;
+ }
+ break;
+ }
+ if (chn->siop_id < MAXID)
+ ha->id[bus_no][chn->siop_id].type = SIOP_DTYP;
+ }
+ ha->bus_cnt = (unchar)bus_no;
+ TRACE2(("gdth_search_drives() %d SCSI channels\n",ha->bus_cnt));
+
+ /* read cache configuration */
+ if (!gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL,CACHE_INFO,
+ INVALID_CHANNEL,sizeof(gdth_cinfo_str))) {
+ printk("GDT: Initialization error cache service (code %d)\n",
+ ha->status);
+ return 0;
+ }
+ ha->cpar = ((gdth_cinfo_str *)DMADATA(gdth_ctr_tab[hanum]))->cpar;
+ TRACE2(("gdth_search_drives() cinfo: vs %lx sta %d str %d dw %d b %d\n",
+ ha->cpar.version,ha->cpar.state,ha->cpar.strategy,
+ ha->cpar.write_back,ha->cpar.block_size));
+
+ /* initialize raw service */
+ if (!gdth_internal_cmd(hanum,SCSIRAWSERVICE,GDT_INIT,0,0,0)) {
+ printk("GDT: Initialization error raw service (code %d)\n",
+ ha->status);
+ return 0;
+ }
+ TRACE2(("gdth_search_drives(): RAWSERVICE initialized\n"));
+
+ /* set/get features raw service (scatter/gather) */
+ ha->raw_feat = 0;
+ if (gdth_internal_cmd(hanum,SCSIRAWSERVICE,GDT_SET_FEAT,SCATTER_GATHER,
+ 0,0)) {
+ TRACE2(("gdth_search_drives(): set features RAWSERVICE OK\n"));
+ if (gdth_internal_cmd(hanum,SCSIRAWSERVICE,GDT_GET_FEAT,0,0,0))
+ {
+ TRACE2(("gdth_search_dr(): get feat RAWSERVICE %ld\n",
+ ha->info));
+ ha->raw_feat = (ushort)ha->info;
+ }
+ }
+
+ /* set/get features cache service (equal to raw service) */
+ if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_SET_FEAT,0,
+ SCATTER_GATHER,0)) {
+ TRACE2(("gdth_search_drives(): set features CACHESERVICE OK\n"));
+ if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_GET_FEAT,0,0,0)) {
+ TRACE2(("gdth_search_dr(): get feat CACHESERV. %ld\n",
+ ha->info));
+ ha->cache_feat = (ushort)ha->info;
+ }
+ }
+
+ /* scanning for raw devices */
+ for (b=0; b<ha->bus_cnt; ++b) {
+ for (t=0; t<MAXID; ++t) {
+ TRACE(("gdth_search_drives() rawd. bus %d id %d\n",b,t));
+ if (ha->id[b][t].type != SIOP_DTYP &&
+ gdth_internal_cmd(hanum,SCSIRAWSERVICE,GDT_INFO,0,b,t)) {
+ ha->id[b][t].type = RAW_DTYP;
+ }
+ }
+ }
+
+ /* scanning for cache devices */
+ for (i=0; i<cdev_cnt && i<MAX_HDRIVES; ++i) {
+ TRACE(("gdth_search_drives() cachedev. %d\n",i));
+ if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_INFO,i,0,0)) {
+ /* dynamic relation between host drive number and Bus/ID */
+ /* search free position */
+ pos_found = FALSE;
+ for (b=0,t=0; b<ha->bus_cnt; ++b) {
+ for (t=0; t<MAXID; ++t) {
+ if (ha->id[b][t].type == EMPTY_DTYP) {
+ pos_found = TRUE;
+ break;
+ }
+ }
+ if (pos_found)
+ break;
+ }
+ TRACE(("gdth_search_dr() drive %d free pos at bus/id %d/%d\n",
+ i,b,t));
+
+ ha->id[b][t].type = CACHE_DTYP;
+ ha->id[b][t].devtype = 0;
+ ha->id[b][t].size = ha->info;
+ ha->id[b][t].hostdrive = i;
+
+ /* evaluate mapping (sectors per head, heads per cylinder) */
+ ha->id[b][t].size &= ~SECS32;
+ if (ha->info2 == 0) {
+ drv_cyls = ha->id[b][t].size /HEADS/SECS;
+ if (drv_cyls <= MAXCYLS) {
+ drv_hds = HEADS;
+ drv_secs= SECS;
+ } else { /* too high for 64*32 */
+ drv_cyls = ha->id[b][t].size /MEDHEADS/MEDSECS;
+ if (drv_cyls <= MAXCYLS) {
+ drv_hds = MEDHEADS;
+ drv_secs= MEDSECS;
+ } else { /* too high for 127*63 */
+ drv_cyls = ha->id[b][t].size /BIGHEADS/BIGSECS;
+ drv_hds = BIGHEADS;
+ drv_secs= BIGSECS;
+ }
+ }
+ } else {
+ drv_hds = ha->info2 & 0xff;
+ drv_secs = (ha->info2 >> 8) & 0xff;
+ drv_cyls = ha->id[b][t].size /drv_hds/drv_secs;
+ }
+ ha->id[b][t].heads = (unchar)drv_hds;
+ ha->id[b][t].secs = (unchar)drv_secs;
+ /* round size */
+ ha->id[b][t].size = drv_cyls * drv_hds * drv_secs;
+ TRACE2(("gdth_search_dr() cdr. %d size %ld hds %ld scs %ld\n",
+ i,ha->id[b][t].size,drv_hds,drv_secs));
+
+ /* get informations about device */
+ if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_DEVTYPE,i,
+ 0,0)) {
+ TRACE(("gdth_search_dr() cache drive %d devtype %ld\n",
+ i,ha->info));
+ ha->id[b][t].devtype = (ushort)ha->info;
+ }
+ }
+ }
+
+ TRACE(("gdth_search_drives() OK\n"));
+ return 1;
+}
+
+
+/* command queueing/sending functions */
+
+static void gdth_putq(int hanum,Scsi_Cmnd *scp,unchar priority)
+{
+ register gdth_ha_str *ha;
+ register Scsi_Cmnd *pscp;
+ register Scsi_Cmnd *nscp;
+ ulong flags;
+ unchar b, t;
+
+ TRACE(("gdth_putq() priority %d\n",priority));
+ save_flags(flags);
+ cli();
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ scp->SCp.this_residual = (int)priority;
+ gdth_update_timeout(scp, scp->timeout * 6);
+#if LINUX_VERSION_CODE >= 0x020000
+ b = scp->channel;
+#else
+ b = NUMDATA(nscp->host)->busnum;
+#endif
+ t = scp->target;
+#if LINUX_VERSION_CODE >= 0x010300
+ if (priority >= DEFAULT_PRI && ha->id[b][t].lock) {
+ TRACE2(("gdth_putq(): locked IO -> update_timeout()\n"));
+ scp->SCp.buffers_residual = gdth_update_timeout(scp, 0);
+ }
+#endif
+
+ if (ha->req_first==NULL) {
+ ha->req_first = scp; /* queue was empty */
+ scp->SCp.ptr = NULL;
+ } else { /* queue not empty */
+ pscp = ha->req_first;
+ nscp = (Scsi_Cmnd *)pscp->SCp.ptr;
+ /* priority: 0-highest,..,0xff-lowest */
+ while (nscp && (unchar)nscp->SCp.this_residual <= priority) {
+ pscp = nscp;
+ nscp = (Scsi_Cmnd *)pscp->SCp.ptr;
+ }
+ pscp->SCp.ptr = (char *)scp;
+ scp->SCp.ptr = (char *)nscp;
+ }
+ restore_flags(flags);
+
+#ifdef GDTH_STATISTICS
+ flags = 0;
+ for (nscp=ha->req_first; nscp; nscp=(Scsi_Cmnd*)nscp->SCp.ptr)
+ ++flags;
+ if (max_rq < flags) {
+ max_rq = flags;
+ TRACE3(("GDT: max_rq = %d\n",(ushort)max_rq));
+ }
+#endif
+}
+
+static void gdth_next(int hanum)
+{
+ register gdth_ha_str *ha;
+ register Scsi_Cmnd *pscp;
+ register Scsi_Cmnd *nscp;
+ unchar b, t, next_cmd, firsttime;
+ ushort hdrive;
+ ulong flags;
+ int cmd_index;
+
+ TRACE(("gdth_next() hanum %d\n",hanum));
+ save_flags(flags);
+ cli();
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ ha->cmd_cnt = ha->cmd_offs_dpmem = 0;
+ next_cmd = firsttime = TRUE;
+ cmd_index = 0;
+
+ for (nscp = pscp = ha->req_first; nscp; nscp = (Scsi_Cmnd *)nscp->SCp.ptr) {
+ if (nscp != pscp && nscp != (Scsi_Cmnd *)pscp->SCp.ptr)
+ pscp = (Scsi_Cmnd *)pscp->SCp.ptr;
+#if LINUX_VERSION_CODE >= 0x020000
+ b = nscp->channel;
+#else
+ b = NUMDATA(nscp->host)->busnum;
+#endif
+ t = nscp->target;
+ if (nscp->SCp.this_residual < DEFAULT_PRI || !ha->id[b][t].lock) {
+
+ if (firsttime) {
+ if (gdth_test_busy(hanum)) { /* controller busy ? */
+ TRACE(("gdth_next() controller %d busy !\n",hanum));
+ if (!gdth_polling) {
+ restore_flags(flags);
+ return;
+ }
+ while (gdth_test_busy(hanum))
+ udelay(1000);
+ }
+ firsttime = FALSE;
+ }
+
+#if LINUX_VERSION_CODE >= 0x010300
+ if (nscp->done == gdth_scsi_done) {
+ if (!(cmd_index=gdth_special_cmd(hanum,nscp,b)))
+ next_cmd = FALSE;
+ } else
+#endif
+ if (ha->id[b][t].type != CACHE_DTYP) {
+ if (!(cmd_index=gdth_fill_raw_cmd(hanum,nscp,b)))
+ next_cmd = FALSE;
+ } else {
+ hdrive = ha->id[b][t].hostdrive;
+ switch (nscp->cmnd[0]) {
+ case TEST_UNIT_READY:
+ case INQUIRY:
+ case REQUEST_SENSE:
+ case READ_CAPACITY:
+ case VERIFY:
+ case START_STOP:
+ case MODE_SENSE:
+ TRACE2(("cache cmd %x/%x/%x/%x/%x/%x\n",nscp->cmnd[0],
+ nscp->cmnd[1],nscp->cmnd[2],nscp->cmnd[3],
+ nscp->cmnd[4],nscp->cmnd[5]));
+ gdth_internal_cache_cmd(hanum,nscp,b,&flags);
+ break;
+
+ case ALLOW_MEDIUM_REMOVAL:
+ TRACE2(("cache cmd %x/%x/%x/%x/%x/%x\n",nscp->cmnd[0],
+ nscp->cmnd[1],nscp->cmnd[2],nscp->cmnd[3],
+ nscp->cmnd[4],nscp->cmnd[5]));
+ if ( (nscp->cmnd[4]&1) && !(ha->id[b][t].devtype&1) ) {
+ TRACE2(("Prevent r. nonremov. drive->do nothing\n"));
+ nscp->result = DID_OK << 16;
+ restore_flags( flags );
+ nscp->scsi_done(nscp);
+ save_flags( flags );
+ cli();
+ } else {
+ nscp->cmnd[3] = (ha->id[b][t].devtype&1) ? 1:0;
+ TRACE2(("Prevent/allow r. %d rem. drive %d\n",
+ nscp->cmnd[4],nscp->cmnd[3]));
+ if (!(cmd_index=gdth_fill_cache_cmd(hanum,nscp,hdrive)))
+ next_cmd = FALSE;
+ }
+ break;
+
+ case READ_6:
+ case WRITE_6:
+ case READ_10:
+ case WRITE_10:
+ if (!(cmd_index=gdth_fill_cache_cmd(hanum,nscp,hdrive)))
+ next_cmd = FALSE;
+ break;
+
+ default:
+ TRACE2(("cache cmd %x/%x/%x/%x/%x/%x\n",nscp->cmnd[0],
+ nscp->cmnd[1],nscp->cmnd[2],nscp->cmnd[3],
+ nscp->cmnd[4],nscp->cmnd[5]));
+ printk("GDT: Unknown SCSI command 0x%x to cache service !\n",
+ nscp->cmnd[0]);
+ nscp->result = DID_ABORT << 16;
+ restore_flags( flags );
+ nscp->scsi_done( nscp );
+ save_flags( flags );
+ cli();
+ break;
+ }
+ }
+
+ if (!next_cmd)
+ break;
+ if (nscp == ha->req_first)
+ ha->req_first = pscp = (Scsi_Cmnd *)nscp->SCp.ptr;
+ else
+ pscp->SCp.ptr = nscp->SCp.ptr;
+ if (gdth_polling)
+ break;
+ }
+ }
+
+ if (ha->cmd_cnt > 0) {
+ gdth_release_event(hanum);
+ }
+
+ restore_flags(flags);
+
+ if (gdth_polling && ha->cmd_cnt > 0) {
+ if (!gdth_wait(hanum,cmd_index,POLL_TIMEOUT))
+ printk("GDT: Controller %d: Command %d timed out !\n",
+ hanum,cmd_index);
+ }
+}
+
+static void gdth_copy_internal_data(Scsi_Cmnd *scp,char *buffer,ushort count)
+{
+ ushort cpcount,i;
+ ushort cpsum,cpnow;
+ struct scatterlist *sl;
+
+ cpcount = count<=(ushort)scp->bufflen ? count:(ushort)scp->bufflen;
+ if (scp->use_sg) {
+ sl = (struct scatterlist *)scp->request_buffer;
+ for (i=0,cpsum=0; i<scp->use_sg; ++i,++sl) {
+ cpnow = (ushort)sl->length;
+ TRACE(("copy_internal() now %d sum %d count %d %d\n",
+ cpnow,cpsum,cpcount,(ushort)scp->bufflen));
+ if (cpsum+cpnow > cpcount)
+ cpnow = cpcount - cpsum;
+ cpsum += cpnow;
+ memcpy((char*)sl->address,buffer,cpnow);
+ if (cpsum == cpcount)
+ break;
+ buffer += cpnow;
+ }
+ } else {
+ TRACE(("copy_internal() count %d\n",cpcount));
+ memcpy((char*)scp->request_buffer,buffer,cpcount);
+ }
+}
+
+static int gdth_internal_cache_cmd(int hanum,Scsi_Cmnd *scp,
+ unchar b,ulong *flags)
+{
+ register gdth_ha_str *ha;
+ ushort hdrive;
+ unchar t;
+ gdth_inq_data inq;
+ gdth_rdcap_data rdc;
+ gdth_sense_data sd;
+ gdth_modep_data mpd;
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ t = scp->target;
+ hdrive = ha->id[b][t].hostdrive;
+ TRACE(("gdth_internal_cache_cmd() cmd 0x%x hdrive %d\n",
+ scp->cmnd[0],hdrive));
+
+ if (scp->lun !=0)
+ scp->result = DID_BAD_TARGET << 16;
+ else {
+ switch (scp->cmnd[0]) {
+ case TEST_UNIT_READY:
+ case VERIFY:
+ case START_STOP:
+ TRACE2(("Test/Verify/Start hdrive %d\n",hdrive));
+ break;
+
+ case INQUIRY:
+ TRACE2(("Inquiry hdrive %d devtype %d\n",
+ hdrive,ha->id[b][t].devtype));
+ inq.type_qual = (ha->id[b][t].devtype&4) ? TYPE_ROM:TYPE_DISK;
+ /* you can here set all disks to removable, if you want to do
+ a flush using the ALLOW_MEDIUM_REMOVAL command */
+ inq.modif_rmb = ha->id[b][t].devtype&1 ? 0x80:0x00;
+ inq.version = 2;
+ inq.resp_aenc = 2;
+ inq.add_length= 32;
+ strcpy(inq.vendor,"ICP ");
+ sprintf(inq.product,"Host Drive #%02d",hdrive);
+ strcpy(inq.revision," ");
+ gdth_copy_internal_data(scp,(char*)&inq,sizeof(gdth_inq_data));
+ break;
+
+ case REQUEST_SENSE:
+ TRACE2(("Request sense hdrive %d\n",hdrive));
+ sd.errorcode = 0x70;
+ sd.segno = 0x00;
+ sd.key = NO_SENSE;
+ sd.info = 0;
+ sd.add_length= 0;
+ gdth_copy_internal_data(scp,(char*)&sd,sizeof(gdth_sense_data));
+ break;
+
+ case MODE_SENSE:
+ TRACE2(("Mode sense hdrive %d\n",hdrive));
+ memset((char*)&mpd,0,sizeof(gdth_modep_data));
+ mpd.hd.data_length = sizeof(gdth_modep_data);
+ mpd.hd.dev_par = (ha->id[b][t].devtype&2) ? 0x80:0;
+ mpd.hd.bd_length = sizeof(mpd.bd);
+ mpd.bd.block_length[0] = (SECTOR_SIZE & 0x00ff0000) >> 16;
+ mpd.bd.block_length[1] = (SECTOR_SIZE & 0x0000ff00) >> 8;
+ mpd.bd.block_length[2] = (SECTOR_SIZE & 0x000000ff);
+ gdth_copy_internal_data(scp,(char*)&mpd,sizeof(gdth_modep_data));
+ break;
+
+ case READ_CAPACITY:
+ TRACE2(("Read capacity hdrive %d\n",hdrive));
+ rdc.last_block_no = ntohl(ha->id[b][t].size-1);
+ rdc.block_length = ntohl(SECTOR_SIZE);
+ gdth_copy_internal_data(scp,(char*)&rdc,sizeof(gdth_rdcap_data));
+ break;
+
+ default:
+ TRACE2(("Internal cache cmd 0x%x unknown\n",scp->cmnd[0]));
+ break;
+ }
+ scp->result = DID_OK << 16;
+ }
+
+ restore_flags(*flags);
+ scp->scsi_done(scp);
+ save_flags(*flags);
+ cli();
+ return 1;
+}
+
+static int gdth_fill_cache_cmd(int hanum,Scsi_Cmnd *scp,ushort hdrive)
+{
+ register gdth_ha_str *ha;
+ register gdth_cmd_str *cmdp;
+ struct scatterlist *sl;
+ ushort i;
+ int cmd_index;
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ cmdp = ha->pccb;
+ TRACE(("gdth_fill_cache_cmd() cmd 0x%x cmdsize %d hdrive %d\n",
+ scp->cmnd[0],scp->cmd_len,hdrive));
+
+ if (ha->type==GDT_EISA && ha->cmd_cnt>0)
+ return 0;
+
+ cmdp->Service = CACHESERVICE;
+ cmdp->RequestBuffer = scp;
+ /* search free command index */
+ if (!(cmd_index=gdth_get_cmd_index(hanum))) {
+ TRACE(("GDT: No free command index found\n"));
+ return 0;
+ }
+ /* if it's the first command, set command semaphore */
+ if (ha->cmd_cnt == 0)
+ gdth_set_sema0(hanum);
+
+ /* fill command */
+ if (scp->cmnd[0]==ALLOW_MEDIUM_REMOVAL) {
+ if (scp->cmnd[4] & 1) /* prevent ? */
+ cmdp->OpCode = GDT_MOUNT;
+ else if (scp->cmnd[3] & 1) /* removable drive ? */
+ cmdp->OpCode = GDT_UNMOUNT;
+ else
+ cmdp->OpCode = GDT_FLUSH;
+ } else {
+ if (scp->cmnd[0]==WRITE_6 || scp->cmnd[0]==WRITE_10) {
+ if (gdth_write_through)
+ cmdp->OpCode = GDT_WRITE_THR;
+ else
+ cmdp->OpCode = GDT_WRITE;
+ } else {
+ cmdp->OpCode = GDT_READ;
+ }
+ }
+
+ cmdp->BoardNode = LOCALBOARD;
+ cmdp->u.cache.DeviceNo = hdrive;
+
+ if (scp->cmnd[0]==ALLOW_MEDIUM_REMOVAL) {
+ cmdp->u.cache.BlockNo = 1;
+ cmdp->u.cache.sg_canz = 0;
+ } else {
+ if (scp->cmd_len != 6) {
+ cmdp->u.cache.BlockNo = ntohl(*(ulong*)&scp->cmnd[2]);
+ cmdp->u.cache.BlockCnt= (ulong)ntohs(*(ushort*)&scp->cmnd[7]);
+ } else {
+ cmdp->u.cache.BlockNo = ntohl(*(ulong*)&scp->cmnd[0]) & 0x001fffffUL;
+ cmdp->u.cache.BlockCnt= scp->cmnd[4]==0 ? 0x100 : scp->cmnd[4];
+ }
+
+ if (scp->use_sg) {
+ cmdp->u.cache.DestAddr= -1UL;
+ sl = (struct scatterlist *)scp->request_buffer;
+ for (i=0; i<scp->use_sg; ++i,++sl) {
+ cmdp->u.cache.sg_lst[i].sg_ptr = virt_to_bus(sl->address);
+ cmdp->u.cache.sg_lst[i].sg_len = (ulong)sl->length;
+ }
+ cmdp->u.cache.sg_canz = (ulong)i;
+
+#ifdef GDTH_STATISTICS
+ if (max_sg < (ulong)i) {
+ max_sg = (ulong)i;
+ TRACE3(("GDT: max_sg = %d\n",i));
+ }
+#endif
+ if (i<GDTH_MAXSG)
+ cmdp->u.cache.sg_lst[i].sg_len = 0;
+ } else {
+ if (ha->cache_feat & SCATTER_GATHER) {
+ cmdp->u.cache.DestAddr = -1UL;
+ cmdp->u.cache.sg_canz = 1;
+ cmdp->u.cache.sg_lst[0].sg_ptr = virt_to_bus(scp->request_buffer);
+ cmdp->u.cache.sg_lst[0].sg_len = scp->request_bufflen;
+ cmdp->u.cache.sg_lst[1].sg_len = 0;
+ } else {
+ cmdp->u.cache.DestAddr = virt_to_bus(scp->request_buffer);
+ cmdp->u.cache.sg_canz= 0;
+ }
+ }
+ }
+ TRACE(("cache cmd: addr. %lx sganz %lx sgptr0 %lx sglen0 %lx\n",
+ cmdp->u.cache.DestAddr,cmdp->u.cache.sg_canz,
+ cmdp->u.cache.sg_lst[0].sg_ptr,
+ cmdp->u.cache.sg_lst[0].sg_len));
+ TRACE(("cache cmd: cmd %d blockno. %ld, blockcnt %ld\n",
+ cmdp->OpCode,cmdp->u.cache.BlockNo,cmdp->u.cache.BlockCnt));
+
+ /* evaluate command size, check space */
+ ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.cache.sg_lst) +
+ (ushort)cmdp->u.cache.sg_canz * sizeof(gdth_sg_str);
+ if (ha->cmd_len & 3)
+ ha->cmd_len += (4 - (ha->cmd_len & 3));
+
+ if (ha->cmd_cnt > 0) {
+ if ((ha->cmd_offs_dpmem + ha->cmd_len + DPMEM_COMMAND_OFFSET) >
+ ha->ic_all_size) {
+ TRACE2(("gdth_fill_cache() DPMEM overflow\n"));
+ gdth_cmd_tab[cmd_index-2][hanum].cmnd = UNUSED_CMND;
+ return 0;
+ }
+ }
+
+ /* copy command */
+ gdth_copy_command(hanum);
+ return cmd_index;
+}
+
+static int gdth_fill_raw_cmd(int hanum,Scsi_Cmnd *scp,unchar b)
+{
+ register gdth_ha_str *ha;
+ register gdth_cmd_str *cmdp;
+ struct scatterlist *sl;
+ ushort i;
+ int cmd_index;
+ unchar t,l;
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ t = scp->target;
+ l = scp->lun;
+ cmdp = ha->pccb;
+ TRACE(("gdth_fill_raw_cmd() cmd 0x%x bus %d ID %d LUN %d\n",
+ scp->cmnd[0],b,t,l));
+
+ if (ha->type==GDT_EISA && ha->cmd_cnt>0)
+ return 0;
+
+ cmdp->Service = SCSIRAWSERVICE;
+ cmdp->RequestBuffer = scp;
+ /* search free command index */
+ if (!(cmd_index=gdth_get_cmd_index(hanum))) {
+ TRACE(("GDT: No free command index found\n"));
+ return 0;
+ }
+ /* if it's the first command, set command semaphore */
+ if (ha->cmd_cnt == 0)
+ gdth_set_sema0(hanum);
+
+ /* fill command */
+ cmdp->OpCode = GDT_WRITE; /* always */
+ cmdp->BoardNode = LOCALBOARD;
+ cmdp->u.raw.reserved = 0;
+ cmdp->u.raw.mdisc_time = 0;
+ cmdp->u.raw.mcon_time = 0;
+ cmdp->u.raw.clen = scp->cmd_len;
+ cmdp->u.raw.target = t;
+ cmdp->u.raw.lun = l;
+ cmdp->u.raw.bus = b;
+ cmdp->u.raw.priority = 0;
+ cmdp->u.raw.link_p = NULL;
+ cmdp->u.raw.sdlen = scp->request_bufflen;
+ cmdp->u.raw.sense_len = 16;
+ cmdp->u.raw.sense_data = virt_to_bus(scp->sense_buffer);
+ cmdp->u.raw.direction =
+ gdth_direction_tab[scp->cmnd[0]]==DOU ? DATA_OUT : DATA_IN;
+ memcpy(cmdp->u.raw.cmd,scp->cmnd,12);
+
+ if (scp->use_sg) {
+ cmdp->u.raw.sdata = -1UL;
+ sl = (struct scatterlist *)scp->request_buffer;
+ for (i=0; i<scp->use_sg; ++i,++sl) {
+ cmdp->u.raw.sg_lst[i].sg_ptr = virt_to_bus(sl->address);
+ cmdp->u.raw.sg_lst[i].sg_len = (ulong)sl->length;
+ }
+ cmdp->u.raw.sg_ranz = (ulong)i;
+
+#ifdef GDTH_STATISTICS
+ if (max_sg < (ulong)i) {
+ max_sg = (ulong)i;
+ TRACE3(("GDT: max_sg = %d\n",i));
+ }
+#endif
+ if (i<GDTH_MAXSG)
+ cmdp->u.raw.sg_lst[i].sg_len = 0;
+ } else {
+ if (ha->raw_feat & SCATTER_GATHER) {
+ cmdp->u.raw.sdata = -1UL;
+ cmdp->u.raw.sg_ranz= 1;
+ cmdp->u.raw.sg_lst[0].sg_ptr = virt_to_bus(scp->request_buffer);
+ cmdp->u.raw.sg_lst[0].sg_len = scp->request_bufflen;
+ cmdp->u.raw.sg_lst[1].sg_len = 0;
+ } else {
+ cmdp->u.raw.sdata = virt_to_bus(scp->request_buffer);
+ cmdp->u.raw.sg_ranz= 0;
+ }
+ }
+ TRACE(("raw cmd: addr. %lx sganz %lx sgptr0 %lx sglen0 %lx\n",
+ cmdp->u.raw.sdata,cmdp->u.raw.sg_ranz,
+ cmdp->u.raw.sg_lst[0].sg_ptr,
+ cmdp->u.raw.sg_lst[0].sg_len));
+
+ /* evaluate command size, check space */
+ ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.raw.sg_lst) +
+ (ushort)cmdp->u.raw.sg_ranz * sizeof(gdth_sg_str);
+ if (ha->cmd_len & 3)
+ ha->cmd_len += (4 - (ha->cmd_len & 3));
+
+ if (ha->cmd_cnt > 0) {
+ if ((ha->cmd_offs_dpmem + ha->cmd_len + DPMEM_COMMAND_OFFSET) >
+ ha->ic_all_size) {
+ TRACE2(("gdth_fill_raw() DPMEM overflow\n"));
+ gdth_cmd_tab[cmd_index-2][hanum].cmnd = UNUSED_CMND;
+ return 0;
+ }
+ }
+
+ /* copy command */
+ gdth_copy_command(hanum);
+ return cmd_index;
+}
+
+static int gdth_special_cmd(int hanum,Scsi_Cmnd *scp,unchar b)
+{
+ register gdth_ha_str *ha;
+ register gdth_cmd_str *cmdp;
+ int cmd_index;
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ cmdp= ha->pccb;
+ TRACE2(("gdth_special_cmd(): "));
+
+ if (ha->type==GDT_EISA && ha->cmd_cnt>0)
+ return 0;
+
+ memcpy( cmdp, scp->request_buffer, sizeof(gdth_cmd_str));
+ cmdp->RequestBuffer = scp;
+
+ /* search free command index */
+ if (!(cmd_index=gdth_get_cmd_index(hanum))) {
+ TRACE(("GDT: No free command index found\n"));
+ return 0;
+ }
+
+ /* if it's the first command, set command semaphore */
+ if (ha->cmd_cnt == 0)
+ gdth_set_sema0(hanum);
+
+ /* evaluate command size, check space */
+ if (cmdp->OpCode == GDT_IOCTL) {
+ TRACE2(("IOCTL\n"));
+ ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.ioctl.p_param) + sizeof(ulong);
+ } else if (cmdp->Service == CACHESERVICE) {
+ TRACE2(("cache command %d\n",cmdp->OpCode));
+ ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.cache.sg_lst) + sizeof(gdth_sg_str);
+ } else if (cmdp->Service == SCSIRAWSERVICE) {
+ TRACE2(("raw command %d/%d\n",cmdp->OpCode,cmdp->u.raw.cmd[0]));
+ ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.raw.sg_lst) + sizeof(gdth_sg_str);
+ }
+
+ if (ha->cmd_len & 3)
+ ha->cmd_len += (4 - (ha->cmd_len & 3));
+
+ if (ha->cmd_cnt > 0) {
+ if ((ha->cmd_offs_dpmem + ha->cmd_len + DPMEM_COMMAND_OFFSET) >
+ ha->ic_all_size) {
+ TRACE2(("gdth_special_cmd() DPMEM overflow\n"));
+ gdth_cmd_tab[cmd_index-2][hanum].cmnd = UNUSED_CMND;
+ return 0;
+ }
+ }
+
+ /* copy command */
+ gdth_copy_command(hanum);
+ return cmd_index;
+}
+
+
+/* Controller event handling functions */
+static gdth_evt_str *gdth_store_event(ushort source, ushort idx,
+ gdth_evt_data *evt)
+{
+ gdth_evt_str *e;
+ ulong flags;
+ struct timeval tv;
+
+ TRACE2(("gdth_store_event() source %d idx %d\n", source, idx));
+ if (source == 0) /* no source -> no event */
+ return 0;
+
+ save_flags(flags);
+ cli();
+ if (ebuffer[elastidx].event_source == source &&
+ ebuffer[elastidx].event_idx == idx &&
+ !memcmp((char *)&ebuffer[elastidx].event_data.eu,
+ (char *)&evt->eu, evt->size)) {
+ e = &ebuffer[elastidx];
+ do_gettimeofday(&tv);
+ e->last_stamp = tv.tv_sec;
+ ++e->same_count;
+ } else {
+ if (ebuffer[elastidx].event_source != 0) { /* entry not free ? */
+ ++elastidx;
+ if (elastidx == MAX_EVENTS)
+ elastidx = 0;
+ if (elastidx == eoldidx) { /* reached mark ? */
+ ++eoldidx;
+ if (eoldidx == MAX_EVENTS)
+ eoldidx = 0;
+ }
+ }
+ e = &ebuffer[elastidx];
+ e->event_source = source;
+ e->event_idx = idx;
+ do_gettimeofday(&tv);
+ e->first_stamp = e->last_stamp = tv.tv_sec;
+ e->same_count = 1;
+ e->event_data = *evt;
+ }
+ restore_flags(flags);
+ return e;
+}
+
+static int gdth_read_event(int handle, gdth_evt_str *estr)
+{
+ gdth_evt_str *e;
+ int eindex;
+ ulong flags;
+
+ TRACE2(("gdth_read_event() handle %d\n", handle));
+ save_flags(flags);
+ cli();
+ if (handle == -1)
+ eindex = eoldidx;
+ else
+ eindex = handle;
+ estr->event_source = 0;
+
+ if (eindex >= MAX_EVENTS) {
+ restore_flags(flags);
+ return eindex;
+ }
+ e = &ebuffer[eindex];
+ if (e->event_source != 0) {
+ if (eindex != elastidx) {
+ if (++eindex == MAX_EVENTS)
+ eindex = 0;
+ } else {
+ eindex = -1;
+ }
+ memcpy(estr, e, sizeof(gdth_evt_str));
+ }
+ restore_flags(flags);
+ return eindex;
+}
+
+static void gdth_readapp_event(unchar application, gdth_evt_str *estr)
+{
+ gdth_evt_str *e;
+ int eindex;
+ ulong flags;
+ unchar found = FALSE;
+
+ TRACE2(("gdth_readapp_event() app. %d\n", application));
+ save_flags(flags);
+ cli();
+ eindex = eoldidx;
+ for (;;) {
+ e = &ebuffer[eindex];
+ if (e->event_source == 0)
+ break;
+ if ((e->application & application) == 0) {
+ e->application |= application;
+ found = TRUE;
+ break;
+ }
+ if (eindex == elastidx)
+ break;
+ if (++eindex == MAX_EVENTS)
+ eindex = 0;
+ }
+ if (found)
+ memcpy(estr, e, sizeof(gdth_evt_str));
+ else
+ estr->event_source = 0;
+ restore_flags(flags);
+}
+
+static void gdth_clear_events()
+{
+ ulong flags;
+
+ TRACE(("gdth_clear_events()"));
+ save_flags(flags);
+ cli();
+
+ eoldidx = elastidx = 0;
+ ebuffer[0].event_source = 0;
+ restore_flags(flags);
+}
+
+
+/* SCSI interface functions */
+
+#if LINUX_VERSION_CODE >= 0x010346
+static void gdth_interrupt(int irq,void *dev_id,struct pt_regs *regs)
+#else
+static void gdth_interrupt(int irq,struct pt_regs *regs)
+#endif
+{
+ register gdth_ha_str *ha;
+ gdt6m_dpram_str *dp6m_ptr;
+ gdt6_dpram_str *dp6_ptr;
+ gdt2_dpram_str *dp2_ptr;
+ Scsi_Cmnd *scp;
+ int hanum;
+ unchar IStatus;
+ ushort CmdStatus, Service = 0;
+ ulong InfoBytes, InfoBytes2 = 0;
+ gdth_evt_data dvr;
+
+ TRACE(("gdth_interrupt() IRQ %d\n",irq));
+
+ /* if polling and not from gdth_wait() -> return */
+ if (gdth_polling) {
+ if (!gdth_from_wait) {
+ return;
+ }
+ }
+
+ wait_index = 0;
+
+ /* search controller */
+ if ((hanum = gdth_get_status(&IStatus,irq)) == -1) {
+ /*
+ TRACE2(("gdth_interrupt(): Spurious interrupt received\n"));
+ */
+ return;
+ }
+
+#ifdef GDTH_STATISTICS
+ ++act_ints;
+#endif
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ if (ha->type == GDT_EISA) {
+ if (IStatus & 0x80) { /* error flag */
+ IStatus &= ~0x80;
+ CmdStatus = inw(ha->bmic + MAILBOXREG+8);
+ TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus));
+ if (IStatus == ASYNCINDEX) { /* async. event ? */
+ Service = inw(ha->bmic + MAILBOXREG+10);
+ InfoBytes2 = inl(ha->bmic + MAILBOXREG+4);
+ }
+ } else /* no error */
+ CmdStatus = S_OK;
+ InfoBytes = inl(ha->bmic + MAILBOXREG+12);
+ if (gdth_polling) /* init. -> more info */
+ InfoBytes2 = inl(ha->bmic + MAILBOXREG+4);
+ outb(0xff, ha->bmic + EDOORREG); /* acknowledge interrupt */
+ outb(0x00, ha->bmic + SEMA1REG); /* reset status semaphore */
+ } else if (ha->type == GDT_ISA) {
+ dp2_ptr = (gdt2_dpram_str *)ha->brd;
+ if (IStatus & 0x80) { /* error flag */
+ IStatus &= ~0x80;
+ CmdStatus = readw(&dp2_ptr->u.ic.Status);
+ TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus));
+ if (IStatus == ASYNCINDEX) { /* async. event ? */
+ Service = readw(&dp2_ptr->u.ic.Service);
+ InfoBytes2 = readl(&dp2_ptr->u.ic.Info[1]);
+ }
+ } else /* no error */
+ CmdStatus = S_OK;
+ InfoBytes = readl(&dp2_ptr->u.ic.Info[0]);
+ if (gdth_polling) /* init. -> more info */
+ InfoBytes2 = readl(&dp2_ptr->u.ic.Info[1]);
+ writeb(0xff, &dp2_ptr->io.irqdel); /* acknowledge interrupt */
+ writeb(0, &dp2_ptr->u.ic.Cmd_Index); /* reset command index */
+ writeb(0, &dp2_ptr->io.Sema1); /* reset status semaphore */
+ } else if (ha->type == GDT_PCI) {
+ dp6_ptr = (gdt6_dpram_str *)ha->brd;
+ if (IStatus & 0x80) { /* error flag */
+ IStatus &= ~0x80;
+ CmdStatus = readw(&dp6_ptr->u.ic.Status);
+ TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus));
+ if (IStatus == ASYNCINDEX) { /* async. event ? */
+ Service = readw(&dp6_ptr->u.ic.Service);
+ InfoBytes2 = readl(&dp6_ptr->u.ic.Info[1]);
+ }
+ } else /* no error */
+ CmdStatus = S_OK;
+ InfoBytes = readl(&dp6_ptr->u.ic.Info[0]);
+ if (gdth_polling) /* init. -> more info */
+ InfoBytes2 = readl(&dp6_ptr->u.ic.Info[1]);
+ writeb(0xff, &dp6_ptr->io.irqdel); /* acknowledge interrupt */
+ writeb(0, &dp6_ptr->u.ic.Cmd_Index); /* reset command index */
+ writeb(0, &dp6_ptr->io.Sema1); /* reset status semaphore */
+ } else if (ha->type == GDT_PCINEW) {
+ if (IStatus & 0x80) { /* error flag */
+ IStatus &= ~0x80;
+ CmdStatus = inw(PTR2USHORT(&ha->plx->status));
+ TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus));
+ if (IStatus == ASYNCINDEX) { /* async. event ? */
+ Service = inw(PTR2USHORT(&ha->plx->service));
+ InfoBytes2 = inl(PTR2USHORT(&ha->plx->info[1]));
+ }
+ } else
+ CmdStatus = S_OK;
+
+ InfoBytes = inl(PTR2USHORT(&ha->plx->info[0]));
+ if (gdth_polling) /* init. -> more info */
+ InfoBytes2 = inl(PTR2USHORT(&ha->plx->info[1]));
+ outb(0xff, PTR2USHORT(&ha->plx->edoor_reg));
+ outb(0x00, PTR2USHORT(&ha->plx->sema1_reg));
+ } else if (ha->type == GDT_PCIMPR) {
+ dp6m_ptr = (gdt6m_dpram_str *)ha->brd;
+ if (IStatus & 0x80) { /* error flag */
+ IStatus &= ~0x80;
+ CmdStatus = readw(&dp6m_ptr->i960r.status);
+ TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus));
+ if (IStatus == ASYNCINDEX) { /* async. event ? */
+ Service = readw(&dp6m_ptr->i960r.service);
+ InfoBytes2 = readl(&dp6m_ptr->i960r.info[1]);
+ }
+ } else /* no error */
+ CmdStatus = S_OK;
+ InfoBytes = readl(&dp6m_ptr->i960r.info[0]);
+ if (gdth_polling) /* init. -> more info */
+ InfoBytes2 = readl(&dp6m_ptr->i960r.info[1]);
+ writeb(0xff, &dp6m_ptr->i960r.edoor_reg);
+ writeb(0, &dp6m_ptr->i960r.sema1_reg);
+ } else {
+ TRACE2(("gdth_interrupt() unknown controller type\n"));
+ return;
+ }
+
+ TRACE(("gdth_interrupt() index %d stat %d info %ld\n",
+ IStatus,CmdStatus,InfoBytes));
+ ha->status = CmdStatus;
+ ha->info = InfoBytes;
+ ha->info2 = InfoBytes2;
+
+ if (gdth_from_wait) {
+ wait_hanum = hanum;
+ wait_index = (int)IStatus;
+ }
+
+ if (IStatus == ASYNCINDEX) {
+ TRACE2(("gdth_interrupt() async. event\n"));
+ gdth_async_event(hanum,Service);
+ } else {
+ if (IStatus == SPEZINDEX) {
+ TRACE2(("Service unknown or not initialized !\n"));
+ dvr.size = sizeof(dvr.eu.driver);
+ dvr.eu.driver.ionode = hanum;
+ gdth_store_event(ES_DRIVER, 4, &dvr);
+ return;
+ }
+ scp = gdth_cmd_tab[IStatus-2][hanum].cmnd;
+ Service = gdth_cmd_tab[IStatus-2][hanum].service;
+ gdth_cmd_tab[IStatus-2][hanum].cmnd = UNUSED_CMND;
+ if (scp == UNUSED_CMND) {
+ TRACE2(("gdth_interrupt() index to unused command (%d)\n",IStatus));
+ dvr.size = sizeof(dvr.eu.driver);
+ dvr.eu.driver.ionode = hanum;
+ dvr.eu.driver.index = IStatus;
+ gdth_store_event(ES_DRIVER, 1, &dvr);
+ return;
+ }
+ if (scp == INTERNAL_CMND) {
+ TRACE(("gdth_interrupt() answer to internal command\n"));
+ return;
+ }
+ TRACE(("gdth_interrupt() sync. status\n"));
+ gdth_sync_event(hanum,Service,IStatus,scp);
+ }
+ gdth_next(hanum);
+}
+
+static int gdth_sync_event(int hanum,int service,unchar index,Scsi_Cmnd *scp)
+{
+ register gdth_ha_str *ha;
+ gdth_msg_str *msg;
+ gdth_cmd_str *cmdp;
+ char c='\r';
+ ushort i;
+ gdth_evt_data dvr;
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ cmdp = ha->pccb;
+ TRACE(("gdth_sync_event() scp %lx serv %d status %d\n",
+ (ulong)scp,service,ha->status));
+
+ if (service == SCREENSERVICE) {
+ msg = (gdth_msg_str *)ha->pscratch;
+ TRACE(("len: %ld, answer: %d, ext: %d, alen: %ld\n",
+ msg->msg_len,msg->msg_answer,msg->msg_ext,msg->msg_alen));
+ if (msg->msg_len)
+ if (!(msg->msg_answer && msg->msg_ext)) {
+ msg->msg_text[msg->msg_len] = '\0';
+ printk("%s",msg->msg_text);
+ }
+
+ if (msg->msg_ext && !msg->msg_answer) {
+ while (gdth_test_busy(hanum))
+ udelay(1);
+ cmdp->Service = SCREENSERVICE;
+ cmdp->RequestBuffer = SCREEN_CMND;
+ gdth_get_cmd_index(hanum);
+ gdth_set_sema0(hanum);
+ cmdp->OpCode = GDT_READ;
+ cmdp->BoardNode = LOCALBOARD;
+ cmdp->u.screen.reserved = 0;
+ cmdp->u.screen.msg_handle= msg->msg_handle;
+ cmdp->u.screen.msg_addr = (ulong)msg;
+ ha->cmd_offs_dpmem = 0;
+ ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.screen.msg_addr)
+ + sizeof(ulong);
+ ha->cmd_cnt = 0;
+ gdth_copy_command(hanum);
+ gdth_release_event(hanum);
+ return 1;
+ }
+
+ if (msg->msg_answer && msg->msg_alen) {
+ for (i=0; i<msg->msg_alen && i<MSGLEN; ++i) {
+ /* getchar() ?? */
+ /* .. */
+ if (c == '\r')
+ break;
+ msg->msg_text[i] = c;
+ }
+ msg->msg_alen -= i;
+ if (c!='\r' && msg->msg_alen!=0) {
+ msg->msg_answer = 1;
+ msg->msg_ext = 1;
+ } else {
+ msg->msg_ext = 0;
+ msg->msg_answer = 0;
+ }
+ msg->msg_len = i;
+ while (gdth_test_busy(hanum))
+ udelay(1);
+ cmdp->Service = SCREENSERVICE;
+ cmdp->RequestBuffer = SCREEN_CMND;
+ gdth_get_cmd_index(hanum);
+ gdth_set_sema0(hanum);
+ cmdp->OpCode = GDT_WRITE;
+ cmdp->BoardNode = LOCALBOARD;
+ cmdp->u.screen.reserved = 0;
+ cmdp->u.screen.msg_handle= msg->msg_handle;
+ cmdp->u.screen.msg_addr = (ulong)msg;
+ ha->cmd_offs_dpmem = 0;
+ ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.screen.msg_addr)
+ + sizeof(ulong);
+ ha->cmd_cnt = 0;
+ gdth_copy_command(hanum);
+ gdth_release_event(hanum);
+ return 1;
+ }
+ printk("\n");
+
+ } else {
+ scp->SCp.Message = (int)ha->status;
+ /* cache or raw service */
+ if (ha->status == S_OK) {
+ scp->result = DID_OK << 16;
+ } else if (ha->status == S_BSY) {
+ TRACE2(("Controller busy -> retry !\n"));
+ gdth_putq(hanum,scp,DEFAULT_PRI);
+ return 1;
+ } else {
+ if (service == CACHESERVICE) {
+ memset((char*)scp->sense_buffer,0,16);
+ scp->sense_buffer[0] = 0x70;
+ scp->sense_buffer[2] = NOT_READY;
+ scp->result = (DID_OK << 16) | (CHECK_CONDITION << 1);
+
+ if (scp->done != gdth_scsi_done) {
+ dvr.size = sizeof(dvr.eu.sync);
+ dvr.eu.sync.ionode = hanum;
+ dvr.eu.sync.service = service;
+ dvr.eu.sync.status = ha->status;
+ dvr.eu.sync.info = ha->info;
+ dvr.eu.sync.hostdrive =
+#if LINUX_VERSION_CODE >= 0x020000
+ ha->id[scp->channel][scp->target].hostdrive;
+#else
+ ha->id[NUMDATA(scp->host)->busnum][scp->target].hostdrive;
+#endif
+ if (ha->status >= 0x8000)
+ gdth_store_event(ES_SYNC, 0, &dvr);
+ else
+ gdth_store_event(ES_SYNC, service, &dvr);
+ }
+ } else {
+ if (ha->status!=S_RAW_SCSI || ha->status==S_RAW_ILL) {
+ scp->result = DID_BAD_TARGET << 16;
+ } else {
+ scp->result = (DID_OK << 16) | ha->info;
+ }
+ }
+ }
+ scp->SCp.have_data_in++;
+ scp->scsi_done(scp);
+ }
+
+ return 1;
+}
+
+static char *async_cache_tab[] = {
+/* 0*/ "\011\000\002\002\002\004\002\006\004"
+ "GDT HA %u, service %u, async. status %u/%lu unknown",
+/* 1*/ "\011\000\002\002\002\004\002\006\004"
+ "GDT HA %u, service %u, async. status %u/%lu unknown",
+/* 2*/ "\005\000\002\006\004"
+ "GDT HA %u, Host Drive %lu not ready",
+/* 3*/ "\005\000\002\006\004"
+ "GDT HA %u, Host Drive %lu: REASSIGN not successful and/or data error on reassigned blocks. Drive may crash in the future and should be replaced",
+/* 4*/ "\005\000\002\006\004"
+ "GDT HA %u, mirror update on Host Drive %lu failed",
+/* 5*/ "\005\000\002\006\004"
+ "GDT HA %u, Mirror Drive %lu failed",
+/* 6*/ "\005\000\002\006\004"
+ "GDT HA %u, Mirror Drive %lu: REASSIGN not successful and/or data error on reassigned blocks. Drive may crash in the future and should be replaced",
+/* 7*/ "\005\000\002\006\004"
+ "GDT HA %u, Host Drive %lu write protected",
+/* 8*/ "\005\000\002\006\004"
+ "GDT HA %u, media changed in Host Drive %lu",
+/* 9*/ "\005\000\002\006\004"
+ "GDT HA %u, Host Drive %lu is offline",
+/*10*/ "\005\000\002\006\004"
+ "GDT HA %u, media change of Mirror Drive %lu",
+/*11*/ "\005\000\002\006\004"
+ "GDT HA %u, Mirror Drive %lu is write protected",
+/*12*/ "\005\000\002\006\004"
+ "GDT HA %u, general error on Host Drive %lu. Please check the devices of this drive!",
+/*13*/ "\007\000\002\006\002\010\002"
+ "GDT HA %u, Array Drive %u: Cache Drive %u failed",
+/*14*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: FAIL state entered",
+/*15*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: error",
+/*16*/ "\007\000\002\006\002\010\002"
+ "GDT HA %u, Array Drive %u: failed drive replaced by Cache Drive %u",
+/*17*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: parity build failed",
+/*18*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: drive rebuild failed",
+/*19*/ "\007\000\002\010\002"
+ "GDT HA %u, Test of Hot Fix %u failed",
+/*20*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: drive build finished successfully",
+/*21*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: drive rebuild finished successfully",
+/*22*/ "\007\000\002\006\002\010\002"
+ "GDT HA %u, Array Drive %u: Hot Fix %u activated",
+/*23*/ "\005\000\002\006\002"
+ "GDT HA %u, Host Drive %u: processing of i/o aborted due to serious drive error",
+/*24*/ "\005\000\002\010\002"
+ "GDT HA %u, mirror update on Cache Drive %u completed",
+/*25*/ "\005\000\002\010\002"
+ "GDT HA %u, mirror update on Cache Drive %lu failed",
+/*26*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: drive rebuild started",
+/*27*/ "\005\000\002\012\001"
+ "GDT HA %u, Fault bus %u: SHELF OK detected",
+/*28*/ "\005\000\002\012\001"
+ "GDT HA %u, Fault bus %u: SHELF not OK detected",
+/*29*/ "\007\000\002\012\001\013\001"
+ "GDT HA %u, Fault bus %u, ID %u: Auto Hot Plug started",
+/*30*/ "\007\000\002\012\001\013\001"
+ "GDT HA %u, Fault bus %u, ID %u: new disk detected",
+/*31*/ "\007\000\002\012\001\013\001"
+ "GDT HA %u, Fault bus %u, ID %u: old disk detected",
+/*32*/ "\007\000\002\012\001\013\001"
+ "GDT HA %u, Fault bus %u, ID %u: plugging an active disk is illegal",
+/*33*/ "\007\000\002\012\001\013\001"
+ "GDT HA %u, Fault bus %u, ID %u: illegal device detected",
+/*34*/ "\011\000\002\012\001\013\001\006\004"
+ "GDT HA %u, Fault bus %u, ID %u: insufficient disk capacity (%lu MB required)",
+/*35*/ "\007\000\002\012\001\013\001"
+ "GDT HA %u, Fault bus %u, ID %u: disk write protected",
+/*36*/ "\007\000\002\012\001\013\001"
+ "GDT HA %u, Fault bus %u, ID %u: disk not available",
+/*37*/ "\007\000\002\012\001\006\004"
+ "GDT HA %u, Fault bus %u: swap detected (%lu)",
+/*38*/ "\007\000\002\012\001\013\001"
+ "GDT HA %u, Fault bus %u, ID %u: Auto Hot Plug finished successfully",
+/*39*/ "\007\000\002\012\001\013\001"
+ "GDT HA %u, Fault bus %u, ID %u: Auto Hot Plug aborted due to user Hot Plug",
+/*40*/ "\007\000\002\012\001\013\001"
+ "GDT HA %u, Fault bus %u, ID %u: Auto Hot Plug aborted",
+/*41*/ "\007\000\002\012\001\013\001"
+ "GDT HA %u, Fault bus %u, ID %u: Auto Hot Plug for Hot Fix started",
+/*42*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: drive build started",
+/*43*/ "\003\000\002"
+ "GDT HA %u, DRAM parity error detected",
+/*44*/ "\005\000\002\006\002"
+ "GDT HA %u, Mirror Drive %u: update started",
+/*45*/ "\007\000\002\006\002\010\002"
+ "GDT HA %u, Mirror Drive %u: Hot Fix %u activated",
+/*46*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: no matching Pool Hot Fix Drive available",
+/*47*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: Pool Hot Fix Drive available",
+/*48*/ "\005\000\002\006\002"
+ "GDT HA %u, Mirror Drive %u: no matching Pool Hot Fix Drive available",
+/*49*/ "\005\000\002\006\002"
+ "GDT HA %u, Mirror Drive %u: Pool Hot Fix Drive available",
+/*50*/ "\007\000\002\012\001\013\001"
+ "GDT HA %u, SCSI bus %u, ID %u: IGNORE_WIDE_RESIDUE message received",
+/*51*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: expand started",
+/*52*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: expand finished successfully",
+/*53*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: expand failed",
+/*54*/ "\003\000\002"
+ "GDT HA %u, CPU temperature critical",
+/*55*/ "\003\000\002"
+ "GDT HA %u, CPU temperature OK",
+/*56*/ "\005\000\002\006\004"
+ "GDT HA %u, Host drive %lu created",
+/*57*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: expand restarted",
+/*58*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: expand stopped",
+};
+
+
+static int gdth_async_event(int hanum,int service)
+{
+ gdth_stackframe stack;
+ gdth_evt_data dvr;
+ char *f = NULL;
+ int i,j;
+ gdth_ha_str *ha;
+ gdth_msg_str *msg;
+ gdth_cmd_str *cmdp;
+ int cmd_index;
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ cmdp= ha->pccb;
+ msg = (gdth_msg_str *)ha->pscratch;
+ TRACE2(("gdth_async_event() ha %d serv %d\n",
+ hanum,service));
+
+ if (service == SCREENSERVICE) {
+ if (ha->status == MSG_REQUEST) {
+ while (gdth_test_busy(hanum))
+ udelay(1);
+ cmdp->Service = SCREENSERVICE;
+ cmdp->RequestBuffer = SCREEN_CMND;
+ cmd_index = gdth_get_cmd_index(hanum);
+ gdth_set_sema0(hanum);
+ cmdp->OpCode = GDT_READ;
+ cmdp->BoardNode = LOCALBOARD;
+ cmdp->u.screen.reserved = 0;
+ cmdp->u.screen.msg_handle= MSG_INV_HANDLE;
+ cmdp->u.screen.msg_addr = (ulong)msg;
+ ha->cmd_offs_dpmem = 0;
+ ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.screen.msg_addr)
+ + sizeof(ulong);
+ ha->cmd_cnt = 0;
+ gdth_copy_command(hanum);
+ if (ha->type == GDT_EISA)
+ printk("[EISA slot %d] ",(ushort)ha->brd_phys);
+ else if (ha->type == GDT_ISA)
+ printk("[DPMEM 0x%4X] ",(ushort)ha->brd_phys);
+ else
+ printk("[PCI %d/%d] ",(ushort)(ha->brd_phys>>8),
+ (ushort)((ha->brd_phys>>3)&0x1f));
+ gdth_release_event(hanum);
+ }
+
+ } else {
+ dvr.size = sizeof(dvr.eu.async);
+ dvr.eu.async.ionode = hanum;
+ dvr.eu.async.service = service;
+ dvr.eu.async.status = ha->status;
+ dvr.eu.async.info = ha->info;
+ *(ulong *)dvr.eu.async.scsi_coord = ha->info2;
+ gdth_store_event(ES_ASYNC, service, &dvr);
+
+ if (service==CACHESERVICE && INDEX_OK(ha->status,async_cache_tab)) {
+ TRACE2(("GDT: Async. event cache service, event no.: %d\n",
+ ha->status));
+
+ f = async_cache_tab[ha->status];
+
+ /* i: parameter to push, j: stack element to fill */
+ for (j=0,i=1; i < f[0]; i+=2) {
+ switch (f[i+1]) {
+ case 4:
+ stack.b[j++] = *(ulong*)&dvr.eu.stream[(int)f[i]];
+ break;
+ case 2:
+ stack.b[j++] = *(ushort*)&dvr.eu.stream[(int)f[i]];
+ break;
+ case 1:
+ stack.b[j++] = *(unchar*)&dvr.eu.stream[(int)f[i]];
+ break;
+ default:
+ break;
+ }
+ }
+
+ printk(&f[f[0]],stack); printk("\n");
+
+ } else {
+ printk("GDT: Unknown async. event service %d event no. %d\n",
+ service,ha->status);
+ }
+ }
+ return 1;
+}
+
+#ifdef GDTH_STATISTICS
+void gdth_timeout(void)
+{
+ ulong flags,i;
+ Scsi_Cmnd *nscp;
+ gdth_ha_str *ha;
+ int hanum = 0;
+
+ save_flags(flags);
+ cli();
+
+ for (act_stats=0,i=0; i<GDTH_MAXCMDS; ++i)
+ if (gdth_cmd_tab[i][hanum].cmnd != UNUSED_CMND)
+ ++act_stats;
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ for (act_rq=0,nscp=ha->req_first; nscp; nscp=(Scsi_Cmnd*)nscp->SCp.ptr)
+ ++act_rq;
+
+ TRACE2(("gdth_to(): ints %ld, ios %ld, act_stats %ld, act_rq %ld\n",
+ act_ints, act_ios, act_stats, act_rq));
+ act_ints = act_ios = 0;
+
+ timer_table[GDTH_TIMER].expires = jiffies + 30*HZ;
+ timer_active |= 1<<GDTH_TIMER;
+ sti();
+}
+#endif
+
+int gdth_detect(Scsi_Host_Template *shtp)
+{
+ struct Scsi_Host *shp;
+ gdth_ha_str *ha;
+ unsigned long flags;
+ ulong isa_bios;
+ ushort eisa_slot,device_id,index;
+ gdth_pci_str pcistr;
+ int i,j,hanum;
+ unchar b;
+
+
+#ifdef DEBUG_GDTH
+ printk("GDT: This driver contains debugging information !! Trace level = %d\n",
+ DebugState);
+ printk(" Destination of debugging information: ");
+#ifdef __SERIAL__
+#ifdef __COM2__
+ printk("Serial port COM2\n");
+#else
+ printk("Serial port COM1\n");
+#endif
+#else
+ printk("Console\n");
+#endif
+ WAITSEC(3);
+#endif
+
+ TRACE(("gdth_detect()\n"));
+
+ if (disable_gdth_scan) {
+ printk("GDT: Controller driver disabled from command line !\n");
+ return 0;
+ }
+
+ /* initializations */
+ gdth_polling = TRUE; b = 0;
+ for (i=0; i<GDTH_MAXCMDS; ++i)
+ for (j=0; j<MAXHA; ++j)
+ gdth_cmd_tab[i][j].cmnd = UNUSED_CMND;
+ for (i=0; i<4; ++i)
+ for (j=0; j<MAXHA; ++j)
+ gdth_ioctl_tab[i][j] = NULL;
+ gdth_clear_events();
+
+ /* scanning for controllers, at first: ISA controller */
+ for (isa_bios=0xc8000UL; isa_bios<=0xd8000UL; isa_bios+=0x8000UL) {
+ if (gdth_search_isa(isa_bios)) { /* controller found */
+ shp = scsi_register(shtp,sizeof(gdth_ext_str));
+ ha = HADATA(shp);
+ if (!gdth_init_isa(isa_bios,ha)) {
+ scsi_unregister(shp);
+ continue;
+ }
+ /* controller found and initialized */
+ printk("Configuring GDT-ISA HA at BIOS 0x%05lX IRQ %u DRQ %u\n",
+ isa_bios,ha->irq,ha->drq);
+
+ save_flags(flags);
+ cli();
+#if LINUX_VERSION_CODE >= 0x010346
+ if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth",NULL))
+#else
+ if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth"))
+#endif
+ {
+ printk("GDT-ISA: Unable to allocate IRQ\n");
+ restore_flags(flags);
+ scsi_unregister(shp);
+ continue;
+ }
+ if (request_dma(ha->drq,"gdth")) {
+ printk("GDT-ISA: Unable to allocate DMA channel\n");
+#if LINUX_VERSION_CODE >= 0x010346
+ free_irq(ha->irq,NULL);
+#else
+ free_irq(ha->irq);
+#endif
+ restore_flags(flags);
+ scsi_unregister(shp);
+ continue;
+ }
+ set_dma_mode(ha->drq,DMA_MODE_CASCADE);
+ enable_dma(ha->drq);
+ shp->unchecked_isa_dma = 1;
+ shp->irq = ha->irq;
+ shp->dma_channel = ha->drq;
+ for (i=0; i<MAXID; ++i) {
+ if (ha->id[0][i].type==SIOP_DTYP) {
+ shp->this_id = i;
+ break;
+ }
+ }
+ hanum = gdth_ctr_count;
+ gdth_ctr_tab[gdth_ctr_count++] = shp;
+ gdth_ctr_vtab[gdth_ctr_vcount++] = shp;
+
+ NUMDATA(shp)->hanum = (ushort)hanum;
+ NUMDATA(shp)->busnum= 0;
+
+ ha->pccb = CMDDATA(shp);
+ ha->pscratch = DMADATA(shp);
+ ha->req_first = NULL;
+ for (i=0; i<MAXBUS; ++i) {
+ for (j=0; j<MAXID; ++j) {
+ ha->id[i][j].type = EMPTY_DTYP;
+ ha->id[i][j].lock = 0;
+ }
+ }
+ restore_flags(flags);
+
+ if (!gdth_search_drives(hanum)) {
+ printk("GDT-ISA: Error during device scan\n");
+ --gdth_ctr_count;
+ --gdth_ctr_vcount;
+ save_flags(flags);
+ cli();
+#if LINUX_VERSION_CODE >= 0x010346
+ free_irq(ha->irq,NULL);
+#else
+ free_irq(ha->irq);
+#endif
+ restore_flags(flags);
+ scsi_unregister(shp);
+ continue;
+ }
+
+#if LINUX_VERSION_CODE >= 0x020000
+ shp->max_id = 8;
+ shp->max_lun = 8;
+ shp->max_channel = ha->bus_cnt - 1;
+#else
+ /* register addit. SCSI channels as virtual controllers */
+ for (b=1; b<ha->bus_cnt; ++b) {
+ shp = scsi_register(shtp,sizeof(gdth_num_str));
+ shp->unchecked_isa_dma = 1;
+ shp->irq = ha->irq;
+ shp->dma_channel = ha->drq;
+ for (i=0; i<MAXID; ++i) {
+ if (ha->id[b][i].type==SIOP_DTYP) {
+ shp->this_id = i;
+ break;
+ }
+ }
+ gdth_ctr_vtab[gdth_ctr_vcount++] = shp;
+ NUMDATA(shp)->hanum = (ushort)hanum;
+ NUMDATA(shp)->busnum = b;
+ }
+#endif
+
+ gdth_enable_int(hanum);
+ }
+ }
+
+ /* scanning for EISA controllers */
+ for (eisa_slot=0x1000; eisa_slot<=0x8000; eisa_slot+=0x1000) {
+ if (gdth_search_eisa(eisa_slot)) { /* controller found */
+ shp = scsi_register(shtp,sizeof(gdth_ext_str));
+ ha = HADATA(shp);
+ if (!gdth_init_eisa(eisa_slot,ha)) {
+ scsi_unregister(shp);
+ continue;
+ }
+ /* controller found and initialized */
+ printk("Configuring GDT-EISA HA at Slot %d IRQ %u\n",
+ eisa_slot>>12,ha->irq);
+
+ save_flags(flags);
+ cli();
+#if LINUX_VERSION_CODE >= 0x010346
+ if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth",NULL))
+#else
+ if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth"))
+#endif
+ {
+ printk("GDT-EISA: Unable to allocate IRQ\n");
+ restore_flags(flags);
+ scsi_unregister(shp);
+ continue;
+ }
+ shp->unchecked_isa_dma = 0;
+ shp->irq = ha->irq;
+ shp->dma_channel = 0xff;
+ for (i=0; i<MAXID; ++i) {
+ if (ha->id[0][i].type==SIOP_DTYP) {
+ shp->this_id = i;
+ break;
+ }
+ }
+ hanum = gdth_ctr_count;
+ gdth_ctr_tab[gdth_ctr_count++] = shp;
+ gdth_ctr_vtab[gdth_ctr_vcount++] = shp;
+
+ NUMDATA(shp)->hanum = (ushort)hanum;
+ NUMDATA(shp)->busnum= 0;
+ TRACE2(("EISA detect Bus 0: shp %lx hanum %d\n",
+ (ulong)shp,NUMDATA(shp)->hanum));
+
+ ha->pccb = CMDDATA(shp);
+ ha->pscratch = DMADATA(shp);
+ ha->req_first = NULL;
+ for (i=0; i<MAXBUS; ++i) {
+ for (j=0; j<MAXID; ++j) {
+ ha->id[i][j].type = EMPTY_DTYP;
+ ha->id[i][j].lock = 0;
+ }
+ }
+ restore_flags(flags);
+
+ if (!gdth_search_drives(hanum)) {
+ printk("GDT-EISA: Error during device scan\n");
+ --gdth_ctr_count;
+ --gdth_ctr_vcount;
+ save_flags(flags);
+ cli();
+#if LINUX_VERSION_CODE >= 0x010346
+ free_irq(ha->irq,NULL);
+#else
+ free_irq(ha->irq);
+#endif
+ restore_flags(flags);
+ scsi_unregister(shp);
+ continue;
+ }
+
+#if LINUX_VERSION_CODE >= 0x020000
+ shp->max_id = 8;
+ shp->max_lun = 8;
+ shp->max_channel = ha->bus_cnt - 1;
+#else
+ /* register addit. SCSI channels as virtual controllers */
+ for (b=1; b<ha->bus_cnt; ++b) {
+ shp = scsi_register(shtp,sizeof(gdth_num_str));
+ shp->unchecked_isa_dma = 0;
+ shp->irq = ha->irq;
+ shp->dma_channel = 0xff;
+ for (i=0; i<MAXID; ++i) {
+ if (ha->id[b][i].type==SIOP_DTYP) {
+ shp->this_id = i;
+ break;
+ }
+ }
+ gdth_ctr_vtab[gdth_ctr_vcount++] = shp;
+ NUMDATA(shp)->hanum = (ushort)hanum;
+ NUMDATA(shp)->busnum = b;
+ TRACE2(("EISA detect Bus %d: shp %lx hanum %d\n",
+ NUMDATA(shp)->busnum,(ulong)shp,
+ NUMDATA(shp)->hanum));
+ }
+#endif
+
+ gdth_enable_int(hanum);
+ }
+ }
+
+ /* scanning for PCI controllers */
+ for (device_id = 0; device_id <= PCI_DEVICE_ID_VORTEX_GDT6x21RP2; ++device_id) {
+ if (device_id > PCI_DEVICE_ID_VORTEX_GDT6555 &&
+ device_id < PCI_DEVICE_ID_VORTEX_GDT6x17RP)
+ continue;
+ for (index = 0; ; ++index) {
+ if (!gdth_search_pci(device_id,index,&pcistr))
+ break; /* next device_id */
+ shp = scsi_register(shtp,sizeof(gdth_ext_str));
+ ha = HADATA(shp);
+ if (!gdth_init_pci(&pcistr,ha)) {
+ scsi_unregister(shp);
+ continue;
+ }
+ /* controller found and initialized */
+ printk("Configuring GDT-PCI HA at %d/%d IRQ %u\n",
+ pcistr.bus,pcistr.device_fn>>3,ha->irq);
+
+ save_flags(flags);
+ cli();
+#if LINUX_VERSION_CODE >= 0x010346
+ if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth",NULL))
+#else
+ if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth"))
+#endif
+ {
+ printk("GDT-PCI: Unable to allocate IRQ\n");
+ restore_flags(flags);
+ scsi_unregister(shp);
+ continue;
+ }
+ shp->unchecked_isa_dma = 0;
+ shp->irq = ha->irq;
+ shp->dma_channel = 0xff;
+ for (i=0; i<MAXID; ++i) {
+ if (ha->id[0][i].type==SIOP_DTYP) {
+ shp->this_id = i;
+ break;
+ }
+ }
+ hanum = gdth_ctr_count;
+ gdth_ctr_tab[gdth_ctr_count++] = shp;
+ gdth_ctr_vtab[gdth_ctr_vcount++] = shp;
+
+ NUMDATA(shp)->hanum = (ushort)hanum;
+ NUMDATA(shp)->busnum= 0;
+
+ ha->pccb = CMDDATA(shp);
+ ha->pscratch = DMADATA(shp);
+ ha->req_first = NULL;
+ for (i=0; i<MAXBUS; ++i) {
+ for (j=0; j<MAXID; ++j) {
+ ha->id[i][j].type = EMPTY_DTYP;
+ ha->id[i][j].lock = 0;
+ }
+ }
+ restore_flags(flags);
+
+ if (!gdth_search_drives(hanum)) {
+ printk("GDT-PCI: Error during device scan\n");
+ --gdth_ctr_count;
+ --gdth_ctr_vcount;
+ save_flags(flags);
+ cli();
+#if LINUX_VERSION_CODE >= 0x010346
+ free_irq(ha->irq,NULL);
+#else
+ free_irq(ha->irq);
+#endif
+ restore_flags(flags);
+ scsi_unregister(shp);
+ continue;
+ }
+
+#if LINUX_VERSION_CODE >= 0x020000
+ shp->max_id = 8;
+ shp->max_lun = 8;
+ shp->max_channel = ha->bus_cnt - 1;
+#else
+ /* register addit. SCSI channels as virtual controllers */
+ for (b=1; b<ha->bus_cnt; ++b) {
+ shp = scsi_register(shtp,sizeof(gdth_num_str));
+ shp->unchecked_isa_dma = 0;
+ shp->irq = ha->irq;
+ shp->dma_channel = 0xff;
+ for (i=0; i<MAXID; ++i) {
+ if (ha->id[b][i].type==SIOP_DTYP) {
+ shp->this_id = i;
+ break;
+ }
+ }
+ gdth_ctr_vtab[gdth_ctr_vcount++] = shp;
+ NUMDATA(shp)->hanum = (ushort)hanum;
+ NUMDATA(shp)->busnum = b;
+ }
+#endif
+
+ gdth_enable_int(hanum);
+ }
+ }
+
+ TRACE2(("gdth_detect() %d controller detected\n",gdth_ctr_count));
+ if (gdth_ctr_count > 0) {
+#ifdef GDTH_STATISTICS
+ TRACE2(("gdth_detect(): Initializing timer !\n"));
+ timer_table[GDTH_TIMER].fn = gdth_timeout;
+ timer_table[GDTH_TIMER].expires = jiffies + HZ;
+ timer_active |= 1<<GDTH_TIMER;
+#endif
+#if LINUX_VERSION_CODE >= 0x020100
+ register_reboot_notifier(&gdth_notifier);
+#endif
+ }
+ gdth_polling = FALSE;
+ return gdth_ctr_vcount;
+}
+
+
+int gdth_release(struct Scsi_Host *shp)
+{
+ unsigned long flags;
+
+ TRACE2(("gdth_release()\n"));
+
+ save_flags(flags);
+ cli();
+ if (NUMDATA(shp)->busnum == 0) {
+ if (shp->irq) {
+#if LINUX_VERSION_CODE >= 0x010346
+ free_irq(shp->irq,NULL);
+#else
+ free_irq(shp->irq);
+#endif
+ }
+ if (shp->dma_channel != 0xff) {
+ free_dma(shp->dma_channel);
+ }
+ }
+
+ restore_flags(flags);
+ scsi_unregister(shp);
+ return 0;
+}
+
+
+static const char *gdth_ctr_name(int hanum)
+{
+ gdth_ha_str *ha;
+
+ TRACE2(("gdth_ctr_name()\n"));
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+
+ if (ha->type == GDT_EISA) {
+ switch (ha->stype) {
+ case GDT3_ID:
+ return("GDT3000/3020 (EISA)");
+ case GDT3A_ID:
+ return("GDT3000A/3020A/3050A (EISA)");
+ case GDT3B_ID:
+ return("GDT3000B/3010A (EISA)");
+ }
+ } else if (ha->type == GDT_ISA) {
+ return("GDT2000/2020 (ISA)");
+ } else if (ha->type == GDT_PCI) {
+ switch (ha->stype) {
+ case PCI_DEVICE_ID_VORTEX_GDT60x0:
+ return("GDT6000/6020/6050 (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6000B:
+ return("GDT6000B/6010 (PCI)");
+ }
+ } else if (ha->type == GDT_PCINEW) {
+ switch (ha->stype) {
+ case PCI_DEVICE_ID_VORTEX_GDT6x10:
+ return("GDT6110/6510 (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6x20:
+ return("GDT6120/6520 (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6530:
+ return("GDT6530 (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6550:
+ return("GDT6550 (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6x17:
+ return("GDT6117/6517 (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6x27:
+ return("GDT6127/6527 (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6537:
+ return("GDT6537 (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6557:
+ return("GDT6557/6557-ECC (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6x15:
+ return("GDT6115/6515 (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6x25:
+ return("GDT6125/6525 (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6535:
+ return("GDT6535 (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6555:
+ return("GDT6555/6555-ECC (PCI)");
+ }
+ } else if (ha->type == GDT_PCIMPR) {
+ switch (ha->stype) {
+ case PCI_DEVICE_ID_VORTEX_GDT6x17RP:
+ return("GDT6117RP/GDT6517RP (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6x27RP:
+ return("GDT6127RP/GDT6527RP (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6537RP:
+ return("GDT6537RP (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6557RP:
+ return("GDT6557RP (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6x11RP:
+ return("GDT6111RP/GDT6511RP (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6x21RP:
+ return("GDT6121RP/GDT6521RP (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6x17RP1:
+ return("GDT6117RP1/GDT6517RP1 (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6x27RP1:
+ return("GDT6127RP1/GDT6527RP1 (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6537RP1:
+ return("GDT6537RP1 (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6557RP1:
+ return("GDT6557RP1 (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6x11RP1:
+ return("GDT6111RP1/GDT6511RP1 (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6x21RP1:
+ return("GDT6121RP1/GDT6521RP1 (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6x17RP2:
+ return("GDT6117RP2/GDT6517RP2 (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6x27RP2:
+ return("GDT6127RP2/GDT6527RP2 (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6537RP2:
+ return("GDT6537RP2 (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6557RP2:
+ return("GDT6557RP2 (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6x11RP2:
+ return("GDT6111RP2/GDT6511RP2 (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6x21RP2:
+ return("GDT6121RP2/GDT6521RP2 (PCI)");
+ }
+ }
+ return("");
+}
+
+const char *gdth_info(struct Scsi_Host *shp)
+{
+ int hanum;
+
+ TRACE2(("gdth_info()\n"));
+ hanum = NUMDATA(shp)->hanum;
+
+ return (gdth_ctr_name(hanum));
+}
+
+
+int gdth_abort(Scsi_Cmnd *scp)
+{
+ TRACE2(("gdth_abort() reason %d\n",scp->abort_reason));
+ return SCSI_ABORT_SNOOZE;
+}
+
+#if LINUX_VERSION_CODE >= 0x010346
+int gdth_reset(Scsi_Cmnd *scp, unsigned int reset_flags)
+#else
+int gdth_reset(Scsi_Cmnd *scp)
+#endif
+{
+ TRACE2(("gdth_reset()\n"));
+ return SCSI_RESET_PUNT;
+}
+
+
+#if LINUX_VERSION_CODE >= 0x010300
+int gdth_bios_param(Disk *disk,kdev_t dev,int *ip)
+#else
+int gdth_bios_param(Disk *disk,int dev,int *ip)
+#endif
+{
+ unchar b, t;
+ int hanum;
+ gdth_ha_str *ha;
+
+ hanum = NUMDATA(disk->device->host)->hanum;
+ b = disk->device->channel;
+ t = disk->device->id;
+ TRACE2(("gdth_bios_param() ha %d bus %d target %d\n", hanum, b, t));
+ ha = HADATA(gdth_ctr_tab[hanum]);
+
+ ip[0] = ha->id[b][t].heads;
+ ip[1] = ha->id[b][t].secs;
+ ip[2] = disk->capacity / ip[0] / ip[1];
+
+ TRACE2(("gdth_bios_param(): %d heads, %d secs, %d cyls\n",
+ ip[0],ip[1],ip[2]));
+ return 0;
+}
+
+
+static void internal_done(Scsi_Cmnd *scp)
+{
+ scp->SCp.sent_command++;
+}
+
+int gdth_command(Scsi_Cmnd *scp)
+{
+ TRACE2(("gdth_command()\n"));
+
+ scp->SCp.sent_command = 0;
+ gdth_queuecommand(scp,internal_done);
+
+ while (!scp->SCp.sent_command)
+ barrier();
+ return scp->result;
+}
+
+
+int gdth_queuecommand(Scsi_Cmnd *scp,void (*done)(Scsi_Cmnd *))
+{
+ int hanum;
+ int priority;
+
+ TRACE(("gdth_queuecommand() cmd 0x%x id %d lun %d\n",
+ scp->cmnd[0],scp->target,scp->lun));
+
+ scp->scsi_done = (void *)done;
+ scp->SCp.have_data_in = 0;
+ hanum = NUMDATA(scp->host)->hanum;
+#ifdef GDTH_STATISTICS
+ ++act_ios;
+#endif
+
+ priority = DEFAULT_PRI;
+#if LINUX_VERSION_CODE >= 0x010300
+ if (scp->done == gdth_scsi_done)
+ priority = scp->SCp.this_residual;
+#endif
+ gdth_putq( hanum, scp, priority );
+ gdth_next( hanum );
+ return 0;
+}
+
+
+/* shutdown routine */
+#if LINUX_VERSION_CODE >= 0x020100
+static int gdth_halt(struct notifier_block *nb, ulong event, void *buf)
+#else
+void gdth_halt(void)
+#endif
+{
+ int hanum, i, j;
+ gdth_ha_str *ha;
+ Scsi_Cmnd scp;
+ Scsi_Device sdev;
+ gdth_cmd_str gdtcmd;
+ char cmnd[12];
+
+#if LINUX_VERSION_CODE >= 0x020100
+ TRACE2(("gdth_halt() event %d\n",event));
+ if (event != SYS_RESTART && event != SYS_HALT && event != SYS_POWER_OFF)
+ return NOTIFY_DONE;
+#else
+ TRACE2(("gdth_halt()\n"));
+#endif
+ printk("GDT: Flushing all host drives .. ");
+ for (hanum = 0; hanum < gdth_ctr_count; ++hanum) {
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ memset(&sdev,0,sizeof(Scsi_Device));
+ memset(&scp, 0,sizeof(Scsi_Cmnd));
+ sdev.host = gdth_ctr_tab[hanum];
+ sdev.id = sdev.host->this_id;
+ scp.cmd_len = 12;
+ scp.host = gdth_ctr_tab[hanum];
+ scp.target = sdev.host->this_id;
+ scp.device = &sdev;
+ scp.use_sg = 0;
+
+ /* flush */
+ for (i = 0; i < MAXBUS; ++i) {
+ for (j = 0; j < MAXID; ++j) {
+ if (ha->id[i][j].type == CACHE_DTYP) {
+ gdtcmd.BoardNode = LOCALBOARD;
+ gdtcmd.Service = CACHESERVICE;
+ gdtcmd.OpCode = GDT_FLUSH;
+ gdtcmd.u.cache.DeviceNo = ha->id[i][j].hostdrive;
+ gdtcmd.u.cache.BlockNo = 1;
+ gdtcmd.u.cache.sg_canz = 0;
+ TRACE2(("gdth_halt(): flush ha %d drive %d\n",
+ hanum, ha->id[i][j].hostdrive));
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ scp.request.rq_status = RQ_SCSI_BUSY;
+ scp.request.sem = &sem;
+ scsi_do_cmd(&scp, cmnd, &gdtcmd,
+ sizeof(gdth_cmd_str), gdth_scsi_done,
+ 30*HZ, 1);
+ down(&sem);
+ }
+ }
+ }
+ }
+
+ /* controller reset */
+ gdtcmd.BoardNode = LOCALBOARD;
+ gdtcmd.Service = CACHESERVICE;
+ gdtcmd.OpCode = GDT_RESET;
+ TRACE2(("gdth_halt(): reset controller %d\n", hanum));
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ scp.request.rq_status = RQ_SCSI_BUSY;
+ scp.request.sem = &sem;
+ scsi_do_cmd(&scp, cmnd, &gdtcmd,
+ sizeof(gdth_cmd_str), gdth_scsi_done,
+ 10*HZ, 1);
+ down(&sem);
+ }
+ }
+ printk("Done.\n");
+
+#ifdef GDTH_STATISTICS
+ timer_active &= ~(1<<GDTH_TIMER);
+#endif
+#if LINUX_VERSION_CODE >= 0x020100
+ unregister_reboot_notifier(&gdth_notifier);
+ return NOTIFY_OK;
+#endif
+}
+
+
+/* called from init/main.c */
+void gdth_setup(char *str,int *ints)
+{
+ static size_t setup_idx = 0;
+
+ TRACE2(("gdth_setup() str %s ints[0] %d ints[1] %d\n",
+ str ? str:"NULL", ints[0],
+ ints[0] ? ints[1]:0));
+
+ if (setup_idx >= MAXHA) {
+ printk("GDT: gdth_setup() called too many times. Bad LILO params ?\n");
+ return;
+ }
+ if (ints[0] != 1) {
+ printk("GDT: Illegal command line !\n");
+ printk("Usage: gdth=<IRQ>\n");
+ printk("Where: <IRQ>: valid EISA controller IRQ (10,11,12,14)\n");
+ printk(" or 0 to disable controller driver\n");
+ return;
+ }
+ if (ints[1] == 10 || ints[1] == 11 || ints[1] == 12 || ints[1] == 14) {
+ irqs[setup_idx++] = ints[1];
+ irqs[setup_idx] = 0xff;
+ return;
+ }
+ if (ints[1] == 0) {
+ disable_gdth_scan = TRUE;
+ return;
+ }
+ printk("GDT: Invalid IRQ (%d) specified\n",ints[1]);
+}
+
+
+#ifdef MODULE
+Scsi_Host_Template driver_template = GDTH;
+#include "scsi_module.c"
+#endif
+
diff --git a/drivers/scsi/gdth.h b/drivers/scsi/gdth.h
new file mode 100644
index 000000000..4e2c47742
--- /dev/null
+++ b/drivers/scsi/gdth.h
@@ -0,0 +1,720 @@
+#ifndef _GDTH_H
+#define _GDTH_H
+
+/*
+ * Header file for the GDT ISA/EISA/PCI Disk Array Controller driver for Linux
+ *
+ * gdth.h Copyright (C) 1995-97 ICP vortex Computersysteme GmbH, Achim Leubner
+ * See gdth.c for further informations and
+ * below for supported controller types
+ *
+ * <achim@vortex.de>
+ *
+ * $Id: gdth.h,v 1.9 1997/11/04 09:55:42 achim Exp $
+ */
+
+#include <linux/version.h>
+#include <linux/types.h>
+
+#ifndef NULL
+#define NULL 0
+#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+/* defines, macros */
+
+/* driver version */
+#define GDTH_VERSION_STR "1.02"
+#define GDTH_VERSION 1
+#define GDTH_SUBVERSION 2
+
+/* protocol version */
+#define PROTOCOL_VERSION 1
+
+/* controller classes */
+#define GDT_ISA 0x01 /* ISA controller */
+#define GDT_EISA 0x02 /* EISA controller */
+#define GDT_PCI 0x03 /* PCI controller */
+#define GDT_PCINEW 0x04 /* new PCI controller */
+#define GDT_PCIMPR 0x05 /* PCI MPR controller */
+/* GDT_EISA, controller subtypes EISA */
+#define GDT3_ID 0x0130941c /* GDT3000/3020 */
+#define GDT3A_ID 0x0230941c /* GDT3000A/3020A/3050A */
+#define GDT3B_ID 0x0330941c /* GDT3000B/3010A */
+/* GDT_ISA */
+#define GDT2_ID 0x0120941c /* GDT2000/2020 */
+/* vendor ID, device IDs (PCI) */
+/* these defines should already exist in <linux/pci.h> */
+#ifndef PCI_VENDOR_ID_VORTEX
+#define PCI_VENDOR_ID_VORTEX 0x1119 /* PCI controller vendor ID */
+#endif
+#ifndef PCI_DEVICE_ID_VORTEX_GDT60x0
+/* GDT_PCI */
+#define PCI_DEVICE_ID_VORTEX_GDT60x0 0 /* GDT6000/6020/6050 */
+#define PCI_DEVICE_ID_VORTEX_GDT6000B 1 /* GDT6000B/6010 */
+/* GDT_PCINEW */
+#define PCI_DEVICE_ID_VORTEX_GDT6x10 2 /* GDT6110/6510 */
+#define PCI_DEVICE_ID_VORTEX_GDT6x20 3 /* GDT6120/6520 */
+#define PCI_DEVICE_ID_VORTEX_GDT6530 4 /* GDT6530 */
+#define PCI_DEVICE_ID_VORTEX_GDT6550 5 /* GDT6550 */
+/* GDT_PCINEW, wide/ultra SCSI controllers */
+#define PCI_DEVICE_ID_VORTEX_GDT6x17 6 /* GDT6117/6517 */
+#define PCI_DEVICE_ID_VORTEX_GDT6x27 7 /* GDT6127/6527 */
+#define PCI_DEVICE_ID_VORTEX_GDT6537 8 /* GDT6537 */
+#define PCI_DEVICE_ID_VORTEX_GDT6557 9 /* GDT6557/6557-ECC */
+/* GDT_PCINEW, wide SCSI controllers */
+#define PCI_DEVICE_ID_VORTEX_GDT6x15 10 /* GDT6115/6515 */
+#define PCI_DEVICE_ID_VORTEX_GDT6x25 11 /* GDT6125/6525 */
+#define PCI_DEVICE_ID_VORTEX_GDT6535 12 /* GDT6535 */
+#define PCI_DEVICE_ID_VORTEX_GDT6555 13 /* GDT6555/6555-ECC */
+#endif
+
+#ifndef PCI_DEVICE_ID_VORTEX_GDT6x17RP
+/* GDT_MPR, RP series, wide/ultra SCSI */
+#define PCI_DEVICE_ID_VORTEX_GDT6x17RP 0x100 /* GDT6117RP/GDT6517RP */
+#define PCI_DEVICE_ID_VORTEX_GDT6x27RP 0x101 /* GDT6127RP/GDT6527RP */
+#define PCI_DEVICE_ID_VORTEX_GDT6537RP 0x102 /* GDT6537RP */
+#define PCI_DEVICE_ID_VORTEX_GDT6557RP 0x103 /* GDT6557RP */
+/* GDT_MPR, RP series, narrow/ultra SCSI */
+#define PCI_DEVICE_ID_VORTEX_GDT6x11RP 0x104 /* GDT6111RP/GDT6511RP */
+#define PCI_DEVICE_ID_VORTEX_GDT6x21RP 0x105 /* GDT6121RP/GDT6521RP */
+/* GDT_MPR, RP1 series, wide/ultra SCSI */
+#define PCI_DEVICE_ID_VORTEX_GDT6x17RP1 0x110 /* GDT6117RP1/GDT6517RP1 */
+#define PCI_DEVICE_ID_VORTEX_GDT6x27RP1 0x111 /* GDT6127RP1/GDT6527RP1 */
+#define PCI_DEVICE_ID_VORTEX_GDT6537RP1 0x112 /* GDT6537RP1 */
+#define PCI_DEVICE_ID_VORTEX_GDT6557RP1 0x113 /* GDT6557RP1 */
+/* GDT_MPR, RP1 series, narrow/ultra SCSI */
+#define PCI_DEVICE_ID_VORTEX_GDT6x11RP1 0x114 /* GDT6111RP1/GDT6511RP1 */
+#define PCI_DEVICE_ID_VORTEX_GDT6x21RP1 0x115 /* GDT6121RP1/GDT6521RP1 */
+/* GDT_MPR, RP2 series, wide/ultra SCSI */
+#define PCI_DEVICE_ID_VORTEX_GDT6x17RP2 0x120 /* GDT6117RP2/GDT6517RP2 */
+#define PCI_DEVICE_ID_VORTEX_GDT6x27RP2 0x121 /* GDT6127RP2/GDT6527RP2 */
+#define PCI_DEVICE_ID_VORTEX_GDT6537RP2 0x122 /* GDT6537RP2 */
+#define PCI_DEVICE_ID_VORTEX_GDT6557RP2 0x123 /* GDT6557RP2 */
+/* GDT_MPR, RP2 series, narrow/ultra SCSI */
+#define PCI_DEVICE_ID_VORTEX_GDT6x11RP2 0x124 /* GDT6111RP2/GDT6511RP2 */
+#define PCI_DEVICE_ID_VORTEX_GDT6x21RP2 0x125 /* GDT6121RP2/GDT6521RP2 */
+#endif
+
+/* limits */
+#define GDTH_SCRATCH 4096 /* 4KB scratch buffer */
+#define GDTH_MAXCMDS 124
+#define GDTH_MAXC_P_L 16 /* max. cmds per lun */
+#define MAXOFFSETS 128
+#define MAXHA 8
+#define MAXID 8
+#define MAXLUN 8
+#define MAXBUS 5
+#define MAX_HDRIVES 35 /* max. host drive count */
+#define MAX_EVENTS 100 /* event buffer count */
+#define MAXCYLS 1024
+#define HEADS 64
+#define SECS 32 /* mapping 64*32 */
+#define MEDHEADS 127
+#define MEDSECS 63 /* mapping 127*63 */
+#define BIGHEADS 255
+#define BIGSECS 63 /* mapping 255*63 */
+
+/* special command ptr. */
+#define UNUSED_CMND ((Scsi_Cmnd *)-1)
+#define INTERNAL_CMND ((Scsi_Cmnd *)-2)
+#define SCREEN_CMND ((Scsi_Cmnd *)-3)
+#define SPECIAL_SCP(p) (p==UNUSED_CMND || p==INTERNAL_CMND || p==SCREEN_CMND)
+
+/* device types */
+#define EMPTY_DTYP 0
+#define CACHE_DTYP 1
+#define RAW_DTYP 2
+#define SIOP_DTYP 3 /* the SCSI processor */
+
+/* controller services */
+#define SCSIRAWSERVICE 3
+#define CACHESERVICE 9
+#define SCREENSERVICE 11
+
+/* screenservice defines */
+#define MSG_INV_HANDLE -1 /* special message handle */
+#define MSGLEN 16 /* size of message text */
+#define MSG_SIZE 34 /* size of message structure */
+#define MSG_REQUEST 0 /* async. event: message */
+
+/* cacheservice defines */
+#define SECTOR_SIZE 0x200 /* always 512 bytes per sector */
+
+/* DPMEM constants */
+#define IC_HEADER_BYTES 48
+#define IC_QUEUE_BYTES 4
+#define DPMEM_COMMAND_OFFSET IC_HEADER_BYTES+IC_QUEUE_BYTES*MAXOFFSETS
+
+/* service commands */
+#define GDT_INIT 0 /* service initialization */
+#define GDT_READ 1 /* read command */
+#define GDT_WRITE 2 /* write command */
+#define GDT_INFO 3 /* information about devices */
+#define GDT_FLUSH 4 /* flush dirty cache buffers */
+#define GDT_IOCTL 5 /* ioctl command */
+#define GDT_DEVTYPE 9 /* additional information */
+#define GDT_MOUNT 10 /* mount cache device */
+#define GDT_UNMOUNT 11 /* unmount cache device */
+#define GDT_SET_FEAT 12 /* set feat. (scatter/gather) */
+#define GDT_GET_FEAT 13 /* get features */
+#define GDT_RESERVE 14 /* reserve dev. to raw service */
+#define GDT_WRITE_THR 16 /* write through */
+#define GDT_EXT_INFO 18 /* extended info */
+#define GDT_RESET 19 /* controller reset */
+
+/* IOCTL command defines */
+#define SCSI_CHAN_CNT 5 /* subfunctions */
+#define L_CTRL_PATTERN 0x20000000L
+#define CACHE_INFO 4
+#define CACHE_CONFIG 5
+#define IO_CHANNEL 0x00020000L /* channels */
+#define INVALID_CHANNEL 0x0000ffffL
+
+/* IOCTLs */
+#define GDTIOCTL_MASK ('J'<<8)
+#define GDTIOCTL_GENERAL (GDTIOCTL_MASK | 0) /* general IOCTL */
+#define GDTIOCTL_DRVERS (GDTIOCTL_MASK | 1) /* get driver version */
+#define GDTIOCTL_CTRTYPE (GDTIOCTL_MASK | 2) /* get controller type */
+#define GDTIOCTL_CTRCNT (GDTIOCTL_MASK | 5) /* get controller count */
+#define GDTIOCTL_LOCKDRV (GDTIOCTL_MASK | 6) /* lock host drive */
+#define GDTIOCTL_LOCKCHN (GDTIOCTL_MASK | 7) /* lock channel */
+#define GDTIOCTL_EVENT (GDTIOCTL_MASK | 8) /* read controller events */
+
+/* service errors */
+#define S_OK 1 /* no error */
+#define S_BSY 7 /* controller busy */
+#define S_RAW_SCSI 12 /* raw serv.: target error */
+#define S_RAW_ILL 0xff /* raw serv.: illegal */
+
+/* timeout values */
+#define INIT_RETRIES 10000 /* 10000 * 1ms = 10s */
+#define INIT_TIMEOUT 100000 /* 1000 * 1ms = 1s */
+#define POLL_TIMEOUT 10000 /* 10000 * 1ms = 10s */
+
+/* priorities */
+#define DEFAULT_PRI 0x20
+#define IOCTL_PRI 0x10
+
+/* data directions */
+#define DATA_IN 0x01000000L /* data from target */
+#define DATA_OUT 0x00000000L /* data to target */
+
+/* BMIC registers (EISA controllers) */
+#define ID0REG 0x0c80 /* board ID */
+#define EINTENABREG 0x0c89 /* interrupt enable */
+#define SEMA0REG 0x0c8a /* command semaphore */
+#define SEMA1REG 0x0c8b /* status semaphore */
+#define LDOORREG 0x0c8d /* local doorbell */
+#define EDENABREG 0x0c8e /* EISA system doorbell enable */
+#define EDOORREG 0x0c8f /* EISA system doorbell */
+#define MAILBOXREG 0x0c90 /* mailbox reg. (16 bytes) */
+#define EISAREG 0x0cc0 /* EISA configuration */
+
+/* other defines */
+#define LINUX_OS 8 /* used for cache optim. */
+#define SCATTER_GATHER 1 /* s/g feature */
+#define GDTH_MAXSG 32 /* max. s/g elements */
+#define SECS32 0x1f /* round capacity */
+#define BIOS_ID_OFFS 0x10 /* offset contr. ID in ISABIOS */
+#define LOCALBOARD 0 /* board node always 0 */
+#define ASYNCINDEX 0 /* cmd index async. event */
+#define SPEZINDEX 1 /* cmd index unknown service */
+#define GDT_WR_THROUGH 0x100 /* WRITE_THROUGH supported */
+
+/* typedefs */
+
+#pragma pack(1)
+
+typedef struct {
+ char buffer[GDTH_SCRATCH]; /* scratch buffer */
+} gdth_scratch_str;
+
+/* screenservice message */
+typedef struct {
+ ulong msg_handle; /* message handle */
+ ulong msg_len; /* size of message */
+ ulong msg_alen; /* answer length */
+ unchar msg_answer; /* answer flag */
+ unchar msg_ext; /* more messages */
+ unchar msg_reserved[2];
+ char msg_text[MSGLEN+2]; /* the message text */
+} gdth_msg_str;
+
+/* get channel count IOCTL */
+typedef struct {
+ ulong channel_no; /* number of channel */
+ ulong drive_cnt; /* number of drives */
+ unchar siop_id; /* SCSI processor ID */
+ unchar siop_state; /* SCSI processor state */
+} gdth_getch_str;
+
+/* cache info/config IOCTL */
+typedef struct {
+ ulong version; /* firmware version */
+ ushort state; /* cache state (on/off) */
+ ushort strategy; /* cache strategy */
+ ushort write_back; /* write back state (on/off) */
+ ushort block_size; /* cache block size */
+} gdth_cpar_str;
+
+typedef struct {
+ ulong csize; /* cache size */
+ ulong read_cnt; /* read/write counter */
+ ulong write_cnt;
+ ulong tr_hits; /* hits */
+ ulong sec_hits;
+ ulong sec_miss; /* misses */
+} gdth_cstat_str;
+
+typedef struct {
+ gdth_cpar_str cpar;
+ gdth_cstat_str cstat;
+} gdth_cinfo_str;
+
+/* scatter/gather element */
+typedef struct {
+ ulong sg_ptr; /* address */
+ ulong sg_len; /* length */
+} gdth_sg_str;
+
+/* command structure */
+typedef struct {
+ ulong BoardNode; /* board node (always 0) */
+ ulong CommandIndex; /* command number */
+ ushort OpCode; /* the command (READ,..) */
+ union {
+ struct {
+ ushort DeviceNo; /* number of cache drive */
+ ulong BlockNo; /* block number */
+ ulong BlockCnt; /* block count */
+ ulong DestAddr; /* dest. addr. (if s/g: -1) */
+ ulong sg_canz; /* s/g element count */
+ gdth_sg_str sg_lst[GDTH_MAXSG]; /* s/g list */
+ } cache; /* cache service cmd. str. */
+ struct {
+ ushort param_size; /* size of p_param buffer */
+ ulong subfunc; /* IOCTL function */
+ ulong channel; /* device */
+ ulong p_param; /* buffer */
+ } ioctl; /* IOCTL command structure */
+ struct {
+ ushort reserved;
+ ulong msg_handle; /* message handle */
+ ulong msg_addr; /* message buffer address */
+ } screen; /* screen service cmd. str. */
+ struct {
+ ushort reserved;
+ ulong direction; /* data direction */
+ ulong mdisc_time; /* disc. time (0: no timeout)*/
+ ulong mcon_time; /* connect time(0: no to.) */
+ ulong sdata; /* dest. addr. (if s/g: -1) */
+ ulong sdlen; /* data length (bytes) */
+ ulong clen; /* SCSI cmd. length(6,10,12) */
+ unchar cmd[12]; /* SCSI command */
+ unchar target; /* target ID */
+ unchar lun; /* LUN */
+ unchar bus; /* SCSI bus number */
+ unchar priority; /* only 0 used */
+ ulong sense_len; /* sense data length */
+ ulong sense_data; /* sense data addr. */
+ struct raw *link_p; /* linked cmds (not supp.) */
+ ulong sg_ranz; /* s/g element count */
+ gdth_sg_str sg_lst[GDTH_MAXSG]; /* s/g list */
+ } raw; /* raw service cmd. struct. */
+ } u;
+ /* additional variables */
+ unchar Service; /* controller service */
+ ushort Status; /* command result */
+ ulong Info; /* additional information */
+ Scsi_Cmnd *RequestBuffer; /* request buffer */
+} gdth_cmd_str;
+
+/* controller event structure */
+#define ES_ASYNC 1
+#define ES_DRIVER 2
+#define ES_TEST 3
+#define ES_SYNC 4
+typedef struct {
+ ushort size; /* size of structure */
+ union {
+ char stream[16];
+ struct {
+ ushort ionode;
+ ushort service;
+ ulong index;
+ } driver;
+ struct {
+ ushort ionode;
+ ushort service;
+ ushort status;
+ ulong info;
+ unchar scsi_coord[3];
+ } async;
+ struct {
+ ushort ionode;
+ ushort service;
+ ushort status;
+ ulong info;
+ ushort hostdrive;
+ unchar scsi_coord[3];
+ unchar sense_key;
+ } sync;
+ struct {
+ ulong l1, l2, l3, l4;
+ } test;
+ } eu;
+} gdth_evt_data;
+
+typedef struct {
+ ulong first_stamp;
+ ulong last_stamp;
+ ushort same_count;
+ ushort event_source;
+ ushort event_idx;
+ unchar application;
+ unchar reserved;
+ gdth_evt_data event_data;
+} gdth_evt_str;
+
+
+/* DPRAM structures */
+
+/* interface area ISA/PCI */
+typedef struct {
+ unchar S_Cmd_Indx; /* special command */
+ unchar volatile S_Status; /* status special command */
+ ushort reserved1;
+ ulong S_Info[4]; /* add. info special command */
+ unchar volatile Sema0; /* command semaphore */
+ unchar reserved2[3];
+ unchar Cmd_Index; /* command number */
+ unchar reserved3[3];
+ ushort volatile Status; /* command status */
+ ushort Service; /* service(for async.events) */
+ ulong Info[2]; /* additional info */
+ struct {
+ ushort offset; /* command offs. in the DPRAM*/
+ ushort serv_id; /* service */
+ } comm_queue[MAXOFFSETS]; /* command queue */
+ ulong bios_reserved[2];
+ unchar gdt_dpr_cmd[1]; /* commands */
+} gdt_dpr_if;
+
+/* SRAM structure PCI controllers */
+typedef struct {
+ ulong magic; /* controller ID from BIOS */
+ ushort need_deinit; /* switch betw. BIOS/driver */
+ unchar switch_support; /* see need_deinit */
+ unchar padding[9];
+ unchar os_used[16]; /* OS code per service */
+ unchar unused[28];
+ unchar fw_magic; /* contr. ID from firmware */
+} gdt_pci_sram;
+
+/* SRAM structure EISA controllers (but NOT GDT3000/3020) */
+typedef struct {
+ unchar os_used[16]; /* OS code per service */
+ ushort need_deinit; /* switch betw. BIOS/driver */
+ unchar switch_support; /* see need_deinit */
+ unchar padding;
+} gdt_eisa_sram;
+
+
+/* DPRAM ISA controllers */
+typedef struct {
+ union {
+ struct {
+ unchar bios_used[0x3c00-32]; /* 15KB - 32Bytes BIOS */
+ ulong magic; /* controller (EISA) ID */
+ ushort need_deinit; /* switch betw. BIOS/driver */
+ unchar switch_support; /* see need_deinit */
+ unchar padding[9];
+ unchar os_used[16]; /* OS code per service */
+ } dp_sram;
+ unchar bios_area[0x4000]; /* 16KB reserved for BIOS */
+ } bu;
+ union {
+ gdt_dpr_if ic; /* interface area */
+ unchar if_area[0x3000]; /* 12KB for interface */
+ } u;
+ struct {
+ unchar memlock; /* write protection DPRAM */
+ unchar event; /* release event */
+ unchar irqen; /* board interrupts enable */
+ unchar irqdel; /* acknowledge board int. */
+ unchar volatile Sema1; /* status semaphore */
+ unchar rq; /* IRQ/DRQ configuration */
+ } io;
+} gdt2_dpram_str;
+
+/* DPRAM PCI controllers */
+typedef struct {
+ union {
+ gdt_dpr_if ic; /* interface area */
+ unchar if_area[0xff0-sizeof(gdt_pci_sram)];
+ } u;
+ gdt_pci_sram gdt6sr; /* SRAM structure */
+ struct {
+ unchar unused0[1];
+ unchar volatile Sema1; /* command semaphore */
+ unchar unused1[3];
+ unchar irqen; /* board interrupts enable */
+ unchar unused2[2];
+ unchar event; /* release event */
+ unchar unused3[3];
+ unchar irqdel; /* acknowledge board int. */
+ unchar unused4[3];
+ } io;
+} gdt6_dpram_str;
+
+/* PLX register structure (new PCI controllers) */
+typedef struct {
+ unchar cfg_reg; /* DPRAM cfg.(2:below 1MB,0:anywhere)*/
+ unchar unused1[0x3f];
+ unchar volatile sema0_reg; /* command semaphore */
+ unchar volatile sema1_reg; /* status semaphore */
+ unchar unused2[2];
+ ushort volatile status; /* command status */
+ ushort service; /* service */
+ ulong info[2]; /* additional info */
+ unchar unused3[0x10];
+ unchar ldoor_reg; /* PCI to local doorbell */
+ unchar unused4[3];
+ unchar volatile edoor_reg; /* local to PCI doorbell */
+ unchar unused5[3];
+ unchar control0; /* control0 register(unused) */
+ unchar control1; /* board interrupts enable */
+ unchar unused6[0x16];
+} gdt6c_plx_regs;
+
+/* DPRAM new PCI controllers */
+typedef struct {
+ union {
+ gdt_dpr_if ic; /* interface area */
+ unchar if_area[0x4000-sizeof(gdt_pci_sram)];
+ } u;
+ gdt_pci_sram gdt6sr; /* SRAM structure */
+} gdt6c_dpram_str;
+
+/* i960 register structure (PCI MPR controllers) */
+typedef struct {
+ unchar unused1[16];
+ unchar volatile sema0_reg; /* command semaphore */
+ unchar unused2;
+ unchar volatile sema1_reg; /* status semaphore */
+ unchar unused3;
+ ushort volatile status; /* command status */
+ ushort service; /* service */
+ ulong info[2]; /* additional info */
+ unchar ldoor_reg; /* PCI to local doorbell */
+ unchar unused4[11];
+ unchar volatile edoor_reg; /* local to PCI doorbell */
+ unchar unused5[7];
+ unchar edoor_en_reg; /* board interrupts enable */
+ unchar unused6[27];
+ ulong unused7[1004]; /* size: 4 KB */
+} gdt6m_i960_regs;
+
+/* DPRAM PCI MPR controllers */
+typedef struct {
+ gdt6m_i960_regs i960r; /* 4KB i960 registers */
+ union {
+ gdt_dpr_if ic; /* interface area */
+ unchar if_area[0x3000-sizeof(gdt_pci_sram)];
+ } u;
+ gdt_pci_sram gdt6sr; /* SRAM structure */
+} gdt6m_dpram_str;
+
+
+/* PCI resources */
+typedef struct {
+ ushort device_id; /* device ID (0,..,9) */
+ unchar bus; /* PCI bus */
+ unchar device_fn; /* PCI device/function no. */
+ ulong dpmem; /* DPRAM address */
+ ulong io; /* IO address */
+ ulong io_mm; /* IO address mem. mapped */
+ ulong bios; /* BIOS address */
+ unchar irq; /* IRQ */
+} gdth_pci_str;
+
+
+/* controller information structure */
+typedef struct {
+ unchar bus_cnt; /* SCSI bus count */
+ unchar type; /* controller class */
+ ushort raw_feat; /* feat. raw service (s/g,..) */
+ ulong stype; /* controller subtype */
+ ushort cache_feat; /* feat. cache serv. (s/g,..) */
+ ushort bmic; /* BMIC address (EISA) */
+ void *brd; /* DPRAM address */
+ ulong brd_phys; /* slot number/BIOS address */
+ gdt6c_plx_regs *plx; /* PLX regs (new PCI contr.) */
+ gdth_cmd_str *pccb; /* address command structure */
+ gdth_scratch_str *pscratch;
+ unchar irq; /* IRQ */
+ unchar drq; /* DRQ (ISA controllers) */
+ ushort status; /* command status */
+ ulong info;
+ ulong info2; /* additional info */
+ Scsi_Cmnd *req_first; /* top of request queue */
+ struct {
+ unchar type; /* device type */
+ unchar heads; /* mapping */
+ unchar secs;
+ unchar lock; /* drive locked ? (hot plug) */
+ ushort hostdrive; /* host drive number */
+ ushort devtype; /* further information */
+ ulong size; /* capacity */
+ } id[MAXBUS][MAXID];
+ ushort cmd_cnt; /* command count in DPRAM */
+ ushort cmd_len; /* length of actual command */
+ ushort cmd_offs_dpmem; /* actual offset in DPRAM */
+ ushort ic_all_size; /* sizeof DPRAM interf. area */
+ unchar reserved;
+ unchar mode; /* information from /proc */
+ ushort param_size;
+ gdth_cpar_str cpar; /* controller cache par. */
+} gdth_ha_str;
+
+/* structure for scsi_register(), SCSI bus != 0 */
+typedef struct {
+ ushort hanum;
+ ushort busnum;
+} gdth_num_str;
+
+/* structure for scsi_register() */
+typedef struct {
+ gdth_num_str numext; /* must be the first element */
+ gdth_ha_str haext;
+ gdth_cmd_str cmdext;
+ gdth_scratch_str dmaext;
+} gdth_ext_str;
+
+
+/* INQUIRY data format */
+typedef struct {
+ unchar type_qual;
+ unchar modif_rmb;
+ unchar version;
+ unchar resp_aenc;
+ unchar add_length;
+ unchar reserved1;
+ unchar reserved2;
+ unchar misc;
+ unchar vendor[8];
+ unchar product[16];
+ unchar revision[4];
+} gdth_inq_data;
+
+/* READ_CAPACITY data format */
+typedef struct {
+ ulong last_block_no;
+ ulong block_length;
+} gdth_rdcap_data;
+
+/* REQUEST_SENSE data format */
+typedef struct {
+ unchar errorcode;
+ unchar segno;
+ unchar key;
+ ulong info;
+ unchar add_length;
+ ulong cmd_info;
+ unchar adsc;
+ unchar adsq;
+ unchar fruc;
+ unchar key_spec[3];
+} gdth_sense_data;
+
+/* MODE_SENSE data format */
+typedef struct {
+ struct {
+ unchar data_length;
+ unchar med_type;
+ unchar dev_par;
+ unchar bd_length;
+ } hd;
+ struct {
+ unchar dens_code;
+ unchar block_count[3];
+ unchar reserved;
+ unchar block_length[3];
+ } bd;
+} gdth_modep_data;
+
+typedef struct {
+ ulong b[10]; /* 32 bit compiler ! */
+} gdth_stackframe;
+
+#pragma pack()
+
+/* function prototyping */
+
+int gdth_detect(Scsi_Host_Template *);
+int gdth_release(struct Scsi_Host *);
+int gdth_command(Scsi_Cmnd *);
+int gdth_queuecommand(Scsi_Cmnd *,void (*done)(Scsi_Cmnd *));
+int gdth_abort(Scsi_Cmnd *);
+#if LINUX_VERSION_CODE >= 0x010346
+int gdth_reset(Scsi_Cmnd *, unsigned int reset_flags);
+#else
+int gdth_reset(Scsi_Cmnd *);
+#endif
+const char *gdth_info(struct Scsi_Host *);
+
+
+#if LINUX_VERSION_CODE >= 0x010300
+int gdth_bios_param(Disk *,kdev_t,int *);
+extern struct proc_dir_entry proc_scsi_gdth;
+int gdth_proc_info(char *,char **,off_t,int,int,int);
+#define GDTH { NULL, NULL, \
+ &proc_scsi_gdth, \
+ gdth_proc_info, \
+ "GDT SCSI Disk Array Controller", \
+ gdth_detect, \
+ gdth_release, \
+ gdth_info, \
+ gdth_command, \
+ gdth_queuecommand, \
+ gdth_abort, \
+ gdth_reset, \
+ NULL, \
+ gdth_bios_param, \
+ GDTH_MAXCMDS, \
+ -1, \
+ GDTH_MAXSG, \
+ GDTH_MAXC_P_L, \
+ 0, \
+ 1, \
+ ENABLE_CLUSTERING}
+#else
+int gdth_bios_param(Disk *,int,int *);
+#define GDTH { NULL, NULL, \
+ "GDT SCSI Disk Array Controller", \
+ gdth_detect, \
+ gdth_release, \
+ gdth_info, \
+ gdth_command, \
+ gdth_queuecommand, \
+ gdth_abort, \
+ gdth_reset, \
+ NULL, \
+ gdth_bios_param, \
+ GDTH_MAXCMDS, \
+ -1, \
+ GDTH_MAXSG, \
+ GDTH_MAXC_P_L, \
+ 0, \
+ 1, \
+ ENABLE_CLUSTERING}
+#endif
+
+#endif
+
diff --git a/drivers/scsi/gdth_ioctl.h b/drivers/scsi/gdth_ioctl.h
new file mode 100644
index 000000000..01f5db4c5
--- /dev/null
+++ b/drivers/scsi/gdth_ioctl.h
@@ -0,0 +1,86 @@
+#ifndef _GDTH_IOCTL_H
+#define _GDTH_IOCTL_H
+
+/* gdth_ioctl.h
+ * $Id: gdth_ioctl.h,v 1.1 1997/02/21 08:07:27 achim Exp $
+ */
+
+/* IOCTLs */
+#define GDTIOCTL_MASK ('J'<<8)
+#define GDTIOCTL_GENERAL (GDTIOCTL_MASK | 0) /* general IOCTL */
+#define GDTIOCTL_DRVERS (GDTIOCTL_MASK | 1) /* get driver version */
+#define GDTIOCTL_CTRTYPE (GDTIOCTL_MASK | 2) /* get controller type */
+#define GDTIOCTL_OSVERS (GDTIOCTL_MASK | 3) /* get OS version */
+#define GDTIOCTL_CTRCNT (GDTIOCTL_MASK | 5) /* get controller count */
+#define GDTIOCTL_LOCKDRV (GDTIOCTL_MASK | 6) /* lock host drive */
+#define GDTIOCTL_LOCKCHN (GDTIOCTL_MASK | 7) /* lock channel */
+#define GDTIOCTL_EVENT (GDTIOCTL_MASK | 8) /* read controller events */
+
+#define GDTIOCTL_MAGIC 0x06030f07UL
+
+
+/* IOCTL structure (write) */
+typedef struct {
+ ulong magic; /* IOCTL magic */
+ ushort ioctl; /* IOCTL */
+ ushort ionode; /* controller number */
+ ushort service; /* controller service */
+ ushort timeout; /* timeout */
+ union {
+ struct {
+ unchar command[512]; /* controller command */
+ unchar data[1]; /* add. data */
+ } general;
+ struct {
+ unchar lock; /* lock/unlock */
+ unchar drive_cnt; /* drive count */
+ ushort drives[35]; /* drives */
+ } lockdrv;
+ struct {
+ unchar lock; /* lock/unlock */
+ unchar channel; /* channel */
+ } lockchn;
+ struct {
+ int erase; /* erase event ? */
+ int handle;
+ } event;
+ } iu;
+} gdth_iowr_str;
+
+/* IOCTL structure (read) */
+typedef struct {
+ ulong size; /* buffer size */
+ ulong status; /* IOCTL error code */
+ union {
+ struct {
+ unchar data[1]; /* data */
+ } general;
+ struct {
+ ushort version; /* driver version */
+ } drvers;
+ struct {
+ unchar type; /* controller type */
+ ushort info; /* slot etc. */
+ ushort oem_id; /* OEM ID */
+ ushort bios_ver; /* not used */
+ ushort access; /* not used */
+ ushort ext_type; /* extended type */
+ } ctrtype;
+ struct {
+ unchar version; /* OS version */
+ unchar subversion; /* OS subversion */
+ ushort revision; /* revision */
+ } osvers;
+ struct {
+ ushort count; /* controller count */
+ } ctrcnt;
+ struct {
+ int handle;
+ unchar evt[32]; /* event structure */
+ } event;
+ } iu;
+} gdth_iord_str;
+
+
+#endif
+
diff --git a/drivers/scsi/gdth_proc.c b/drivers/scsi/gdth_proc.c
new file mode 100644
index 000000000..1a34996a1
--- /dev/null
+++ b/drivers/scsi/gdth_proc.c
@@ -0,0 +1,635 @@
+/* gdth_proc.c
+ * $Id: gdth_proc.c,v 1.6 1997/10/31 10:36:24 achim Exp $
+ */
+
+#include "gdth_ioctl.h"
+
+int gdth_proc_info(char *buffer,char **start,off_t offset,int length,
+ int hostno,int inout)
+{
+ int hanum,busnum,i;
+
+ TRACE2(("gdth_proc_info() length %d ha %d offs %d inout %d\n",
+ length,hostno,offset,inout));
+
+ for (i=0; i<gdth_ctr_vcount; ++i) {
+ if (gdth_ctr_vtab[i]->host_no == hostno)
+ break;
+ }
+ if (i==gdth_ctr_vcount)
+ return(-EINVAL);
+
+ hanum = NUMDATA(gdth_ctr_vtab[i])->hanum;
+ busnum= NUMDATA(gdth_ctr_vtab[i])->busnum;
+
+ if (inout)
+ return(gdth_set_info(buffer,length,i,hanum,busnum));
+ else
+ return(gdth_get_info(buffer,start,offset,length,i,hanum,busnum));
+}
+
+static int gdth_set_info(char *buffer,int length,int vh,int hanum,int busnum)
+{
+ int ret_val;
+ Scsi_Cmnd scp;
+ Scsi_Device sdev;
+ gdth_iowr_str *piowr;
+
+ TRACE2(("gdth_set_info() ha %d bus %d\n",hanum,busnum));
+ piowr = (gdth_iowr_str *)buffer;
+
+ memset(&sdev,0,sizeof(Scsi_Device));
+ memset(&scp, 0,sizeof(Scsi_Cmnd));
+ sdev.host = gdth_ctr_vtab[vh];
+ sdev.id = sdev.host->this_id;
+ scp.cmd_len = 12;
+ scp.host = gdth_ctr_vtab[vh];
+ scp.target = sdev.host->this_id;
+ scp.device = &sdev;
+ scp.use_sg = 0;
+
+ if (length >= 4) {
+ if (strncmp(buffer,"gdth",4) == 0) {
+ buffer += 5;
+ length -= 5;
+ ret_val = gdth_set_asc_info( buffer, length, hanum, scp );
+ } else if (piowr->magic == GDTIOCTL_MAGIC) {
+ ret_val = gdth_set_bin_info( buffer, length, hanum, scp );
+ } else {
+ printk("GDT: Wrong signature: %6s\n",buffer);
+ ret_val = -EINVAL;
+ }
+ } else {
+ ret_val = -EINVAL;
+ }
+ return ret_val;
+}
+
+static int gdth_set_asc_info(char *buffer,int length,int hanum,Scsi_Cmnd scp)
+{
+ int orig_length, drive, wb_mode;
+ char cmnd[12];
+ int i, j, found;
+ gdth_ha_str *ha;
+ gdth_cmd_str gdtcmd;
+ gdth_cpar_str *pcpar;
+
+ TRACE2(("gdth_set_asc_info() ha %d\n",hanum));
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ memset(cmnd, 0,10);
+ orig_length = length + 5;
+ drive = -1;
+ wb_mode = 0;
+ found = FALSE;
+
+ if (length >= 5 && strncmp(buffer,"flush",5)==0) {
+ buffer += 6;
+ length -= 6;
+ if (length && *buffer>='0' && *buffer<='9') {
+ drive = (int)(*buffer-'0');
+ ++buffer; --length;
+ if (length && *buffer>='0' && *buffer<='9') {
+ drive = drive*10 + (int)(*buffer-'0');
+ ++buffer; --length;
+ }
+ printk("GDT: Flushing host drive %d .. ",drive);
+ } else {
+ printk("GDT: Flushing all host drives .. ");
+ }
+ for (i = 0; i < MAXBUS; ++i) {
+ for (j = 0; j < MAXID; ++j) {
+ if (ha->id[i][j].type == CACHE_DTYP) {
+ if (drive != -1 &&
+ ha->id[i][j].hostdrive != (ushort)drive)
+ continue;
+ found = TRUE;
+ gdtcmd.BoardNode = LOCALBOARD;
+ gdtcmd.Service = CACHESERVICE;
+ gdtcmd.OpCode = GDT_FLUSH;
+ gdtcmd.u.cache.DeviceNo = ha->id[i][j].hostdrive;
+ gdtcmd.u.cache.BlockNo = 1;
+ gdtcmd.u.cache.sg_canz = 0;
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ scp.request.rq_status = RQ_SCSI_BUSY;
+ scp.request.sem = &sem;
+ scsi_do_cmd(&scp, cmnd, &gdtcmd,
+ sizeof(gdth_cmd_str), gdth_scsi_done,
+ 30*HZ, 1);
+ down(&sem);
+ }
+ }
+ }
+ }
+ if (!found)
+ printk("\nNo host drive found !\n");
+ else
+ printk("Done.\n");
+ return(orig_length);
+ }
+
+ if (length >= 7 && strncmp(buffer,"wbp_off",7)==0) {
+ buffer += 8;
+ length -= 8;
+ printk("GDT: Disabling write back permanently .. ");
+ wb_mode = 1;
+ } else if (length >= 6 && strncmp(buffer,"wbp_on",6)==0) {
+ buffer += 7;
+ length -= 7;
+ printk("GDT: Enabling write back permanently .. ");
+ wb_mode = 2;
+ } else if (length >= 6 && strncmp(buffer,"wb_off",6)==0) {
+ buffer += 7;
+ length -= 7;
+ printk("GDT: Disabling write back commands .. ");
+ if (ha->cache_feat & GDT_WR_THROUGH) {
+ gdth_write_through = TRUE;
+ printk("Done.\n");
+ } else {
+ printk("Not supported !\n");
+ }
+ return(orig_length);
+ } else if (length >= 5 && strncmp(buffer,"wb_on",5)==0) {
+ buffer += 6;
+ length -= 6;
+ printk("GDT: Enabling write back commands .. ");
+ gdth_write_through = FALSE;
+ printk("Done.\n");
+ return(orig_length);
+ }
+
+ if (wb_mode) {
+ pcpar = (gdth_cpar_str *)kmalloc( sizeof(gdth_cpar_str),
+ GFP_ATOMIC | GFP_DMA );
+ if (pcpar == NULL) {
+ TRACE2(("gdth_set_info(): Unable to allocate memory.\n"));
+ printk("Unable to allocate memory.\n");
+ return(-EINVAL);
+ }
+ memcpy( pcpar, &ha->cpar, sizeof(gdth_cpar_str) );
+ gdtcmd.BoardNode = LOCALBOARD;
+ gdtcmd.Service = CACHESERVICE;
+ gdtcmd.OpCode = GDT_IOCTL;
+ gdtcmd.u.ioctl.p_param = virt_to_bus(pcpar);
+ gdtcmd.u.ioctl.param_size = sizeof(gdth_cpar_str);
+ gdtcmd.u.ioctl.subfunc = CACHE_CONFIG;
+ gdtcmd.u.ioctl.channel = INVALID_CHANNEL;
+ pcpar->write_back = wb_mode==1 ? 0:1;
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ scp.request.rq_status = RQ_SCSI_BUSY;
+ scp.request.sem = &sem;
+ scsi_do_cmd(&scp, cmnd, &gdtcmd, sizeof(gdth_cmd_str),
+ gdth_scsi_done, 30*HZ, 1);
+ down(&sem);
+ }
+ kfree( pcpar );
+ printk("Done.\n");
+ return(orig_length);
+ }
+
+ printk("GDT: Unknown command: %s Length: %d\n",buffer,length);
+ return(-EINVAL);
+}
+
+static int gdth_set_bin_info(char *buffer,int length,int hanum,Scsi_Cmnd scp)
+{
+ char cmnd[12];
+ int id;
+ unchar i, j, k, found;
+ gdth_ha_str *ha;
+ gdth_iowr_str *piowr;
+ gdth_iord_str *piord;
+ gdth_cmd_str *pcmd;
+ ulong *ppadd;
+ ulong add_size, flags;
+
+
+ TRACE2(("gdth_set_bin_info() ha %d\n",hanum));
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ memset(cmnd, 0,10);
+ piowr = (gdth_iowr_str *)buffer;
+ piord = NULL;
+ pcmd = NULL;
+
+ if (length < GDTOFFSOF(gdth_iowr_str,iu))
+ return(-EINVAL);
+
+ switch (piowr->ioctl) {
+ case GDTIOCTL_GENERAL:
+ if (length < GDTOFFSOF(gdth_iowr_str,iu.general.data[0]))
+ return(-EINVAL);
+ pcmd = (gdth_cmd_str *)piowr->iu.general.command;
+ pcmd->Service = piowr->service;
+ if (pcmd->OpCode == GDT_IOCTL) {
+ ppadd = &pcmd->u.ioctl.p_param;
+ add_size = pcmd->u.ioctl.param_size;
+ } else if (piowr->service == CACHESERVICE) {
+ add_size = pcmd->u.cache.BlockCnt * SECTOR_SIZE;
+ if (ha->cache_feat & SCATTER_GATHER) {
+ ppadd = &pcmd->u.cache.sg_lst[0].sg_ptr;
+ pcmd->u.cache.DestAddr = -1UL;
+ pcmd->u.cache.sg_lst[0].sg_len = add_size;
+ pcmd->u.cache.sg_canz = 1;
+ } else {
+ ppadd = &pcmd->u.cache.DestAddr;
+ pcmd->u.cache.sg_canz = 0;
+ }
+ } else if (piowr->service == SCSIRAWSERVICE) {
+ add_size = pcmd->u.raw.sdlen;
+ if (ha->raw_feat & SCATTER_GATHER) {
+ ppadd = &pcmd->u.raw.sg_lst[0].sg_ptr;
+ pcmd->u.raw.sdata = -1UL;
+ pcmd->u.raw.sg_lst[0].sg_len = add_size;
+ pcmd->u.raw.sg_ranz = 1;
+ } else {
+ ppadd = &pcmd->u.raw.sdata;
+ pcmd->u.raw.sg_ranz = 0;
+ }
+ } else {
+ return(-EINVAL);
+ }
+ id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) + add_size );
+ if (id == -1)
+ return(-EBUSY);
+ piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum];
+
+ piord->size = sizeof(gdth_iord_str) + add_size;
+ if (add_size > 0) {
+ memcpy(piord->iu.general.data, piowr->iu.general.data, add_size);
+ *ppadd = virt_to_bus(piord->iu.general.data);
+ }
+ /* do IOCTL */
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ scp.request.rq_status = RQ_SCSI_BUSY;
+ scp.request.sem = &sem;
+ scp.SCp.this_residual = IOCTL_PRI;
+ scsi_do_cmd(&scp, cmnd, pcmd,
+ sizeof(gdth_cmd_str), gdth_scsi_done,
+ piowr->timeout*HZ, 1);
+ down(&sem);
+ piord->status = (ulong)scp.SCp.Message;
+ }
+ break;
+
+ case GDTIOCTL_DRVERS:
+ id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) );
+ if (id == -1)
+ return(-EBUSY);
+ piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum];
+ piord->size = sizeof(gdth_iord_str);
+ piord->status = S_OK;
+ piord->iu.drvers.version = (GDTH_VERSION<<8) | GDTH_SUBVERSION;
+ break;
+
+ case GDTIOCTL_CTRTYPE:
+ id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) );
+ if (id == -1)
+ return(-EBUSY);
+ piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum];
+ piord->size = sizeof(gdth_iord_str);
+ piord->status = S_OK;
+ if (ha->type == GDT_ISA || ha->type == GDT_EISA) {
+ piord->iu.ctrtype.type = (unchar)((ha->stype>>20) - 10);
+ } else if (ha->type != GDT_PCIMPR) {
+ piord->iu.ctrtype.type = (unchar)((ha->stype<<8) + 6);
+ } else {
+ piord->iu.ctrtype.type = 0xfe;
+ piord->iu.ctrtype.ext_type = 0x6000 | ha->stype;
+ }
+ piord->iu.ctrtype.info = ha->brd_phys;
+ piord->iu.ctrtype.oem_id = (ushort)GDT3_ID;
+ break;
+
+ case GDTIOCTL_CTRCNT:
+ id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) );
+ if (id == -1)
+ return(-EBUSY);
+ piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum];
+ piord->size = sizeof(gdth_iord_str);
+ piord->status = S_OK;
+ piord->iu.ctrcnt.count = (ushort)gdth_ctr_count;
+ break;
+
+ case GDTIOCTL_OSVERS:
+ id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) );
+ if (id == -1)
+ return(-EBUSY);
+ piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum];
+ piord->size = sizeof(gdth_iord_str);
+ piord->status = S_OK;
+ piord->iu.osvers.version = (unchar)(LINUX_VERSION_CODE >> 16);
+ piord->iu.osvers.subversion = (unchar)(LINUX_VERSION_CODE >> 8);
+ piord->iu.osvers.revision = (ushort)(LINUX_VERSION_CODE & 0xff);
+ break;
+
+ case GDTIOCTL_LOCKDRV:
+ id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) );
+ if (id == -1)
+ return(-EBUSY);
+ piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum];
+ for (i = k = 0; i < piowr->iu.lockdrv.drive_cnt; ++i) {
+ found = FALSE;
+ for (j = 0; j < ha->bus_cnt; ++j) {
+ for (k = 0; k < MAXID; ++k) {
+ if (ha->id[j][k].type == CACHE_DTYP &&
+ ha->id[j][k].hostdrive == piowr->iu.lockdrv.drives[i]) {
+ found = TRUE;
+ break;
+ }
+ }
+ if (found)
+ break;
+ }
+ if (!found)
+ continue;
+
+ if (piowr->iu.lockdrv.lock) {
+ save_flags( flags );
+ cli();
+ ha->id[j][k].lock = 1;
+ restore_flags( flags );
+ gdth_wait_completion( hanum, j, k );
+ gdth_stop_timeout( hanum, j, k );
+ } else {
+ save_flags( flags );
+ cli();
+ ha->id[j][k].lock = 0;
+ restore_flags( flags );
+ gdth_start_timeout( hanum, j, k );
+ gdth_next( hanum );
+ }
+ }
+ piord->size = sizeof(gdth_iord_str);
+ piord->status = S_OK;
+ break;
+
+ case GDTIOCTL_LOCKCHN:
+ id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) );
+ if (id == -1)
+ return(-EBUSY);
+ for (k = 0, j = piowr->iu.lockchn.channel; k < MAXID; ++k) {
+ if (ha->id[j][k].type != RAW_DTYP)
+ continue;
+
+ if (piowr->iu.lockchn.lock) {
+ save_flags( flags );
+ cli();
+ ha->id[j][k].lock = 1;
+ restore_flags( flags );
+ gdth_wait_completion( hanum, j, k );
+ gdth_stop_timeout( hanum, j, k );
+ } else {
+ save_flags( flags );
+ cli();
+ ha->id[j][k].lock = 0;
+ restore_flags( flags );
+ gdth_start_timeout( hanum, j, k );
+ gdth_next( hanum );
+ }
+ }
+ piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum];
+ piord->size = sizeof(gdth_iord_str);
+ piord->status = S_OK;
+ break;
+
+ case GDTIOCTL_EVENT:
+ id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) );
+ if (id == -1)
+ return(-EBUSY);
+ piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum];
+ if (piowr->iu.event.erase == 0) {
+ piord->iu.event.handle = gdth_read_event( piowr->iu.event.handle,
+ (gdth_evt_str *)piord->iu.event.evt );
+ } else {
+ piord->iu.event.handle = piowr->iu.event.handle;
+ gdth_readapp_event( (unchar)piowr->iu.event.erase,
+ (gdth_evt_str *)piord->iu.event.evt );
+ }
+ piord->size = sizeof(gdth_iord_str);
+ piord->status = S_OK;
+ break;
+
+ default:
+ return(-EINVAL);
+ }
+ /* we return a buffer ID to detect the right buffer during READ-IOCTL */
+ return id;
+}
+
+static int gdth_get_info(char *buffer,char **start,off_t offset,
+ int length,int vh,int hanum,int busnum)
+{
+ int size = 0,len = 0;
+ off_t begin = 0,pos = 0;
+ gdth_ha_str *ha;
+ gdth_iord_str *piord;
+ int id;
+
+ TRACE2(("gdth_get_info() ha %d bus %d\n",hanum,busnum));
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ id = length;
+
+ /* look for buffer ID in length */
+ if (id > 4) {
+#if LINUX_VERSION_CODE >= 0x020000
+ size = sprintf(buffer+len,
+ "%s SCSI Disk Array Controller\n",
+ gdth_ctr_name(hanum));
+#else
+ size = sprintf(buffer+len,
+ "%s SCSI Disk Array Controller (SCSI Bus %d)\n",
+ gdth_ctr_name(hanum),busnum);
+#endif
+ len += size; pos = begin + len;
+ size = sprintf(buffer+len,
+ "Firmware Version: %d.%2d\tDriver Version: %s\n",
+ (unchar)(ha->cpar.version>>8),
+ (unchar)(ha->cpar.version),GDTH_VERSION_STR);
+ len += size; pos = begin + len;
+
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+ if (pos > offset + length)
+ goto stop_output;
+
+ } else {
+ piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum];
+ if (piord == NULL)
+ goto stop_output;
+ length = piord->size;
+ memcpy(buffer+len, (char *)piord, length);
+ gdth_ioctl_free(hanum, id);
+ len += length; pos = begin + len;
+
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+ if (pos > offset + length)
+ goto stop_output;
+ }
+
+stop_output:
+ *start = buffer +(offset-begin);
+ len -= (offset-begin);
+ if (len > length)
+ len = length;
+ TRACE2(("get_info() len %d pos %d begin %d offset %d length %d size %d\n",
+ len,pos,begin,offset,length,size));
+ return(len);
+}
+
+
+void gdth_scsi_done(Scsi_Cmnd *scp)
+{
+ TRACE2(("gdth_scsi_done()\n"));
+
+ scp->request.rq_status = RQ_SCSI_DONE;
+
+ if (scp->request.sem != NULL)
+ up(scp->request.sem);
+}
+
+static int gdth_ioctl_alloc(int hanum, ushort size)
+{
+ ulong flags;
+ int i;
+
+ if (size == 0)
+ return -1;
+
+ save_flags(flags);
+ cli();
+
+ for (i = 0; i < 4; ++i) {
+ if (gdth_ioctl_tab[i][hanum] == NULL) {
+ gdth_ioctl_tab[i][hanum] = kmalloc( size, GFP_ATOMIC | GFP_DMA );
+ break;
+ }
+ }
+
+ restore_flags(flags);
+ if (i == 4 || gdth_ioctl_tab[i][hanum] == NULL)
+ return -1;
+ return (i+1);
+}
+
+static void gdth_ioctl_free(int hanum, int idx)
+{
+ ulong flags;
+
+ save_flags(flags);
+ cli();
+
+ kfree( gdth_ioctl_tab[idx-1][hanum] );
+ gdth_ioctl_tab[idx-1][hanum] = NULL;
+
+ restore_flags(flags);
+}
+
+static void gdth_wait_completion(int hanum, int busnum, int id)
+{
+ ulong flags;
+ int i;
+ Scsi_Cmnd *scp;
+
+ save_flags(flags);
+ cli();
+
+ for (i = 0; i < GDTH_MAXCMDS; ++i) {
+ scp = gdth_cmd_tab[i][hanum].cmnd;
+ if (!SPECIAL_SCP(scp) && scp->target == (unchar)id &&
+#if LINUX_VERSION_CODE >= 0x020000
+ scp->channel == (unchar)busnum)
+#else
+ NUMDATA(scp->host)->busnum == (unchar)busnum)
+#endif
+ {
+ restore_flags(flags);
+ while (!scp->SCp.have_data_in)
+ barrier();
+ save_flags(flags);
+ cli();
+ }
+ }
+ restore_flags(flags);
+}
+
+static void gdth_stop_timeout(int hanum, int busnum, int id)
+{
+ ulong flags;
+ Scsi_Cmnd *scp;
+ gdth_ha_str *ha;
+
+ save_flags(flags);
+ cli();
+ ha = HADATA(gdth_ctr_tab[hanum]);
+
+ for (scp = ha->req_first; scp; scp = (Scsi_Cmnd *)scp->SCp.ptr) {
+ if (scp->target == (unchar)id &&
+#if LINUX_VERSION_CODE >= 0x020000
+ scp->channel == (unchar)busnum)
+#else
+ NUMDATA(scp->host)->busnum == (unchar)busnum)
+#endif
+ {
+ TRACE2(("gdth_stop_timeout(): update_timeout()\n"));
+ scp->SCp.buffers_residual = gdth_update_timeout(scp, 0);
+ }
+ }
+ restore_flags(flags);
+}
+
+static void gdth_start_timeout(int hanum, int busnum, int id)
+{
+ ulong flags;
+ Scsi_Cmnd *scp;
+ gdth_ha_str *ha;
+
+ save_flags(flags);
+ cli();
+ ha = HADATA(gdth_ctr_tab[hanum]);
+
+ for (scp = ha->req_first; scp; scp = (Scsi_Cmnd *)scp->SCp.ptr) {
+ if (scp->target == (unchar)id &&
+#if LINUX_VERSION_CODE >= 0x020000
+ scp->channel == (unchar)busnum)
+#else
+ NUMDATA(scp->host)->busnum == (unchar)busnum)
+#endif
+ {
+ TRACE2(("gdth_start_timeout(): update_timeout()\n"));
+ gdth_update_timeout(scp, scp->SCp.buffers_residual);
+ }
+ }
+ restore_flags(flags);
+}
+
+static int gdth_update_timeout(Scsi_Cmnd *scp, int timeout)
+{
+ ulong flags;
+ int oldto;
+
+ save_flags(flags);
+ cli();
+
+ oldto = scp->timeout;
+ scp->timeout = timeout;
+ if (timeout > 0) {
+ if (timer_table[SCSI_TIMER].expires == 0) {
+ timer_table[SCSI_TIMER].expires = jiffies + timeout;
+ timer_active |= 1 << SCSI_TIMER;
+ } else {
+ if (jiffies + timeout < timer_table[SCSI_TIMER].expires)
+ timer_table[SCSI_TIMER].expires = jiffies + timeout;
+ }
+ }
+
+ restore_flags(flags);
+ return oldto;
+}
+
diff --git a/drivers/scsi/gdth_proc.h b/drivers/scsi/gdth_proc.h
new file mode 100644
index 000000000..a3d5dcd71
--- /dev/null
+++ b/drivers/scsi/gdth_proc.h
@@ -0,0 +1,24 @@
+#ifndef _GDTH_PROC_H
+#define _GDTH_PROC_H
+
+/* gdth_proc.h
+ * $Id: gdth_proc.h,v 1.2 1997/02/21 08:08:51 achim Exp $
+ */
+
+static int gdth_set_info(char *buffer,int length,int vh,int hanum,int busnum);
+static int gdth_set_asc_info(char *buffer,int length,int hanum,Scsi_Cmnd scp);
+static int gdth_set_bin_info(char *buffer,int length,int hanum,Scsi_Cmnd scp);
+static int gdth_get_info(char *buffer,char **start,off_t offset,
+ int length,int vh,int hanum,int busnum);
+
+static int gdth_ioctl_alloc(int hanum, ushort size);
+static void gdth_ioctl_free(int hanum, int id);
+static void gdth_wait_completion(int hanum, int busnum, int id);
+static void gdth_stop_timeout(int hanum, int busnum, int id);
+static void gdth_start_timeout(int hanum, int busnum, int id);
+static int gdth_update_timeout(Scsi_Cmnd *scp, int timeout);
+
+void gdth_scsi_done(Scsi_Cmnd *scp);
+
+#endif
+
diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
index e67ec54b5..83c912247 100644
--- a/drivers/scsi/hosts.c
+++ b/drivers/scsi/hosts.c
@@ -198,6 +198,22 @@
#include "mac53c94.h"
#endif
+#ifdef CONFIG_SCSI_GDTH
+#include "gdth.h"
+#endif
+
+#ifdef CONFIG_SCSI_PCI2000
+#include "pci2000.h"
+#endif
+
+#ifdef CONFIG_SCSI_PCI2220I
+#include "pci2220i.h"
+#endif
+
+#ifdef CONFIG_SCSI_PSI240I
+#include "psi240i.h"
+#endif
+
#ifdef CONFIG_SCSI_DEBUG
#include "scsi_debug.h"
#endif
@@ -260,6 +276,17 @@ static Scsi_Host_Template builtin_scsi_hosts[] =
#ifdef CONFIG_SCSI_ADVANSYS
ADVANSYS,
#endif
+
+#ifdef CONFIG_SCSI_PCI2000
+ PCI2000,
+#endif
+#ifdef CONFIG_SCSI_PCI2220I
+ PCI2220I,
+#endif
+#ifdef CONFIG_SCSI_PSI240I
+ PSI240I,
+#endif
+
/* BusLogic must come before aha1542.c */
#ifdef CONFIG_SCSI_BUSLOGIC
BUSLOGIC,
@@ -345,6 +372,9 @@ static Scsi_Host_Template builtin_scsi_hosts[] =
#ifdef CONFIG_SCSI_SUNESP
SCSI_SPARC_ESP,
#endif
+#ifdef CONFIG_SCSI_GDTH
+ GDTH,
+#endif
#ifdef CONFIG_SCSI_QLOGICPTI
QLOGICPTI,
#endif
diff --git a/drivers/scsi/ibmmca.c b/drivers/scsi/ibmmca.c
index 55ac986a0..1bd283a45 100644
--- a/drivers/scsi/ibmmca.c
+++ b/drivers/scsi/ibmmca.c
@@ -7,6 +7,7 @@
/* Update history:
Jan 15 1996: First public release.
+ - Martin Kolinek
Jan 23 1996: Scrapped code which reassigned scsi devices to logical
device numbers. Instead, the existing assignment (created
@@ -16,15 +17,18 @@
and also the hard disks are ordered under Linux the
same way as they are under dos (i.e., C: disk is sda,
D: disk is sdb, etc.).
+ - Martin Kolinek
I think that the CD-ROM is now detected only if a CD is
inside CD_ROM while Linux boots. This can be fixed later,
once the driver works on all types of PS/2's.
+ - Martin Kolinek
Feb 7 1996: Modified biosparam function. Fixed the CD-ROM detection.
For now, devices other than harddisk and CD_ROM are
ignored. Temporarily modified abort() function
- to behave like reset().
+ to behave like reset().
+ - Martin Kolinek
Mar 31 1996: The integrated scsi subsystem is correctly found
in PS/2 models 56,57, but not in model 76. Therefore
@@ -32,21 +36,200 @@
This function allows the user to force detection of
scsi subsystem. The kernel option has format
ibmmcascsi=n
- where n is the scsi_id (pun) of the subsystem. Most
- likely, n is 7.
+ where n is the scsi_id (pun) of the subsystem. Most likely, n is 7.
+ - Martin Kolinek
Aug 21 1996: Modified the code which maps ldns to (pun,0). It was
insufficient for those of us with CD-ROM changers.
- Chris Beauregard
-
- Mar 16 1997: Modified driver to run as a module and to support
- multiple adapters.
+
+ Dec 14 1996: More improvements to the ldn mapping. See check_devices
+ for details. Did more fiddling with the integrated SCSI detection,
+ but I think it's ultimately hopeless without actually testing the
+ model of the machine. The 56, 57, 76 and 95 (ultimedia) all have
+ different integrated SCSI register configurations. However, the 56
+ and 57 are the only ones that have problems with forced detection.
+ - Chris Beauregard
+
+ Mar 8-16 1997: Modified driver to run as a module and to support
+ multiple adapters. A structure, called ibmmca_hostdata, is now
+ present, containing all the variables, that were once only
+ available for one single adapter. The find_subsystem-routine has vanished.
+ The hardware recognition is now done in ibmmca_detect directly.
+ This routine checks for presence of MCA-bus, checks the interrupt
+ level and continues with checking the installed hardware.
+ Certain PS/2-models do not recognize a SCSI-subsystem automatically.
+ Hence, the setup defined by command-line-parameters is checked first.
+ Thereafter, the routine probes for an integrated SCSI-subsystem.
+ Finally, adapters are checked. This method has the advantage to cover all
+ possible combinations of multiple SCSI-subsystems on one MCA-board. Up to
+ eight SCSI-subsystems can be recognized and announced to the upper-level
+ drivers with this improvement. A set of defines made changes to other
+ routines as small as possible.
- Klaus Kudielka
- */
+
+ May 30 1997: (v1.5b)
+ 1) SCSI-command capability enlarged by the recognition of MODE_SELECT.
+ This needs the RD-Bit to be disabled on IM_OTHER_SCSI_CMD_CMD which
+ allows data to be written from the system to the device. It is a
+ necessary step to be allowed to set blocksize of SCSI-tape-drives and
+ the tape-speed, whithout confusing the SCSI-Subsystem.
+ 2) The recognition of a tape is included in the check_devices routine.
+ This is done by checking for TYPE_TAPE, that is already defined in
+ the kernel-scsi-environment. The markup of a tape is done in the
+ global ldn_is_tape[] array. If the entry on index ldn
+ is 1, there is a tapedrive connected.
+ 3) The ldn_is_tape[] array is necessary to distinguish between tape- and
+ other devices. Fixed blocklength devices should not cause a problem
+ with the SCB-command for read and write in the ibmmca_queuecommand
+ subroutine. Therefore, I only derivate the READ_XX, WRITE_XX for
+ the tape-devices, as recommended by IBM in this Technical Reference,
+ mentioned below. (IBM recommends to avoid using the read/write of the
+ subsystem, but the fact was, that read/write causes a command error from
+ the subsystem and this causes kernel-panic.)
+ 4) In addition, I propose to use the ldn instead of a fix char for the
+ display of PS2_DISK_LED_ON(). On 95, one can distinguish between the
+ devices that are accessed. It shows activity and easyfies debugging.
+ The tape-support has been tested with a SONY SDT-5200 and a HP DDS-2
+ (I do not know yet the type). Optimization and CD-ROM audio-support,
+ I am working on ...
+ - Michael Lang
+
+ June 19 1997: (v1.6b)
+ 1) Submitting the extra-array ldn_is_tape[] -> to the local ld[]
+ device-array.
+ 2) CD-ROM Audio-Play seems to work now.
+ 3) When using DDS-2 (120M) DAT-Tapes, mtst shows still density-code
+ 0x13 for ordinary DDS (61000 BPM) instead 0x24 for DDS-2. This appears
+ also on Adaptec 2940 adaptor in a PCI-System. Therefore, I assume that
+ the problem is independent of the low-level-driver/bus-architecture.
+ 4) Hexadecimal ldn on PS/2-95 LED-display.
+ 5) Fixing of the PS/2-LED on/off that it works right with tapedrives and
+ does not confuse the disk_rw_in_progress counter.
+ - Michael Lang
+
+ June 21 1997: (v1.7b)
+ 1) Adding of a proc_info routine to inform in /proc/scsi/ibmmca/<host> the
+ outer-world about operational load statistics on the different ldns,
+ seen by the driver. Everybody that has more than one IBM-SCSI should
+ test this, because I only have one and cannot see what happens with more
+ than one IBM-SCSI hosts.
+ 2) Definition of a driver version-number to have a better recognition of
+ the source when there are existing too much releases that may confuse
+ the user, when reading about release-specific problems. Up to know,
+ I calculated the version-number to be 1.7. Because we are in BETA-test
+ yet, it is today 1.7b.
+ 3) Sorry for the heavy bug I programmed on June 19 1997! After that, the
+ CD-ROM did not work any more! The C7-command was a fake impression
+ I got while programming. Now, the READ and WRITE commands for CD-ROM are
+ no longer running over the subsystem, but just over
+ IM_OTHER_SCSI_CMD_CMD. On my observations (PS/2-95), now CD-ROM mounts
+ much faster(!) and hopefully all fancy multimedia-functions, like direct
+ digital recording from audio-CDs also work. (I tried it with cdda2wav
+ from the cdwtools-package and it filled up the harddisk immediately :-).)
+ To easify boolean logics, a further local device-type in ld[], called
+ is_cdrom has been included.
+ 4) If one uses a SCSI-device of unsupported type/commands, one
+ immediately runs into a kernel-panic caused by Command Error. To better
+ understand which SCSI-command caused the problem, I extended this
+ specific panic-message slightly.
+ - Michael Lang
+
+ June 25 1997: (v1.8b)
+ 1) Some cosmetical changes for the handling of SCSI-device-types.
+ Now, also CD-Burners / WORMs and SCSI-scanners should work. For
+ MO-drives I have no experience, therefore not yet supported.
+ In logical_devices I changed from different type-variables to one
+ called 'device_type' where the values, corresponding to scsi.h,
+ of a SCSI-device are stored.
+ 2) There existed a small bug, that maps a device, coming after a SCSI-tape
+ wrong. Therefore, e.g. a CD-ROM changer would have been mapped wrong
+ -> problem removed.
+ 3) Extension of the logical_device structure. Now it contains also device,
+ vendor and revision-level of a SCSI-device for internal usage.
+ - Michael Lang
+
+ June 26-29 1997: (v2.0b)
+ 1) The release number 2.0b is necessary because of the completely new done
+ recognition and handling of SCSI-devices with the adapter. As I got
+ from Chris the hint, that the subsystem can reassign ldns dynamically,
+ I remembered this immediate_assign-command, I found once in the handbook.
+ Now, the driver first kills all ldn assignments that are set by default
+ on the SCSI-subsystem. After that, it probes on all puns and luns for
+ devices by going through all combinations with immediate_assign and
+ probing for devices, using device_inquiry. The found physical(!) pun,lun
+ structure is stored in get_scsi[][] as device types. This is followed
+ by the assignment of all ldns to existing SCSI-devices. If more ldns
+ than devices are available, they are assigned to non existing pun,lun
+ combinations to satisfy the adapter. With this, the dynamical mapping
+ was possible to implement. (For further info see the text in the
+ source-code and in the description below. Read the description
+ below BEFORE installing this driver on your system!)
+ 2) Changed the name IBMMCA_DRIVER_VERSION to IBMMCA_SCSI_DRIVER_VERSION.
+ 3) The LED-display shows on PS/2-95 no longer the ldn, but the SCSI-ID
+ (pun) of the accessed SCSI-device. This is now senseful, because the
+ pun known within the driver is exactly the pun of the physical device
+ and no longer a fake one.
+ 4) The /proc/scsi/ibmmca/<host_no> consists now of the first part, where
+ hit-statistics of ldns is shown and a second part, where the maps of
+ physical and logical SCSI-devices are displayed. This could be very
+ interesting, when one is using more than 15 SCSI-devices in order to
+ follow the dynamical remapping of ldns.
+ - Michael Lang
+
+ June 26-29 1997: (v2.0b-1)
+ 1) I forgot to switch the local_checking_phase_flag to 1 and back to 0
+ in the dynamical remapping part in ibmmca_queuecommand for the
+ device_exist routine. Sorry.
+ - Michael Lang
+
+ July 1-13 1997: (v3.0b,c)
+ 1) Merging of the driver-developments of Klaus Kudielka and Michael Lang
+ in order to get a optimum and unified driver-release for the
+ IBM-SCSI-Subsystem-Adapter(s).
+ For people, using the Kernel-release >=2.1.0, module-support should
+ be no problem. For users, running under <2.1.0, module-support may not
+ work, because the methods have changed between 2.0.x and 2.1.x.
+ 2) Added some more effective statistics for /proc-output.
+ 3) Change typecasting at necessary points from (unsigned long) to
+ virt_to_bus().
+ 4) Included #if... at special points to have specific adaption of the
+ driver to kernel 2.0.x and 2.1.x. It should therefore also run with
+ later releases.
+ 5) Magneto-Optical drives and medium-changers are also recognized, now.
+ Therefore, we have a completely gapfree recognition of all SCSI-
+ device-types, that are known by Linux up to kernel 2.1.31.
+ 6) The flag SCSI_IBMMCA_DEV_RESET has been inserted. If it is set within
+ the configuration, each connected SCSI-device will get a reset command
+ during boottime. This can be necessary for some special SCSI-devices.
+ This flag should be included in Config.in.
+ (See also the new Config.in file.)
+ Probable next improvement: bad disk handler.
+ - Michael Lang
+
+ Sept 14 1997: (v3.0c)
+ 1) Some debugging and speed optimization applied.
+ - Michael Lang
+
+
+
+ TODO:
+
+ - It seems that the handling of bad disks is really bad -
+ non-existent, in fact.
+ - More testing of the full driver-controlled dynamical ldn
+ (re)mapping for up to 56 SCSI-devices.
+ - Support more SCSI-device-types, if Linux defines more.
+ - Support more of the SCSI-command set.
+ - Support some of the caching abilities, particularly Read Prefetch.
+ This fetches data into the cache, which later gets hit by the
+ regular Read Data.
+ - Abort and Reset functions still slightly buggy. Especially when
+ floppydisk(!) operations report errors.
+
+******************************************************************************/
#include <linux/module.h>
-
-#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/head.h>
#include <linux/types.h>
@@ -65,8 +248,13 @@
#include "hosts.h"
#include "ibmmca.h"
+#include <linux/config.h> /* for CONFIG_SCSI_IBMMCA etc. */
+
/*--------------------------------------------------------------------*/
+/* current version of this driver-source: */
+#define IBMMCA_SCSI_DRIVER_VERSION "3.0d"
+
/*
Driver Description
@@ -115,6 +303,47 @@
ldn -> (ldn/8,ldn%8). We end up with a real mishmash of puns
and luns, but it all seems to work. - Chris Beaurgard
+ And that last paragraph is also no longer correct. It uses a
+ slightly more complex mapping that will always map hard disks to
+ (x,0), for some x, and consecutive none disk devices will usually
+ share puns.
+
+ Again, the last paragraphs are no longer correct. Now, the physical
+ SCSI-devices on the SCSI-bus are probed via immediate_assign- and
+ device_inquiry-commands. This delivers a exact map of the physical
+ SCSI-world that is now stored in the get_scsi[][]-array. This means,
+ that the once hidden pun,lun assignment is now known to this driver.
+ It no longer believes in default-settings of the subsystem and maps all
+ ldns to existing pun,lun by foot. This assures full control of the ldn
+ mapping and allows dynamical remapping of ldns to different pun,lun, if
+ there are more SCSI-devices installed than ldns available (n>15). The
+ ldns from 0 to 6 get 'hardwired' by this driver to puns 0 to 7 at lun=0,
+ excluding the pun of the subsystem. This assures, that at least simple
+ SCSI-installations have optimum access-speed and are not touched by
+ dynamical remapping. The ldns 7 to 14 are put to existing devices with
+ lun>0 or to non-existing devices, in order to satisfy the subsystem, if
+ there are less than 15 SCSI-devices connected. In the case of more than 15
+ devices, the dynamical mapping goes active. If the get_scsi[][] reports a
+ device to be existant, but it has no ldn assigned, it gets a ldn out of 7
+ to 14. The numbers are assigned in cyclic order. Therefore it takes 8
+ dynamical assignments on SCSI-devices, until a certain device
+ looses its ldn again. This assures, that dynamical remapping is avoided
+ during intense I/O between up to eight SCSI-devices (means pun,lun
+ combinations). A further advantage of this method is, that people who
+ build their kernel without probing on all luns will get what they expect.
+
+ IMPORTANT: Because of the now correct recognition of physical pun,lun, and
+ their report to mid-level- and higher-level-drivers, the new reported puns
+ can be different from the old, faked puns. Therefore, Linux will eventually
+ change /dev/sdXXX assignments and prompt you for corrupted superblock
+ repair on boottime. In this case DO NOT PANIC, YOUR DISKS ARE STILL OK!!!
+ You have to reboot (CTRL-D) with a old kernel and set the /etc/fstab-file
+ entries right. After that, the system should come up as errorfree as before.
+ If your boot-partition is not coming up, also edit the /etc/lilo.conf-file
+ in a Linux session booted on old kernel and run lilo before reboot. Check
+ lilo.conf anyway to get boot on other partitions with foreign OSes right
+ again.
+
(C) Regular Processing
Only three functions get involved: ibmmca_queuecommand(), issue_cmd(),
and interrupt_handler().
@@ -123,7 +352,8 @@
ibmmca_queuecommand(). This function fills a "subsystem control block"
(scb) and calls a local function issue_cmd(), which writes a scb
command into subsystem I/O ports. Once the scb command is carried out,
- interrupt_handler() is invoked.
+ interrupt_handler() is invoked. If a device is determined to be existant
+ and it has not assigned any ldn, it gets one dynamically.
(D) Abort, Reset.
These are implemented with busy waiting for interrupt to arrive.
@@ -137,12 +367,24 @@
100% sure that it is correct for larger disks.
(F) Kernel Boot Option
- The function ibmmca_scsi_setup() is called if option ibmmcascsi=...
+ The function ibmmca_scsi_setup() is called if option ibmmcascsi=n
is passed to the kernel. See file linux/init/main.c for details.
+
+ (G) Driver Module Support
+ Is implemented and tested by K. Kudielka. This could probably not work
+ on kernels <2.1.0.
+
+ (H) Multiple Hostadapter Support
+ This driver supports up to eight interfaces of type IBM-SCSI-Subsystem.
+ Integrated-, and MCA-adapters are automatically recognized. Unrecognizable
+ IBM-SCSI-Subsystem interfaces can be specified as kernel-parameters.
+
+ (I) /proc-Filesystem Information
+ Information about the driver condition is given in
+ /proc/scsi/ibmmca/<host_no>. ibmmca_proc_info provides this information.
*/
/*--------------------------------------------------------------------*/
-
/* Here are the values and structures specific for the subsystem.
* The source of information is "Update for the PS/2 Hardware
* Interface Technical Reference, Common Interfaces", September 1991,
@@ -152,26 +394,57 @@
* In addition to SCSI subsystem, this update contains fairly detailed
* (at hardware register level) sections on diskette controller,
* keyboard controller, serial port controller, VGA, and XGA.
+ *
+ * Additional information from "Personal System/2 Micro Channel SCSI
+ * Adapter with Cache Technical Reference", March 1990, PN 68X2365,
+ * probably available from the same source (or possibly found buried
+ * in officemates desk).
+ *
+ * Further literature/program-sources referred for this driver:
+ *
+ * Friedhelm Schmidt, "SCSI-Bus und IDE-Schnittstelle - Moderne Peripherie-
+ * Schnittstellen: Hardware, Protokollbeschreibung und Anwendung", 2. Aufl.
+ * Addison Wesley, 1996.
+ *
+ * Michael K. Johnson, "The Linux Kernel Hackers' Guide", Version 0.6, Chapel
+ * Hill - North Carolina, 1995
+ *
+ * Andreas Kaiser, "SCSI TAPE BACKUP for OS/2 2.0", Version 2.12, Stuttgart
+ * 1993
*/
+/*--------------------------------------------------------------------*/
+
/* driver configuration */
#define IM_MAX_HOSTS 8 /* maximum number of host adapters */
#define IM_RESET_DELAY 10 /* seconds allowed for a reset */
/* driver debugging - #undef all for normal operation */
-#undef IM_DEBUG_TIMEOUT 50 /* if defined: count interrupts
- and ignore this special one */
-#undef IM_DEBUG_INT /* verbose interrupt */
-#undef IM_DEBUG_CMD /* verbose queuecommand */
-
-/* addresses of hardware registers on the subsystem */
-#define IM_CMD_REG (shpnt->io_port) /*Command Interface, (4 bytes long) */
-#define IM_ATTN_REG (shpnt->io_port+4) /*Attention (1 byte) */
-#define IM_CTR_REG (shpnt->io_port+5) /*Basic Control (1 byte) */
-#define IM_INTR_REG (shpnt->io_port+6) /*Interrupt Status (1 byte, r/o) */
-#define IM_STAT_REG (shpnt->io_port+7) /*Basic Status (1 byte, read only) */
+/* if defined: count interrupts and ignore this special one: */
+#undef IM_DEBUG_TIMEOUT 50
+/* verbose interrupt: */
+#undef IM_DEBUG_INT
+/* verbose queuecommand: */
+#undef IM_DEBUG_CMD
+/* verbose queucommand for specific SCSI-device type: */
+#undef IM_DEBUG_CMD_SPEC_DEV
+/* verbose device probing */
+#undef IM_DEBUG_PROBE
+
+/* device type that shall be displayed on syslog (only during debugging): */
+#define IM_DEBUG_CMD_DEVICE TYPE_TAPE
+
+/* relative addresses of hardware registers on a subsystem */
+#define IM_CMD_REG (shpnt->io_port) /*Command Interface, (4 bytes long) */
+#define IM_ATTN_REG (shpnt->io_port+4) /*Attention (1 byte) */
+#define IM_CTR_REG (shpnt->io_port+5) /*Basic Control (1 byte) */
+#define IM_INTR_REG (shpnt->io_port+6) /*Interrupt Status (1 byte, r/o) */
+#define IM_STAT_REG (shpnt->io_port+7) /*Basic Status (1 byte, read only) */
+
+/* basic I/O-port of first adapter */
#define IM_IO_PORT 0x3540
+/* maximum number of hosts that can be find */
#define IM_N_IO_PORT 8
/*requests going into the upper nibble of the Attention register */
@@ -204,11 +477,11 @@
/*immediate commands (word written into low 2 bytes of command reg) */
#define IM_RESET_IMM_CMD 0x0400
-#define IM_FORMAT_PREP_IMM_CMD 0x0417
#define IM_FEATURE_CTR_IMM_CMD 0x040c
#define IM_DMA_PACING_IMM_CMD 0x040d
#define IM_ASSIGN_IMM_CMD 0x040e
#define IM_ABORT_IMM_CMD 0x040f
+#define IM_FORMAT_PREP_IMM_CMD 0x0417
/*SCB (Subsystem Control Block) structure */
struct im_scb
@@ -257,6 +530,13 @@ struct im_sge
#define IM_DEVICE_INQUIRY_CMD 0x1c0b
#define IM_OTHER_SCSI_CMD_CMD 0x241f
+/* unused, but supported, SCB commands */
+#define IM_GET_COMMAND_COMPLETE_STATUS_CMD 0x1c07 /* command status */
+#define IM_GET_POS_INFO_CMD 0x1c0a /* returns neat stuff */
+#define IM_READ_PREFETCH_CMD 0x1c31 /* caching controller only */
+#define IM_FOMAT_UNIT_CMD 0x1c16 /* format unit */
+#define IM_REASSIGN_BLOCK_CMD 0x1c18 /* in case of error */
+
/*values to set bits in the enable word of SCB */
#define IM_READ_CONTROL 0x8000
#define IM_REPORT_TSB_ONLY_ON_ERROR 0x4000
@@ -286,10 +566,41 @@ struct im_tsb
/*subsystem uses interrupt request level 14 */
#define IM_IRQ 14
-/*PS2 disk led is turned on/off by bits 6,7 of system control port */
-#define PS2_SYS_CTR 0x92
-#define PS2_DISK_LED_ON() outb(inb(PS2_SYS_CTR) | 0xc0, PS2_SYS_CTR)
-#define PS2_DISK_LED_OFF() outb(inb(PS2_SYS_CTR) & 0x3f, PS2_SYS_CTR)
+/*--------------------------------------------------------------------*/
+/*
+ The model 95 doesn't have a standard activity light. Instead it
+ has a row of LEDs on the front. We use the last one as the activity
+ indicator if we think we're on a model 95. I suspect the model id
+ check will be either too narrow or too general, and some machines
+ won't have an activity indicator. Oh well...
+
+ The regular PS/2 disk led is turned on/off by bits 6,7 of system
+ control port.
+*/
+
+/* LED display-port (actually, last LED on display) */
+#define MOD95_LED_PORT 0x108
+/* system-control-register of PS/2s with diskindicator */
+#define PS2_SYS_CTR 0x92
+
+/* The SCSI-ID(!) of the accessed SCSI-device is shown on PS/2-95 machines' LED
+ displays. ldn is no longer displayed here, because the ldn mapping is now
+ done dynamically and the ldn <-> pun,lun maps can be looked-up at boottime
+ or during uptime in /proc/scsi/ibmmca/<host_no> in case of trouble,
+ interest, debugging or just for having fun. The left number gives the
+ host-adapter number and the right shows the accessed SCSI-ID. */
+
+#define PS2_DISK_LED_ON(ad,id) {\
+ if( machine_id == 0xf8 ) { outb((char)(id+48), MOD95_LED_PORT ); \
+ outb((char)(ad+48), MOD95_LED_PORT+1); } \
+ else outb(inb(PS2_SYS_CTR) | 0xc0, PS2_SYS_CTR); \
+}
+
+#define PS2_DISK_LED_OFF() {\
+ if( machine_id == 0xf8 ) { outb( ' ', MOD95_LED_PORT ); \
+ outb(' ', MOD95_LED_PORT+1); } \
+ else outb(inb(PS2_SYS_CTR) | 0x3f, PS2_SYS_CTR); \
+}
/*--------------------------------------------------------------------*/
@@ -315,7 +626,8 @@ struct proc_dir_entry proc_scsi_ibmmca =
S_IFDIR | S_IRUGO | S_IXUGO, 2
};
-/*max number of logical devices (can be up to 15) */
+/* Max number of logical devices (can be up from 0 to 14). 15 is the address
+of the adapter itself. */
#define MAX_LOG_DEV 15
/*local data for a logical device */
@@ -324,50 +636,108 @@ struct logical_device
struct im_scb scb;
struct im_tsb tsb;
struct im_sge sge[16];
- Scsi_Cmnd *cmd;
- int is_disk;
+ Scsi_Cmnd *cmd; /* SCSI-command that is currently in progress */
+
+ int device_type; /* type of the SCSI-device. See include/scsi/scsi.h
+ for interpreation of the possible values */
int block_length;
};
+/* statistics of the driver during operations (for proc_info) */
+struct Driver_Statistics
+ {
+ /* SCSI statistics on the adapter */
+ int ldn_access[MAX_LOG_DEV+1]; /* total accesses on a ldn */
+ int ldn_read_access[MAX_LOG_DEV+1]; /* total read-access on a ldn */
+ int ldn_write_access[MAX_LOG_DEV+1]; /* total write-access on a ldn */
+ int total_accesses; /* total accesses on all ldns */
+ int total_interrupts; /* total interrupts (should be
+ same as total_accesses) */
+ /* dynamical assignment statistics */
+ int total_scsi_devices; /* number of physical pun,lun */
+ int dyn_flag; /* flag showing dynamical mode */
+ int dynamical_assignments; /* number of remappings of ldns */
+ int ldn_assignments[MAX_LOG_DEV+1]; /* number of remappings of each
+ ldn */
+ };
+
/* data structure for each host adapter */
struct ibmmca_hostdata
- {
- /* array of logical devices */
+{
+ /* array of logical devices */
struct logical_device _ld[MAX_LOG_DEV];
- /* array to convert (pun, lun) into logical device number */
+ /* array to convert (pun, lun) into logical device number */
unsigned char _get_ldn[8][8];
- /* used only when checking logical devices */
+ /*array that contains the information about the physical SCSI-devices
+ attached to this host adapter */
+ unsigned char _get_scsi[8][8];
+ /* used only when checking logical devices */
int _local_checking_phase_flag;
int _got_interrupt;
int _stat_result;
- /* reset status (used only when doing reset) */
+ /* reset status (used only when doing reset) */
int _reset_status;
- };
-
-/* reset status values */
-#define IM_RESET_NOT_IN_PROGRESS 0
-#define IM_RESET_IN_PROGRESS 1
-#define IM_RESET_FINISHED_OK 2
-#define IM_RESET_FINISHED_FAIL 3
+ /* code of the last SCSI command (needed for panic info) */
+ int _last_scsi_command;
+ /* counter that points on next reassignable ldn for dynamical remapping */
+ /* The default value is 7, that is the first reassignable number in
+ the list on startup. */
+ int _next_ldn;
+ /* Statistics for this IBM-SCSI-host */
+ struct Driver_Statistics _IBM_DS;
+};
/* macros to access host data structure */
#define HOSTDATA(shpnt) ((struct ibmmca_hostdata *) shpnt->hostdata)
#define subsystem_pun (shpnt->this_id)
#define ld (HOSTDATA(shpnt)->_ld)
#define get_ldn (HOSTDATA(shpnt)->_get_ldn)
+#define get_scsi (HOSTDATA(shpnt)->_get_scsi)
#define local_checking_phase_flag (HOSTDATA(shpnt)->_local_checking_phase_flag)
#define got_interrupt (HOSTDATA(shpnt)->_got_interrupt)
#define stat_result (HOSTDATA(shpnt)->_stat_result)
#define reset_status (HOSTDATA(shpnt)->_reset_status)
+#define last_scsi_command (HOSTDATA(shpnt)->_last_scsi_command)
+#define next_ldn (HOSTDATA(shpnt)->_next_ldn)
+#define IBM_DS (HOSTDATA(shpnt)->_IBM_DS)
+
+/* Define a arbitrary number as subsystem-marker-type. This number is, as
+ described in the SCSI-Standard, not occupied by other device-types. */
+#define TYPE_IBM_SCSI_ADAPTER 0x2F
+
+/* Define 0xFF for no device type, because this type is not defined within
+ the SCSI-standard, therefore, it can be used and should not cause any
+ harm. */
+#define TYPE_NO_DEVICE 0xFF
+
+/* define medium-changer. If this is not defined previously, define
+ this type here. */
+#ifndef TYPE_MEDIUM_CHANGER
+#define TYPE_MEDIUM_CHANGER 0x08
+#endif
-/*--------------------------------------------------------------------*/
+/* define operations for immediate_assign */
+#define SET_LDN 0
+#define REMOVE_LDN 1
+
+/* reset status flag contents */
+#define IM_RESET_NOT_IN_PROGRESS 0
+#define IM_RESET_IN_PROGRESS 1
+#define IM_RESET_FINISHED_OK 2
+#define IM_RESET_FINISHED_FAIL 3
+
+/*-----------------------------------------------------------------------*/
/* if this is nonzero, ibmmcascsi option has been passed to the kernel */
-static int io_port[IM_MAX_HOSTS] = { 0 };
-static int scsi_id[IM_MAX_HOSTS] = { 7 };
+static int io_port[IM_MAX_HOSTS] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+static int scsi_id[IM_MAX_HOSTS] = { 7, 7, 7, 7, 7, 7, 7, 7 };
+/* fill module-parameters only, when this define is present.
+ (that is kernel >=2.1.0) */
+#ifdef MODULE_PARM
MODULE_PARM(io_port, "1-" __MODULE_STRING(IM_MAX_HOSTS) "i");
-MODULE_PARM(scsi_id, "1-" __MODULE_STRING(IM_MAX_HOSTS) "i");
+MODULE_PARM(scsi_id, "1-" __MODULE_STRING(IM_MAX_HOSTS) "i");
+#endif
/*counter of concurrent disk read/writes, to turn on/off disk led */
static int disk_rw_in_progress = 0;
@@ -375,26 +745,34 @@ static int disk_rw_in_progress = 0;
/* host information */
static int found = 0;
static struct Scsi_Host *hosts[IM_MAX_HOSTS+1] = { NULL };
+/*-----------------------------------------------------------------------*/
-/*--------------------------------------------------------------------*/
-
-/*local functions */
-static void interrupt_handler (int irq, void *dev_id,
- struct pt_regs *regs);
-static void issue_cmd (struct Scsi_Host *shpnt, unsigned long cmd_reg,
- unsigned char attn_reg);
+/*local functions in forward declaration */
+static void interrupt_handler (int irq, void *dev_id, struct pt_regs *regs);
+static void issue_cmd (struct Scsi_Host *shpnt, unsigned long cmd_reg,
+ unsigned char attn_reg);
static void internal_done (Scsi_Cmnd * cmd);
static void check_devices (struct Scsi_Host *shpnt);
-static int device_exists (struct Scsi_Host *shpnt, int ldn, int *is_disk,
- int *block_length);
-static struct Scsi_Host *ibmmca_register(Scsi_Host_Template * template,
+static int immediate_assign(struct Scsi_Host *shpnt, unsigned int pun,
+ unsigned int lun, unsigned int ldn,
+ unsigned int operation);
+static int device_inquiry(struct Scsi_Host *shpnt, int ldn,
+ unsigned char *buf);
+static char *ti_p(int value);
+static char *ti_l(int value);
+static int device_exists (struct Scsi_Host *shpnt, int ldn, int *block_length,
+ int *device_type);
+static struct Scsi_Host *ibmmca_register(Scsi_Host_Template * template,
int port, int id);
+/* local functions needed for proc_info */
+static int ldn_access_load(struct Scsi_Host *shpnt, int ldn);
+static int ldn_access_total_read_write(struct Scsi_Host *shpnt);
+
/*--------------------------------------------------------------------*/
static void
-interrupt_handler (int irq, void *dev_id,
- struct pt_regs *regs)
+interrupt_handler (int irq, void *dev_id, struct pt_regs *regs)
{
int i = 0;
struct Scsi_Host *shpnt;
@@ -402,21 +780,24 @@ interrupt_handler (int irq, void *dev_id,
unsigned int cmd_result;
unsigned int ldn;
- do shpnt = hosts[i++];
+ /* search for one adapter-response on shared interrupt */
+ do
+ shpnt = hosts[i++];
while (shpnt && !(inb(IM_STAT_REG) & IM_INTR_REQUEST));
+
+ /* return if some other device on this IRQ caused the interrupt */
if (!shpnt) return;
/*get command result and logical device */
- intr_reg = inb(IM_INTR_REG);
+ intr_reg = inb (IM_INTR_REG);
cmd_result = intr_reg & 0xf0;
ldn = intr_reg & 0x0f;
/*must wait for attention reg not busy, then send EOI to subsystem */
- while (1)
- {
+ while (1) {
cli ();
- if (!(inb (IM_STAT_REG) & IM_BUSY))
- break;
+ if (!(inb (IM_STAT_REG) & IM_BUSY))
+ break;
sti ();
}
outb (IM_EOI | ldn, IM_ATTN_REG);
@@ -424,17 +805,24 @@ interrupt_handler (int irq, void *dev_id,
/*these should never happen (hw fails, or a local programming bug) */
if (cmd_result == IM_ADAPTER_HW_FAILURE)
- panic ("IBM MCA SCSI: subsystem hardware failure.\n");
+ panic ("IBM MCA SCSI: subsystem hardware failure. Last SCSI_CMD=0x%X. \n",
+ last_scsi_command);
if (cmd_result == IM_CMD_ERROR)
- panic ("IBM MCA SCSI: command error.\n");
+ panic ("IBM MCA SCSI: command error. Last SCSI_CMD=0x%X. \n",
+ last_scsi_command);
if (cmd_result == IM_SOFTWARE_SEQUENCING_ERROR)
- panic ("IBM MCA SCSI: software sequencing error.\n");
+ panic ("IBM MCA SCSI: software sequencing error. Last SCSI_CMD=0x%X. \n",
+ last_scsi_command);
+ /* if no panic appeared, increase the interrupt-counter */
+ IBM_DS.total_interrupts++;
+
/*only for local checking phase */
if (local_checking_phase_flag)
{
stat_result = cmd_result;
got_interrupt = 1;
+ reset_status = IM_RESET_FINISHED_OK;
return;
}
@@ -454,6 +842,9 @@ interrupt_handler (int irq, void *dev_id,
}
else
{
+ /*reset disk led counter, turn off disk led */
+ disk_rw_in_progress = 0;
+ PS2_DISK_LED_OFF ();
reset_status = IM_RESET_FINISHED_OK;
}
return;
@@ -464,12 +855,12 @@ interrupt_handler (int irq, void *dev_id,
#ifdef IM_DEBUG_TIMEOUT
{
- static int count = 0;
+ static int count = 0;
- if (++count == IM_DEBUG_TIMEOUT) {
- printk("IBM MCA SCSI: Ignoring interrupt.\n");
- return;
- }
+ if (++count == IM_DEBUG_TIMEOUT) {
+ printk("IBM MCA SCSI: Ignoring interrupt.\n");
+ return;
+ }
}
#endif
@@ -481,25 +872,28 @@ interrupt_handler (int irq, void *dev_id,
#ifdef IM_DEBUG_INT
printk("cmd=%02x ireg=%02x ds=%02x cs=%02x de=%02x ce=%02x\n",
- cmd->cmnd[0], intr_reg,
- ld[ldn].tsb.dev_status, ld[ldn].tsb.cmd_status,
- ld[ldn].tsb.dev_error, ld[ldn].tsb.cmd_error);
+ cmd->cmnd[0], intr_reg,
+ ld[ldn].tsb.dev_status, ld[ldn].tsb.cmd_status,
+ ld[ldn].tsb.dev_error, ld[ldn].tsb.cmd_error);
#endif
- /*if this is end of disk read/write, may turn off PS/2 disk led */
- if (ld[ldn].is_disk)
- {
+ /*if this is end of media read/write, may turn off PS/2 disk led */
+ if ((ld[ldn].device_type!=TYPE_NO_LUN)&&
+ (ld[ldn].device_type!=TYPE_NO_DEVICE))
+ { /* only access this, if there was a valid device addressed */
switch (cmd->cmnd[0])
{
case READ_6:
case WRITE_6:
case READ_10:
case WRITE_10:
+ case READ_12:
+ case WRITE_12:
if (--disk_rw_in_progress == 0)
PS2_DISK_LED_OFF ();
}
}
-
+
/*write device status into cmd->result, and call done function */
if (cmd_result == IM_CMD_COMPLETED_WITH_FAILURE)
cmd->result = ld[ldn].tsb.dev_status & 0x1e;
@@ -512,8 +906,8 @@ interrupt_handler (int irq, void *dev_id,
/*--------------------------------------------------------------------*/
static void
-issue_cmd (struct Scsi_Host *shpnt, unsigned long cmd_reg,
- unsigned char attn_reg)
+issue_cmd (struct Scsi_Host *shpnt, unsigned long cmd_reg,
+ unsigned char attn_reg)
{
/*must wait for attention reg not busy */
while (1)
@@ -540,51 +934,23 @@ internal_done (Scsi_Cmnd * cmd)
/*--------------------------------------------------------------------*/
-static int
-ibmmca_getinfo (char *buf, int slot, void *dev)
+static int ibmmca_getinfo (char *buf, int slot, void *dev)
{
struct Scsi_Host *shpnt = dev;
int len = 0;
len += sprintf (buf + len, "Subsystem PUN: %d\n", subsystem_pun);
- len += sprintf (buf + len, "I/O base address: 0x%x\n", shpnt->io_port);
+ len += sprintf (buf + len, "I/O base address: 0x%x\n", IM_CMD_REG);
return len;
}
/*--------------------------------------------------------------------*/
-static void
-check_devices(struct Scsi_Host *shpnt)
-{
- int is_disk, block_length;
- int ldn;
- int num_ldn = 0;
-
- /* check ldn's from 0 to MAX_LOG_DEV to find which devices exist */
- for (ldn = 0; ldn < MAX_LOG_DEV; ldn++)
- {
- if (device_exists(shpnt, ldn, &is_disk, &block_length))
- {
- printk("IBM MCA SCSI: logical device found at ldn=%d.\n", ldn);
- ld[ldn].is_disk = is_disk;
- ld[ldn].block_length = block_length;
- get_ldn[num_ldn / 8][num_ldn % 8] = ldn;
- num_ldn++;
- }
- }
-
- return;
-}
-
-/*--------------------------------------------------------------------*/
-
-static int
-device_exists(struct Scsi_Host *shpnt, int ldn, int *is_disk,
- int *block_length)
-{
+/* SCSI-SCB-command for device_inquiry */
+static int device_inquiry(struct Scsi_Host *shpnt, int ldn, unsigned char *buf)
+{
struct im_scb scb;
struct im_tsb tsb;
- unsigned char buf[256];
int retries;
for (retries = 0; retries < 3; retries++)
@@ -592,7 +958,6 @@ device_exists(struct Scsi_Host *shpnt, int ldn, int *is_disk,
/*fill scb with inquiry command */
scb.command = IM_DEVICE_INQUIRY_CMD;
scb.enable = IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT;
- /* I think this virt_to_bus is needed.. ??? AC */
scb.sys_buf_adr = virt_to_bus(buf);
scb.sys_buf_length = 255;
scb.tsb_adr = virt_to_bus(&tsb);
@@ -611,19 +976,340 @@ device_exists(struct Scsi_Host *shpnt, int ldn, int *is_disk,
/*if all three retries failed, return "no device at this ldn" */
if (retries >= 3)
return 0;
+ else
+ return 1;
+}
+
+/* SCSI-immediate-command for assign. This functions maps/unmaps specific
+ ldn-numbers on SCSI (PUN,LUN). It is needed for presetting of the
+ subsystem and for dynamical remapping od ldns. */
+static int immediate_assign(struct Scsi_Host *shpnt, unsigned int pun,
+ unsigned int lun, unsigned int ldn,
+ unsigned int operation)
+{
+ int retries;
+ unsigned long imm_command;
+
+ for (retries=0; retries<3; retries ++)
+ {
+ imm_command = inl(IM_CMD_REG);
+ imm_command &= (unsigned long)(0xF8000000); /* keep reserved bits */
+ imm_command |= (unsigned long)(IM_ASSIGN_IMM_CMD);
+ imm_command |= (unsigned long)((lun & 7) << 24);
+ imm_command |= (unsigned long)((operation & 1) << 23);
+ imm_command |= (unsigned long)((pun & 7) << 20);
+ imm_command |= (unsigned long)((ldn & 15) << 16);
+
+ got_interrupt = 0;
+ issue_cmd (shpnt, (unsigned long)(imm_command), IM_IMM_CMD | 0xf);
+ while (!got_interrupt)
+ barrier ();
+
+ /*if command succesful, break */
+ if (stat_result == IM_IMMEDIATE_CMD_COMPLETED)
+ break;
+ }
+
+ if (retries >= 3)
+ return 0;
+ else
+ return 1;
+}
+
+/* type-interpreter for physical device numbers */
+static char *ti_p(int value)
+{
+ switch (value)
+ {
+ case TYPE_IBM_SCSI_ADAPTER: return("A"); break;
+ case TYPE_DISK: return("D"); break;
+ case TYPE_TAPE: return("T"); break;
+ case TYPE_PROCESSOR: return("P"); break;
+ case TYPE_WORM: return("W"); break;
+ case TYPE_ROM: return("R"); break;
+ case TYPE_SCANNER: return("S"); break;
+ case TYPE_MOD: return("M"); break;
+ case TYPE_MEDIUM_CHANGER: return("C"); break;
+ case TYPE_NO_LUN: return("+"); break; /* show NO_LUN */
+ case TYPE_NO_DEVICE:
+ default: return("-"); break;
+ }
+ return("-");
+}
+
+/* type-interpreter for logical devices
+ (A bit stupid, but it was necessary to get the '-' and the Hex-codes
+ into one type.) */
+static char *ti_l(int value)
+{
+ switch (value)
+ {
+ case 0: return("0"); break; case 1: return("1"); break;
+ case 2: return("2"); break; case 3: return("3"); break;
+ case 4: return("4"); break; case 5: return("5"); break;
+ case 6: return("6"); break; case 7: return("7"); break;
+ case 8: return("8"); break; case 9: return("9"); break;
+ case 10: return("a"); break; case 11: return("b"); break;
+ case 12: return("c"); break; case 13: return("d"); break;
+ case 14: return("e"); break; case 15: return("f"); break;
+ default: return("-"); break;
+ }
+ return("-");
+}
+
+/*
+ The following routine probes the SCSI-devices in four steps:
+ 1. The current ldn -> pun,lun mapping is removed on the SCSI-adapter.
+ 2. ldn 0 is used to go through all possible combinations of pun,lun and
+ a device_inquiry is done to fiddle out whether there is a device
+ responding or not. This physical map is stored in get_scsi[][].
+ 3. The 15 available ldns (0-14) are mapped to existing pun,lun.
+ If there are more devices than ldns, it stops at 14 for the boot
+ time. Dynamical remapping will be done in ibmmca_queuecommand.
+ 4. If there are less than 15 valid pun,lun, the remaining ldns are
+ mapped to NON-existing pun,lun to satisfy the adapter. Information
+ about pun,lun -> ldn is stored as before in get_ldn[][].
+ This method leads to the result, that the SCSI-pun,lun shown to Linux
+ mid-level- and higher-level-drivers is exactly corresponding to the
+ physical reality on the SCSI-bus. Therefore, it is possible that users
+ of older releases of this driver have to rewrite their fstab-file, because
+ the /dev/sdXXX could have changed due to the right pun,lun report, now.
+ The assignment of ALL ldns avoids dynamical remapping by the adapter
+ itself.
+ */
+static void check_devices (struct Scsi_Host *shpnt)
+{
+ int id, lun, ldn;
+ unsigned char buf[256];
+ int count_devices = 0; /* local counter for connected device */
+
+ /* assign default values to certain variables */
+
+ IBM_DS.dyn_flag = 0; /* normally no need for dynamical ldn management */
+ next_ldn = 7; /* next ldn to be assigned is 7, because 0-6 is 'hardwired'*/
+ last_scsi_command = 0; /* emptify last SCSI-command storage */
+
+ /* initialize the very important driver-informational arrays/structs */
+ memset (ld, 0, sizeof ld);
+ memset (get_ldn, TYPE_NO_DEVICE, sizeof get_ldn); /* this is essential ! */
+ memset (get_scsi, TYPE_NO_DEVICE, sizeof get_scsi); /* this is essential ! */
+
+ for (lun=0; lun<=7; lun++) /* mark the adapter at its pun on all luns*/
+ {
+ get_scsi[subsystem_pun][lun] = TYPE_IBM_SCSI_ADAPTER;
+ get_ldn[subsystem_pun][lun] = MAX_LOG_DEV; /* make sure, the subsystem
+ ldn is active for all
+ luns. */
+ }
+
+ /* STEP 1: */
+ printk("IBM MCA SCSI: Removing current logical SCSI-device mapping.");
+ for (ldn=0; ldn<MAX_LOG_DEV; ldn++)
+ {
+#ifdef IM_DEBUG_PROBE
+ printk(".");
+#endif
+ immediate_assign(shpnt,0,0,ldn,REMOVE_LDN); /* remove ldn (wherever)*/
+ }
+
+ lun = 0;
+
+ /* STEP 2: */
+ printk("\nIBM MCA SCSI: Probing SCSI-devices.");
+ for (id=0; id<=7; id++)
+#ifdef CONFIG_SCSI_MULTI_LUN
+ for (lun=0; lun<=7; lun++)
+#endif
+ {
+#ifdef IM_DEBUG_PROBE
+ printk(".");
+#endif
+ if (id != subsystem_pun)
+ { /* if pun is not the adapter: */
+ immediate_assign(shpnt,id,lun,0,SET_LDN); /*set ldn=0 to pun,lun*/
+ if (device_inquiry(shpnt, 0, buf)) /* probe device */
+ {
+ get_scsi[id][lun]=(unsigned char)buf[0]; /* entry, even
+ for NO_LUN */
+ if (buf[0] != TYPE_NO_LUN)
+ count_devices++; /* a existing device is found */
+ }
+ immediate_assign(shpnt,id,lun,0,REMOVE_LDN); /* remove ldn */
+ }
+ }
+
+ /* STEP 3: */
+ printk("\nIBM MCA SCSI: Mapping SCSI-devices.");
+
+ ldn = 0;
+ lun = 0;
+
+#ifdef CONFIG_SCSI_MULTI_LUN
+ for (lun=0; lun<=7 && ldn<MAX_LOG_DEV; lun++)
+#endif
+ for (id=0; id<=7 && ldn<MAX_LOG_DEV; id++)
+ {
+#ifdef IM_DEBUG_PROBE
+ printk(".");
+#endif
+ if (id != subsystem_pun)
+ {
+ if (get_scsi[id][lun] != TYPE_NO_LUN &&
+ get_scsi[id][lun] != TYPE_NO_DEVICE)
+ {
+ /* Only map if accepted type. Always enter for
+ lun == 0 to get no gaps into ldn-mapping for ldn<7. */
+ immediate_assign(shpnt,id,lun,ldn,SET_LDN);
+ get_ldn[id][lun]=ldn; /* map ldn */
+ if (device_exists (shpnt, ldn, &ld[ldn].block_length,
+ &ld[ldn].device_type))
+ {
+#ifdef SCSI_IBMMCA_DEV_RESET
+ int ticks;
+ printk("(resetting)");
+ ticks = IM_RESET_DELAY*HZ;
+ reset_status = IM_RESET_IN_PROGRESS;
+ issue_cmd (shpnt, IM_RESET_IMM_CMD, IM_IMM_CMD | ldn);
+ while (reset_status == IM_RESET_IN_PROGRESS && --ticks)
+ {
+ udelay(1000000/HZ);
+ barrier();
+ }
+ /* if reset did not complete, just claim */
+ if (!ticks)
+ {
+ printk("IBM MCA SCSI: reset did not complete within %d seconds.\n",
+ IM_RESET_DELAY);
+ reset_status = IM_RESET_FINISHED_OK;
+ /* did not work, finish */
+ }
+#endif
+ ldn++;
+ }
+ else
+ {
+ /* device vanished, probably because we don't know how to
+ * handle it or because it has problems */
+ if (lun > 0)
+ {
+ /* remove mapping */
+ get_ldn[id][lun]=TYPE_NO_DEVICE;
+ immediate_assign(shpnt,0,0,ldn,REMOVE_LDN);
+ }
+ else ldn++;
+ }
+ }
+ else if (lun == 0)
+ {
+ /* map lun == 0, even if no device exists */
+ immediate_assign(shpnt,id,lun,ldn,SET_LDN);
+ get_ldn[id][lun]=ldn; /* map ldn */
+ ldn++;
+ }
+ }
+ }
+
+ /* STEP 4: */
+
+ /* map remaining ldns to non-existing devices */
+ for (lun=1; lun<=7 && ldn<MAX_LOG_DEV; lun++)
+ for (id=0; id<=7 && ldn<MAX_LOG_DEV; id++)
+ {
+ if (get_scsi[id][lun] == TYPE_NO_LUN ||
+ get_scsi[id][lun] == TYPE_NO_DEVICE)
+ {
+ /* Map remaining ldns only to NON-existing pun,lun
+ combinations to make sure an inquiry will fail.
+ For MULTI_LUN, it is needed to avoid adapter autonome
+ SCSI-remapping. */
+ immediate_assign(shpnt,id,lun,ldn,SET_LDN);
+ get_ldn[id][lun]=ldn;
+ ldn++;
+ }
+ }
+
+ printk("\n");
+
+#ifdef IM_DEBUG_PROBE
+ /* Show the physical and logical mapping during boot. */
+ printk("IBM MCA SCSI: Determined SCSI-device-mapping:\n");
+ printk(" Physical SCSI-Device Map Logical SCSI-Device Map\n");
+ printk("ID\\LUN 0 1 2 3 4 5 6 7 ID\\LUN 0 1 2 3 4 5 6 7\n");
+ for (id=0; id<=7; id++)
+ {
+ printk("%2d %2s %2s %2s %2s %2s %2s %2s %2s",
+ id, ti_p(get_scsi[id][0]), ti_p(get_scsi[id][1]),
+ ti_p(get_scsi[id][2]), ti_p(get_scsi[id][3]),
+ ti_p(get_scsi[id][4]), ti_p(get_scsi[id][5]),
+ ti_p(get_scsi[id][6]), ti_p(get_scsi[id][7]));
+ printk(" %2d %2s %2s %2s %2s %2s %2s %2s %2s\n",
+ id, ti_l(get_ldn[id][0]), ti_l(get_ldn[id][1]),
+ ti_l(get_ldn[id][2]), ti_l(get_ldn[id][3]),
+ ti_l(get_ldn[id][4]), ti_l(get_ldn[id][5]),
+ ti_l(get_ldn[id][6]), ti_l(get_ldn[id][7]));
+ }
+#endif
+
+ /* assign total number of found SCSI-devices to the statistics struct */
+ IBM_DS.total_scsi_devices = count_devices;
+
+ /* decide for output in /proc-filesystem, if the configuration of
+ SCSI-devices makes dynamical reassignment of devices necessary */
+ if (count_devices>=MAX_LOG_DEV)
+ IBM_DS.dyn_flag = 1; /* dynamical assignment is necessary */
+ else
+ IBM_DS.dyn_flag = 0; /* dynamical assignment is not necessary */
+
+ /* If no SCSI-devices are assigned, return 1 in order to cause message. */
+ if (ldn == 0)
+ printk("IBM MCA SCSI: Warning: No SCSI-devices found/assignable!\n");
+
+ /* reset the counters for statistics on the current adapter */
+ IBM_DS.total_accesses = 0;
+ IBM_DS.total_interrupts = 0;
+ IBM_DS.dynamical_assignments = 0;
+ memset (IBM_DS.ldn_access, 0x0, sizeof (IBM_DS.ldn_access));
+ memset (IBM_DS.ldn_read_access, 0x0, sizeof (IBM_DS.ldn_read_access));
+ memset (IBM_DS.ldn_write_access, 0x0, sizeof (IBM_DS.ldn_write_access));
+ memset (IBM_DS.ldn_assignments, 0x0, sizeof (IBM_DS.ldn_assignments));
+
+ return;
+}
+
+/*--------------------------------------------------------------------*/
+
+static int
+device_exists (struct Scsi_Host *shpnt, int ldn, int *block_length,
+ int *device_type)
+{
+ struct im_scb scb;
+ struct im_tsb tsb;
+ unsigned char buf[256];
+ int retries;
+ /* if no valid device found, return immediately with 0 */
+ if (!(device_inquiry(shpnt, ldn, buf))) return 0;
+
/*if device is CD_ROM, assume block size 2048 and return */
if (buf[0] == TYPE_ROM)
{
- *is_disk = 0;
+ *device_type = TYPE_ROM;
+ *block_length = 2048; /* (standard blocksize for yellow-/red-book) */
+ return 1;
+ }
+
+ if (buf[0] == TYPE_WORM) /* CD-burner, WORM, Linux handles this as CD-ROM
+ therefore, the block_length is also 2048. */
+ {
+ *device_type = TYPE_WORM;
*block_length = 2048;
return 1;
}
-
- /*if device is disk, use "read capacity" to find its block size */
+
+ /* if device is disk, use "read capacity" to find its block size */
if (buf[0] == TYPE_DISK)
{
- *is_disk = 1;
+ *device_type = TYPE_DISK;
for (retries = 0; retries < 3; retries++)
{
@@ -653,7 +1339,70 @@ device_exists(struct Scsi_Host *shpnt, int ldn, int *is_disk,
return 0;
}
- /*for now, ignore tape and other devices - return 0 */
+ /* if this is a magneto-optical drive, treat it like a harddisk */
+ if (buf[0] == TYPE_MOD)
+ {
+ *device_type = TYPE_MOD;
+
+ for (retries = 0; retries < 3; retries++)
+ {
+ /*fill scb with read capacity command */
+ scb.command = IM_READ_CAPACITY_CMD;
+ scb.enable = IM_READ_CONTROL;
+ scb.sys_buf_adr = virt_to_bus(buf);
+ scb.sys_buf_length = 8;
+ scb.tsb_adr = virt_to_bus(&tsb);
+
+ /*issue scb to passed ldn, and busy wait for interrupt */
+ got_interrupt = 0;
+ issue_cmd (shpnt, virt_to_bus(&scb), IM_SCB | ldn);
+ while (!got_interrupt)
+ barrier ();
+
+ /*if got capacity, get block length and return one device found */
+ if (stat_result == IM_SCB_CMD_COMPLETED)
+ {
+ *block_length = buf[7] + (buf[6] << 8) + (buf[5] << 16) + (buf[4] << 24);
+ return 1;
+ }
+ }
+
+ /*if all three retries failed, return "no device at this ldn" */
+ if (retries >= 3)
+ return 0;
+ }
+
+ if (buf[0] == TYPE_TAPE) /* TAPE-device found */
+ {
+ *device_type = TYPE_TAPE;
+ *block_length = 0; /* not in use (setting by mt and mtst in op.) */
+ return 1;
+ }
+
+ if (buf[0] == TYPE_PROCESSOR) /* HP-Scanners, diverse SCSI-processing units*/
+ {
+ *device_type = TYPE_PROCESSOR;
+ *block_length = 0; /* they set their stuff on drivers */
+ return 1;
+ }
+
+ if (buf[0] == TYPE_SCANNER) /* other SCSI-scanners */
+ {
+ *device_type = TYPE_SCANNER;
+ *block_length = 0; /* they set their stuff on drivers */
+ return 1;
+ }
+
+ if (buf[0] == TYPE_MEDIUM_CHANGER) /* Medium-Changer */
+ {
+ *device_type = TYPE_MEDIUM_CHANGER;
+ *block_length = 0; /* One never knows, what to expect on a medium
+ changer device. */
+ return 1;
+ }
+
+ /* Up to now, no SCSI-devices that are known up to kernel 2.1.31 are
+ ignored! MO-drives are now supported and treated as harddisk. */
return 0;
}
@@ -664,19 +1413,17 @@ device_exists(struct Scsi_Host *shpnt, int ldn, int *is_disk,
void
ibmmca_scsi_setup (char *str, int *ints)
{
- int i;
+ int i;
- for (i = 0; i < IM_MAX_HOSTS && i < ints[0]; i++)
- {
+ for (i = 0; i < IM_MAX_HOSTS && i < ints[0]; i++)
io_port[i] = ints[i+1];
- }
}
#endif
/*--------------------------------------------------------------------*/
-int
+int
ibmmca_detect (Scsi_Host_Template * template)
{
struct Scsi_Host *shpnt;
@@ -697,11 +1444,11 @@ ibmmca_detect (Scsi_Host_Template * template)
/* if ibmmcascsi setup option was passed to kernel, return "found" */
for (i = 0; i < IM_MAX_HOSTS; i++)
if (io_port[i] > 0 && scsi_id[i] >= 0 && scsi_id[i] < 8)
- {
+ {
printk("IBM MCA SCSI: forced detection, io=0x%x, scsi id=%d.\n",
- io_port[i], scsi_id[i]);
+ io_port[i], scsi_id[i]);
ibmmca_register(template, io_port[i], scsi_id[i]);
- }
+ }
if (found) return found;
/* first look for the SCSI integrated on the motherboard */
@@ -712,13 +1459,13 @@ ibmmca_detect (Scsi_Host_Template * template)
port = IM_IO_PORT + ((pos2 & 0x0e) << 2);
id = (pos3 & 0xe0) >> 5;
printk("IBM MCA SCSI: integrated SCSI found, io=0x%x, scsi id=%d.\n",
- port, id);
+ port, id);
if ((shpnt = ibmmca_register(template, port, id)))
- {
- mca_set_adapter_name(MCA_INTEGSCSI, "PS/2 Integrated SCSI");
- mca_set_adapter_procfn(MCA_INTEGSCSI, (MCA_ProcFn) ibmmca_getinfo,
- shpnt);
- }
+ {
+ mca_set_adapter_name(MCA_INTEGSCSI, "PS/2 Integrated SCSI");
+ mca_set_adapter_procfn(MCA_INTEGSCSI, (MCA_ProcFn) ibmmca_getinfo,
+ shpnt);
+ }
}
/* now look for other adapters */
@@ -727,22 +1474,22 @@ ibmmca_detect (Scsi_Host_Template * template)
{
slot = 0;
while ((slot = mca_find_adapter(subsys_list[i].mca_id, slot))
- != MCA_NOTFOUND)
- {
- pos2 = mca_read_stored_pos(slot, 2);
- pos3 = mca_read_stored_pos(slot, 3);
- port = IM_IO_PORT + ((pos2 & 0x0e) << 2);
- id = (pos3 & 0xe0) >> 5;
- printk ("IBM MCA SCSI: %s found in slot %d, io=0x%x, scsi id=%d.\n",
- subsys_list[i].description, slot + 1, port, id);
- if ((shpnt = ibmmca_register(template, port, id)))
- {
- mca_set_adapter_name (slot, subsys_list[i].description);
- mca_set_adapter_procfn (slot, (MCA_ProcFn) ibmmca_getinfo,
- shpnt);
- }
- slot++;
- }
+ != MCA_NOTFOUND)
+ {
+ pos2 = mca_read_stored_pos(slot, 2);
+ pos3 = mca_read_stored_pos(slot, 3);
+ port = IM_IO_PORT + ((pos2 & 0x0e) << 2);
+ id = (pos3 & 0xe0) >> 5;
+ printk ("IBM MCA SCSI: %s found in slot %d, io=0x%x, scsi id=%d.\n",
+ subsys_list[i].description, slot + 1, port, id);
+ if ((shpnt = ibmmca_register(template, port, id)))
+ {
+ mca_set_adapter_name (slot, subsys_list[i].description);
+ mca_set_adapter_procfn (slot, (MCA_ProcFn) ibmmca_getinfo,
+ shpnt);
+ }
+ slot++;
+ }
}
if (!found) {
@@ -753,8 +1500,6 @@ ibmmca_detect (Scsi_Host_Template * template)
return found;
}
-/*--------------------------------------------------------------------*/
-
static struct Scsi_Host *
ibmmca_register(Scsi_Host_Template * template, int port, int id)
{
@@ -765,7 +1510,7 @@ ibmmca_register(Scsi_Host_Template * template, int port, int id)
if (check_region(port, IM_N_IO_PORT))
{
printk("IBM MCA SCSI: Unable to get I/O region 0x%x-0x%x.\n",
- port, port + IM_N_IO_PORT);
+ port, port + IM_N_IO_PORT);
return NULL;
}
@@ -785,6 +1530,9 @@ ibmmca_register(Scsi_Host_Template * template, int port, int id)
shpnt->io_port = port;
shpnt->n_io_port = IM_N_IO_PORT;
shpnt->this_id = id;
+
+ reset_status = IM_RESET_NOT_IN_PROGRESS;
+
for (i = 0; i < 8; i++)
for (j = 0; j < 8; j++)
get_ldn[i][j] = MAX_LOG_DEV;
@@ -800,17 +1548,6 @@ ibmmca_register(Scsi_Host_Template * template, int port, int id)
/*--------------------------------------------------------------------*/
-int
-ibmmca_release(struct Scsi_Host *shpnt)
-{
- release_region(shpnt->io_port, shpnt->n_io_port);
- if (!(--found))
- free_irq(shpnt->irq, hosts);
- return 0;
-}
-
-/*--------------------------------------------------------------------*/
-
int
ibmmca_command (Scsi_Cmnd * cmd)
{
@@ -823,21 +1560,134 @@ ibmmca_command (Scsi_Cmnd * cmd)
/*--------------------------------------------------------------------*/
-int
-ibmmca_queuecommand (Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
+int
+ibmmca_release(struct Scsi_Host *shpnt)
+{
+ release_region(shpnt->io_port, shpnt->n_io_port);
+ if (!(--found))
+ free_irq(shpnt->irq, hosts);
+ return 0;
+}
+
+/*--------------------------------------------------------------------*/
+
+/* The following routine is the SCSI command queue. The old edition is
+ now improved by dynamical reassignment of ldn numbers that are
+ currently not assigned. The mechanism works in a way, that first
+ the physical structure is checked. If at a certain pun,lun a device
+ should be present, the routine proceeds to the ldn check from
+ get_ldn. An answer of 0xff would show-up, that the aimed device is
+ currently not assigned any ldn. At this point, the dynamical
+ remapping algorithm is called. It works in a way, that it goes in
+ cyclic order through the ldns from 7 to 14. If a ldn is assigned,
+ it takes 8 dynamical reassignment calls, until a device looses its
+ ldn again. With this method it is assured, that while doing
+ intense I/O between up to eight devices, no dynamical remapping is
+ done there. ldns 0 through 6(!) are left untouched, which means, that
+ puns 0 through 7(!) on lun=0 are always accessible without remapping.
+ These ldns are statically assigned by this driver. The subsystem always
+ occupies at least one pun, therefore 7 ldns (at lun=0) for other devices
+ are sufficient. (The adapter uses always ldn=15, at whatever pun it is.) */
+int ibmmca_queuecommand (Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
{
unsigned int ldn;
unsigned int scsi_cmd;
struct im_scb *scb;
struct Scsi_Host *shpnt = cmd->host;
-
- /*if (target,lun) unassigned, return error */
+
+ int current_ldn;
+ int id,lun;
+
+ /*if (target,lun) is NO LUN or not existing at all, return error */
+ if ((get_scsi[cmd->target][cmd->lun] == TYPE_NO_LUN)||
+ (get_scsi[cmd->target][cmd->lun] == TYPE_NO_DEVICE))
+ {
+ cmd->result = DID_NO_CONNECT << 16;
+ done (cmd);
+ return 0;
+ }
+
+ /*if (target,lun) unassigned, do further checks... */
ldn = get_ldn[cmd->target][cmd->lun];
- if (ldn >= MAX_LOG_DEV)
+ if (ldn >= MAX_LOG_DEV) /* on invalid ldn do special stuff */
{
- cmd->result = DID_NO_CONNECT << 16;
- done (cmd);
- return 0;
+ if (ldn > MAX_LOG_DEV) /* dynamical remapping if ldn unassigned */
+ {
+ current_ldn = next_ldn; /* stop-value for one circle */
+ while (ld[next_ldn].cmd) /* search for a occupied, but not in */
+ { /* command-processing ldn. */
+ next_ldn ++;
+ if (next_ldn>=MAX_LOG_DEV)
+ next_ldn = 7;
+ if (current_ldn == next_ldn) /* One circle done ? */
+ { /* no non-processing ldn found */
+ printk("IBM MCA SCSI: Cannot assign SCSI-device dynamically!\n");
+ printk(" On ldn 7-14 SCSI-commands everywhere in progress.\n");
+ printk(" Reporting DID_NO_CONNECT for device (%d,%d).\n",
+ cmd->target, cmd->lun);
+ cmd->result = DID_NO_CONNECT << 16;/* return no connect*/
+ done (cmd);
+ return 0;
+ }
+ }
+
+ /* unmap non-processing ldn */
+ for (id=0; id<=7; id ++)
+ for (lun=0; lun<=7; lun++)
+ {
+ if (get_ldn[id][lun] == next_ldn)
+ {
+ get_ldn[id][lun] = TYPE_NO_DEVICE; /* unmap entry */
+ goto DYN_ASSIGN; /* jump out as fast as possible */
+ }
+ }
+
+DYN_ASSIGN:
+ /* unassign found ldn (pun,lun does not matter for remove) */
+ immediate_assign(shpnt,0,0,next_ldn,REMOVE_LDN);
+ /* assign found ldn to aimed pun,lun */
+ immediate_assign(shpnt,cmd->target,cmd->lun,next_ldn,SET_LDN);
+ /* map found ldn to pun,lun */
+ get_ldn[cmd->target][cmd->lun] = next_ldn;
+ /* change ldn to the right value, that is now next_ldn */
+ ldn = next_ldn;
+ /* set reduced interrupt_handler-mode for checking */
+ local_checking_phase_flag = 1;
+ /* get device information for ld[ldn] */
+ if (device_exists (shpnt, ldn, &ld[ldn].block_length,
+ &ld[ldn].device_type))
+ {
+ ld[ldn].cmd = 0; /* To prevent panic set 0, because
+ devices that were not assigned,
+ should have nothing in progress. */
+
+ /* increase assignment counters for statistics in /proc */
+ IBM_DS.dynamical_assignments++;
+ IBM_DS.ldn_assignments[ldn]++;
+ }
+ else
+ /* panic here, because a device, found at boottime has
+ vanished */
+ panic("IBM MCA SCSI: ldn=0x%x, SCSI-device on (%d,%d) vanished!\n",
+ ldn, cmd->target, cmd->lun);
+
+ /* set back to normal interrupt_handling */
+ local_checking_phase_flag = 0;
+
+ /* Information on syslog terminal */
+ printk("IBM MCA SCSI: ldn=0x%x dynamically reassigned to (%d,%d).\n",
+ ldn, cmd->target, cmd->lun);
+
+ /* increase next_ldn for next dynamical assignment */
+ next_ldn ++;
+ if (next_ldn>=MAX_LOG_DEV) next_ldn = 7;
+ }
+ else
+ { /* wall against Linux accesses to the subsystem adapter */
+ cmd->result = DID_NO_CONNECT << 16;
+ done (cmd);
+ return 0;
+ }
}
/*verify there is no command already in progress for this log dev */
@@ -875,71 +1725,149 @@ ibmmca_queuecommand (Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
/*fill scb information dependent on scsi command */
scsi_cmd = cmd->cmnd[0];
+
#ifdef IM_DEBUG_CMD
printk("issue scsi cmd=%02x to ldn=%d\n", scsi_cmd, ldn);
#endif
+
+ /* for specific device debugging: */
+#ifdef IM_DEBUG_CMD_SPEC_DEV
+ if (ld[ldn].device_type==IM_DEBUG_CMD_DEVICE)
+ printk("(SCSI-device-type=0x%x) issue scsi cmd=%02x to ldn=%d\n",
+ ld[ldn].device_type, scsi_cmd, ldn);
+#endif
+
+ /* for possible panics store current command */
+ last_scsi_command = scsi_cmd;
+
+ /* update statistical info */
+ IBM_DS.total_accesses++;
+ IBM_DS.ldn_access[ldn]++;
+
switch (scsi_cmd)
{
case READ_6:
case WRITE_6:
case READ_10:
case WRITE_10:
- if (scsi_cmd == READ_6 || scsi_cmd == READ_10)
- {
- scb->command = IM_READ_DATA_CMD;
- scb->enable |= IM_READ_CONTROL;
- }
- else
- {
- scb->command = IM_WRITE_DATA_CMD;
- }
- if (scsi_cmd == READ_6 || scsi_cmd == WRITE_6)
- {
- scb->u1.log_blk_adr = (((unsigned) cmd->cmnd[3]) << 0) |
- (((unsigned) cmd->cmnd[2]) << 8) |
- ((((unsigned) cmd->cmnd[1]) & 0x1f) << 16);
- scb->u2.blk.count = (unsigned) cmd->cmnd[4];
- }
- else
- {
- scb->u1.log_blk_adr = (((unsigned) cmd->cmnd[5]) << 0) |
- (((unsigned) cmd->cmnd[4]) << 8) |
- (((unsigned) cmd->cmnd[3]) << 16) |
- (((unsigned) cmd->cmnd[2]) << 24);
- scb->u2.blk.count = (((unsigned) cmd->cmnd[8]) << 0) |
- (((unsigned) cmd->cmnd[7]) << 8);
- }
- scb->u2.blk.length = ld[ldn].block_length;
- if (ld[ldn].is_disk)
- {
- if (++disk_rw_in_progress == 1)
- PS2_DISK_LED_ON ();
- }
+ case READ_12:
+ case WRITE_12:
+ /* statistics for proc_info */
+ if ((scsi_cmd == READ_6)||(scsi_cmd == READ_10)||(scsi_cmd == READ_12))
+ IBM_DS.ldn_read_access[ldn]++; /* increase READ-access on ldn stat. */
+ else if ((scsi_cmd == WRITE_6)||(scsi_cmd == WRITE_10)||
+ (scsi_cmd == WRITE_12))
+ IBM_DS.ldn_write_access[ldn]++; /* increase write-count on ldn stat.*/
+
+ /* Distinguish between disk and other devices. Only disks (that are the
+ most frequently accessed devices) should be supported by the
+ IBM-SCSI-Subsystem commands. */
+ switch (ld[ldn].device_type)
+ {
+ case TYPE_DISK: /* for harddisks enter here ... */
+ case TYPE_MOD: /* ... try it also for MO-drives (send flames as */
+ /* you like, if this won't work.) */
+ if (scsi_cmd == READ_6 || scsi_cmd == READ_10 ||
+ scsi_cmd == READ_12)
+ {
+ scb->command = IM_READ_DATA_CMD;
+ scb->enable |= IM_READ_CONTROL;
+ }
+ else
+ {
+ scb->command = IM_WRITE_DATA_CMD;
+ }
+ if (scsi_cmd == READ_6 || scsi_cmd == WRITE_6)
+ {
+ scb->u1.log_blk_adr = (((unsigned) cmd->cmnd[3]) << 0) |
+ (((unsigned) cmd->cmnd[2]) << 8) |
+ ((((unsigned) cmd->cmnd[1]) & 0x1f) << 16);
+ scb->u2.blk.count = (unsigned) cmd->cmnd[4];
+ }
+ else
+ {
+ scb->u1.log_blk_adr = (((unsigned) cmd->cmnd[5]) << 0) |
+ (((unsigned) cmd->cmnd[4]) << 8) |
+ (((unsigned) cmd->cmnd[3]) << 16) |
+ (((unsigned) cmd->cmnd[2]) << 24);
+ scb->u2.blk.count = (((unsigned) cmd->cmnd[8]) << 0) |
+ (((unsigned) cmd->cmnd[7]) << 8);
+ }
+ scb->u2.blk.length = ld[ldn].block_length;
+ if (++disk_rw_in_progress == 1)
+ PS2_DISK_LED_ON (shpnt->host_no, cmd->target);
+ break;
+
+ /* for other devices, enter here. Other types are not known by
+ Linux! TYPE_NO_LUN is forbidden as valid device. */
+ case TYPE_ROM:
+ case TYPE_TAPE:
+ case TYPE_PROCESSOR:
+ case TYPE_WORM:
+ case TYPE_SCANNER:
+ case TYPE_MEDIUM_CHANGER:
+
+ /* If there is a sequential-device, IBM recommends to use
+ IM_OTHER_SCSI_CMD_CMD instead of subsystem READ/WRITE.
+ Good/modern CD-ROM-drives are capable of
+ reading sequential AND random-access. This leads to the problem,
+ that random-accesses are covered by the subsystem, but
+ sequentials are not, as like for tape-drives. Therefore, it is
+ the easiest way to use IM_OTHER_SCSI_CMD_CMD for all read-ops
+ on CD-ROM-drives in order not to run into timing problems and
+ to have a stable state. In addition, data-access on CD-ROMs
+ works faster like that. Strange, but obvious. */
+
+ scb->command = IM_OTHER_SCSI_CMD_CMD;
+ if (scsi_cmd == READ_6 || scsi_cmd == READ_10 ||
+ scsi_cmd == READ_12) /* enable READ */
+ scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT;
+ else
+ scb->enable |= IM_SUPRESS_EXCEPTION_SHORT; /* assume WRITE */
+
+ scb->u1.scsi_cmd_length = cmd->cmd_len;
+ memcpy (scb->u2.scsi_command, cmd->cmnd, cmd->cmd_len);
+
+ /* Read/write on this non-disk devices is also displayworthy,
+ so flash-up the LED/display. */
+ if (++disk_rw_in_progress == 1)
+ PS2_DISK_LED_ON (shpnt->host_no, cmd->target);
+ break;
+ }
break;
-
case INQUIRY:
scb->command = IM_DEVICE_INQUIRY_CMD;
- scb->enable |= IM_READ_CONTROL |
- IM_SUPRESS_EXCEPTION_SHORT;
+ scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT;
break;
case READ_CAPACITY:
scb->command = IM_READ_CAPACITY_CMD;
scb->enable |= IM_READ_CONTROL;
- /*the length of system memory buffer must be exactly 8 bytes */
+ /* the length of system memory buffer must be exactly 8 bytes */
if (scb->sys_buf_length >= 8)
scb->sys_buf_length = 8;
break;
+ /* Commands that need read-only-mode (system <- device): */
case REQUEST_SENSE:
scb->command = IM_REQUEST_SENSE_CMD;
scb->enable |= IM_READ_CONTROL;
break;
-
+
+ /* Commands that need write-only-mode (system -> device): */
+ case MODE_SELECT:
+ case MODE_SELECT_10:
+ scb->command = IM_OTHER_SCSI_CMD_CMD;
+ scb->enable |= IM_SUPRESS_EXCEPTION_SHORT; /*Select needs WRITE-enabled*/
+ scb->u1.scsi_cmd_length = cmd->cmd_len;
+ memcpy (scb->u2.scsi_command, cmd->cmnd, cmd->cmd_len);
+ break;
+
+ /* For other commands, read-only is useful. Most other commands are
+ running without an input-data-block. */
default:
scb->command = IM_OTHER_SCSI_CMD_CMD;
- scb->enable |= IM_READ_CONTROL |
- IM_SUPRESS_EXCEPTION_SHORT;
+ scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT;
scb->u1.scsi_cmd_length = cmd->cmd_len;
memcpy (scb->u2.scsi_command, cmd->cmnd, cmd->cmd_len);
break;
@@ -958,10 +1886,10 @@ ibmmca_abort (Scsi_Cmnd * cmd)
/* The code below doesn't work right now, so we tell the upper layer
that we can't abort. This eventually causes a reset.
*/
- return SCSI_ABORT_SNOOZE;
+ return SCSI_ABORT_SNOOZE ;
#if 0
- struct Scsi_Host *shpnt = cmd->host;
+ struct Scsi_host *shpnt = cmd->host;
unsigned int ldn;
void (*saved_done) (Scsi_Cmnd *);
@@ -986,7 +1914,7 @@ ibmmca_abort (Scsi_Cmnd * cmd)
saved_done = cmd->scsi_done;
cmd->scsi_done = internal_done;
cmd->SCp.Status = 0;
- issue_cmd (shpnt, IM_ABORT_IMM_CMD, IM_IMM_CMD | ldn);
+ issue_cmd (shpnt, T_IMM_CMD, IM_IMM_CMD | ldn);
while (!cmd->SCp.Status)
barrier ();
@@ -1004,7 +1932,7 @@ ibmmca_abort (Scsi_Cmnd * cmd)
/*--------------------------------------------------------------------*/
-int
+int
ibmmca_reset (Scsi_Cmnd * cmd, unsigned int reset_flags)
{
struct Scsi_Host *shpnt = cmd->host;
@@ -1024,15 +1952,14 @@ ibmmca_reset (Scsi_Cmnd * cmd, unsigned int reset_flags)
udelay(1000000/HZ);
barrier();
}
-
/* if reset did not complete, just return an error*/
if (!ticks) {
printk("IBM MCA SCSI: reset did not complete within %d seconds.\n",
- IM_RESET_DELAY);
+ IM_RESET_DELAY);
reset_status = IM_RESET_FINISHED_FAIL;
return SCSI_RESET_ERROR;
}
-
+
/* if reset failed, just return an error */
if (reset_status == IM_RESET_FINISHED_FAIL) {
printk("IBM MCA SCSI: reset failed.\n");
@@ -1045,13 +1972,13 @@ ibmmca_reset (Scsi_Cmnd * cmd, unsigned int reset_flags)
int i;
for (i = 0; i < MAX_LOG_DEV; i++)
{
- Scsi_Cmnd *cmd = ld[i].cmd;
- if (cmd && cmd->scsi_done)
- {
- ld[i].cmd = 0;
- cmd->result = DID_RESET;
- (cmd->scsi_done) (cmd);
- }
+ Scsi_Cmnd *cmd = ld[i].cmd;
+ if (cmd && cmd->scsi_done)
+ {
+ ld[i].cmd = 0;
+ cmd->result = DID_RESET;
+ (cmd->scsi_done) (cmd);
+ }
}
}
return SCSI_RESET_SUCCESS;
@@ -1082,7 +2009,114 @@ ibmmca_biosparam (Disk * disk, kdev_t dev, int *info)
return 0;
}
-/*--------------------------------------------------------------------*/
+/* calculate percentage of total accesses on a ldn */
+static int ldn_access_load(struct Scsi_Host *shpnt, int ldn)
+{
+ if (IBM_DS.total_accesses == 0) return (0);
+ if (IBM_DS.ldn_access[ldn] == 0) return (0);
+ return((int)(((float)IBM_DS.ldn_access[ldn]/(float)IBM_DS.total_accesses)*(float)100.000));
+}
+
+/* calculate total amount of r/w-accesses */
+static int ldn_access_total_read_write(struct Scsi_Host *shpnt)
+{
+ int a = 0;
+ int i;
+
+ for (i=0; i<=MAX_LOG_DEV; i++)
+ a+=IBM_DS.ldn_read_access[i]+IBM_DS.ldn_write_access[i];
+ return(a);
+}
+
+/* routine to display info in the proc-fs-structure (a deluxe feature) */
+int ibmmca_proc_info (char *buffer, char **start, off_t offset, int length,
+ int hostno, int inout)
+{
+ int len=0;
+ int i,id;
+ struct Scsi_Host *shpnt;
+
+ for (i = 0; hosts[i] && hosts[i]->host_no != hostno; i++);
+ shpnt = hosts[i];
+ if (!shpnt) {
+ len += sprintf(buffer+len, "\nCan't find adapter for host number %d\n", hostno);
+ return len;
+ }
+
+ cli();
+
+ len += sprintf(buffer+len, "\n IBM-SCSI-Subsystem-Linux-Driver, Version %s\n\n\n",
+ IBMMCA_SCSI_DRIVER_VERSION);
+ len += sprintf(buffer+len, " SCSI Access-Statistics:\n");
+#ifdef CONFIG_SCSI_MULTI_LUN
+ len += sprintf(buffer+len, " Multiple LUN probing.....: Yes\n");
+#else
+ len += sprintf(buffer+len, " Multiple LUN probing.....: No\n");
+#endif
+ len += sprintf(buffer+len, " This Hostnumber..........: %d\n",
+ hostno);
+ len += sprintf(buffer+len, " Base I/O-Port............: 0x%x\n",
+ IM_CMD_REG);
+ len += sprintf(buffer+len, " (Shared) IRQ.............: %d\n",
+ IM_IRQ);
+ len += sprintf(buffer+len, " Total Interrupts.........: %d\n",
+ IBM_DS.total_interrupts);
+ len += sprintf(buffer+len, " Total SCSI Accesses......: %d\n",
+ IBM_DS.total_accesses);
+ len += sprintf(buffer+len, " Total SCSI READ/WRITE..: %d\n",
+ ldn_access_total_read_write(shpnt));
+ len += sprintf(buffer+len, " Total SCSI other cmds..: %d\n\n",
+ IBM_DS.total_accesses - ldn_access_total_read_write(shpnt));
+
+ len += sprintf(buffer+len, " Logical-Device-Number (LDN) Access-Statistics:\n");
+ len += sprintf(buffer+len, " LDN | Accesses [%%] | READ | WRITE | ASSIGNMENTS\n");
+ len += sprintf(buffer+len, " -----|--------------|-----------|-----------|--------------\n");
+ for (i=0; i<=MAX_LOG_DEV; i++)
+ len += sprintf(buffer+len, " %2X | %3d | %8d | %8d | %8d\n",
+ i, ldn_access_load(shpnt, i), IBM_DS.ldn_read_access[i],
+ IBM_DS.ldn_write_access[i], IBM_DS.ldn_assignments[i]);
+ len += sprintf(buffer+len, " -----------------------------------------------------------\n\n");
+
+ len += sprintf(buffer+len, " Dynamical-LDN-Assignment-Statistics:\n");
+ len += sprintf(buffer+len, " Number of physical SCSI-devices..: %d (+ Adapter)\n",
+ IBM_DS.total_scsi_devices);
+ len += sprintf(buffer+len, " Dynamical Assignment necessaray..: %s\n",
+ IBM_DS.dyn_flag ? "Yes" : "No ");
+ len += sprintf(buffer+len, " Next LDN to be assigned..........: 0x%x\n",
+ next_ldn);
+ len += sprintf(buffer+len, " Dynamical assignments done yet...: %d\n",
+ IBM_DS.dynamical_assignments);
+
+ len += sprintf(buffer+len, "\n Current SCSI-Device-Mapping:\n");
+ len += sprintf(buffer+len, " Physical SCSI-Device Map Logical SCSI-Device Map\n");
+ len += sprintf(buffer+len, " ID\\LUN 0 1 2 3 4 5 6 7 ID\\LUN 0 1 2 3 4 5 6 7\n");
+ for (id=0; id<=7; id++)
+ {
+ len += sprintf(buffer+len, " %2d %2s %2s %2s %2s %2s %2s %2s %2s",
+ id, ti_p(get_scsi[id][0]), ti_p(get_scsi[id][1]),
+ ti_p(get_scsi[id][2]), ti_p(get_scsi[id][3]),
+ ti_p(get_scsi[id][4]), ti_p(get_scsi[id][5]),
+ ti_p(get_scsi[id][6]), ti_p(get_scsi[id][7]));
+ len += sprintf(buffer+len, " %2d %2s %2s %2s %2s %2s %2s %2s %2s\n",
+ id, ti_l(get_ldn[id][0]), ti_l(get_ldn[id][1]),
+ ti_l(get_ldn[id][2]), ti_l(get_ldn[id][3]),
+ ti_l(get_ldn[id][4]), ti_l(get_ldn[id][5]),
+ ti_l(get_ldn[id][6]), ti_l(get_ldn[id][7]));
+ }
+
+ len += sprintf(buffer+len, "(A = IBM-Subsystem, D = Harddisk, T = Tapedrive, P = Processor, W = WORM,\n");
+ len += sprintf(buffer+len, " R = CD-ROM, S = Scanner, M = MO-Drive, C = Medium-Changer, + = unprovided LUN,\n");
+ len += sprintf(buffer+len, " - = nothing found)\n\n");
+
+ *start = buffer + offset;
+ len -= offset;
+ if (len > length)
+ len = length;
+
+ sti();
+
+ return len;
+}
#ifdef MODULE
/* Eventually this will go into an include file, but this will be later */
@@ -1091,3 +2125,4 @@ Scsi_Host_Template driver_template = IBMMCA;
#include "scsi_module.c"
#endif
+/*--------------------------------------------------------------------*/
diff --git a/drivers/scsi/ibmmca.h b/drivers/scsi/ibmmca.h
index 90b184829..4306f52ac 100644
--- a/drivers/scsi/ibmmca.h
+++ b/drivers/scsi/ibmmca.h
@@ -1,12 +1,12 @@
-
#ifndef _IBMMCA_H
#define _IBMMCA_H
-/*
+/*
* Low Level Driver for the IBM Microchannel SCSI Subsystem
*/
/*services provided to the higher level of Linux SCSI driver */
+int ibmmca_proc_info (char *, char **, off_t, int, int, int);
int ibmmca_detect (Scsi_Host_Template *);
int ibmmca_release (struct Scsi_Host *);
int ibmmca_command (Scsi_Cmnd *);
@@ -21,9 +21,9 @@ extern struct proc_dir_entry proc_scsi_ibmmca;
/*initialization for Scsi_host_template type */
#define IBMMCA { \
NULL, /*next*/ \
- NULL, /*module*/ \
+ NULL, /*usage_count*/ \
&proc_scsi_ibmmca, /*proc_dir*/ \
- NULL, /*proc info fn*/ \
+ ibmmca_proc_info, /*proc info fn*/ \
"IBMMCA", /*name*/ \
ibmmca_detect, /*detect fn*/ \
ibmmca_release, /*release fn*/ \
diff --git a/drivers/scsi/ncr53c8xx.c b/drivers/scsi/ncr53c8xx.c
index 9945ece31..2595f77f2 100644
--- a/drivers/scsi/ncr53c8xx.c
+++ b/drivers/scsi/ncr53c8xx.c
@@ -67,7 +67,7 @@
*/
/*
-** 23 August 1997, version 2.5a
+** 20 September 1997, version 2.5c
**
** Supported SCSI-II features:
** Synchronous negotiation
@@ -464,9 +464,9 @@ static int guess_xfer_direction(int opcode);
/*
** Head of list of NCR boards
**
-** Host is retrieved by its irq level.
-** If interrupts are shared, the internal host control block
-** address (struct ncb) is used as device id.
+** For kernel version < 1.3.70, host is retrieved by its irq level.
+** For later kernels, the internal host control block address
+** (struct ncb) is used as device id parameter of the irq stuff.
*/
static struct Scsi_Host *first_host = NULL;
@@ -535,6 +535,9 @@ static struct ncr_driver_setup
#ifdef SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT
static struct ncr_driver_setup
driver_safe_setup __initdata = SCSI_NCR_DRIVER_SAFE_SETUP;
+#ifdef MODULE
+char *ncr53c8xx = 0; /* command line passed by insmod */
+#endif
#endif
/*
@@ -4590,7 +4593,7 @@ printf(KERN_INFO "ncr53c%s-%d: rev=0x%02x, base=0x%x, io_port=0x%x, irq=%d\n",
SA_INTERRUPT|SA_SHIRQ, "ncr53c8xx", np)) {
#else
if (request_irq(device->slot.irq, ncr53c8xx_intr,
- SA_INTERRUPT, "ncr53c8xx", NULL)) {
+ SA_INTERRUPT, "ncr53c8xx", np)) {
#endif
#else
if (request_irq(device->slot.irq, ncr53c8xx_intr,
@@ -4747,7 +4750,9 @@ int ncr_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
/*---------------------------------------------------
**
- ** Assign a ccb / bind cmd
+ ** Assign a ccb / bind cmd.
+ ** If resetting, shorten settle_time if necessary
+ ** in order to avoid spurious timeouts.
** If resetting or no free ccb,
** insert cmd into the waiting list.
**
@@ -4755,6 +4760,11 @@ int ncr_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
*/
save_flags(flags); cli();
+ if (np->settle_time && cmd->timeout_per_command >= HZ &&
+ np->settle_time > jiffies + cmd->timeout_per_command - HZ) {
+ np->settle_time = jiffies + cmd->timeout_per_command - HZ;
+ }
+
if (np->settle_time || !(cp=ncr_get_ccb (np, cmd->target, cmd->lun))) {
insert_into_waiting_list(np, cmd);
restore_flags(flags);
@@ -5245,7 +5255,7 @@ int ncr_reset_bus (Scsi_Cmnd *cmd, int sync_reset)
* Commands will now be queued in the waiting list until a settle
* delay of 2 seconds will be completed.
*/
- ncr_start_reset(np, 2);
+ ncr_start_reset(np, driver_setup.settle_delay);
/*
* First, look in the wakeup list
*/
@@ -5393,7 +5403,7 @@ static int ncr_abort_command (Scsi_Cmnd *cmd)
*/
#ifdef MODULE
-static int ncr_detach(ncb_p np, int irq)
+static int ncr_detach(ncb_p np)
{
ccb_p cp;
tcb_p tp;
@@ -5432,16 +5442,12 @@ static int ncr_detach(ncb_p np, int irq)
*/
#ifdef DEBUG_NCR53C8XX
- printf("%s: freeing irq %d\n", ncr_name(np), irq);
+ printf("%s: freeing irq %d\n", ncr_name(np), np->irq);
#endif
#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,70)
-# ifdef SCSI_NCR_SHARE_IRQ
- free_irq(irq, np);
-# else
- free_irq(irq, NULL);
-# endif
+ free_irq(np->irq, np);
#else
- free_irq(irq);
+ free_irq(np->irq);
#endif
/*
@@ -6797,14 +6803,20 @@ static void ncr_log_hard_error(ncb_p np, u_short sist, u_char dstat)
script_base = (u_char *) np->script;
script_name = "script";
}
- else {
+ else if (np->p_scripth < dsp &&
+ dsp <= np->p_scripth + sizeof(struct scripth)) {
script_ofs = dsp - np->p_scripth;
script_size = sizeof(struct scripth);
script_base = (u_char *) np->scripth;
script_name = "scripth";
+ } else {
+ script_ofs = dsp;
+ script_size = 0;
+ script_base = 0;
+ script_name = "mem";
}
- printf ("%s:%d: ERROR (%x:%x) (%x-%x-%x) (%x/%x) @ %s (%x:%08x).\n",
+ printf ("%s:%d: ERROR (%x:%x) (%x-%x-%x) (%x/%x) @ (%s %x:%08x).\n",
ncr_name (np), (unsigned)INB (nc_ctest0)&0x0f, dstat, sist,
(unsigned)INB (nc_socl), (unsigned)INB (nc_sbcl), (unsigned)INB (nc_sbdl),
(unsigned)INB (nc_sxfer),(unsigned)INB (nc_scntl3), script_name, script_ofs,
@@ -6989,13 +7001,13 @@ void ncr_exception (ncb_p np)
if ((sist & (SGE)) ||
(dstat & (MDPE|BF|ABORT|IID))) {
- ncr_start_reset(np, 2);
+ ncr_start_reset(np, driver_setup.settle_delay);
return;
};
if (sist & HTH) {
printf ("%s: handshake timeout\n", ncr_name(np));
- ncr_start_reset(np, 2);
+ ncr_start_reset(np, driver_setup.settle_delay);
return;
};
@@ -7005,7 +7017,7 @@ void ncr_exception (ncb_p np)
OUTB (nc_scr1, HS_UNEXPECTED);
OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, cleanup));
};
- ncr_start_reset(np, 2);
+ ncr_start_reset(np, driver_setup.settle_delay);
return;
};
@@ -7096,7 +7108,7 @@ static int ncr_int_sbmc (ncb_p np)
ncr_name(np), np->scsi_mode, scsi_mode);
np->scsi_mode = scsi_mode;
- ncr_start_reset(np, 2);
+ ncr_start_reset(np, driver_setup.settle_delay);
return 1;
}
@@ -8925,7 +8937,11 @@ void ncr53c8xx_setup(char *str, int *ints)
else
printf("ncr53c8xx_setup: unexpected boot option '%.*s' ignored\n", (int)(pc-cur+1), cur);
+#ifdef MODULE
+ if ((cur = strchr(cur, ' ')) != NULL)
+#else
if ((cur = strchr(cur, ',')) != NULL)
+#endif
++cur;
}
#endif /* SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT */
@@ -9101,6 +9117,11 @@ int ncr53c8xx_detect(Scsi_Host_Template *tpnt)
# endif
#endif
+#if defined(SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT) && defined(MODULE)
+if (ncr53c8xx)
+ ncr53c8xx_setup(ncr53c8xx, (int *) 0);
+#endif
+
/*
** Detect all 53c8xx hosts and then attach them.
**
@@ -9270,6 +9291,28 @@ printk("ncr53c8xx_pci_init() #1: bus == %d, device_fn == %d\n", bus, device_fn);
return -1;
}
+#ifdef __powerpc__
+ /*
+ * Severall fix-up for power/pc.
+ * Should not be performed by the driver.
+ */
+ if ((command &
+ (PCI_COMMAND_MASTER|PCI_COMMAND_IO|PCI_COMMAND_MEMORY)) !=
+ (PCI_COMMAND_MASTER|PCI_COMMAND_IO|PCI_COMMAND_MEMORY)) {
+ printk("ncr53c8xx : setting PCI master/io/command bit\n");
+ command |= PCI_COMMAND_MASTER|PCI_COMMAND_IO|PCI_COMMAND_MEMORY;
+ pcibios_write_config_word(bus, device_fn, PCI_COMMAND, command);
+ }
+ if (io_port >= 0x10000000) {
+ io_port = (io_port & 0x00FFFFFF) | 0x01000000;
+ pcibios_write_config_dword(bus, device_fn, PCI_BASE_ADDRESS_0, io_port);
+ }
+ if (base >= 0x10000000) {
+ base = (base & 0x00FFFFFF) | 0x01000000;
+ pcibios_write_config_dword(bus, device_fn, PCI_BASE_ADDRESS_1, base);
+ }
+#endif
+
/*
* Check availability of IO space, memory space and master capability.
*/
@@ -9532,48 +9575,41 @@ printk("ncr53c8xx : command successfully queued\n");
}
/*
-** Linux entry point of the interrupt handler
+** Linux entry point of the interrupt handler.
+** Fort linux versions > 1.3.70, we trust the kernel for
+** passing the internal host descriptor as 'dev_id'.
+** Otherwise, we scan the host list and call the interrupt
+** routine for each host that uses this IRQ.
*/
#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,70)
static void ncr53c8xx_intr(int irq, void *dev_id, struct pt_regs * regs)
+{
+#ifdef DEBUG_NCR53C8XX
+ printk("ncr53c8xx : interrupt received\n");
+#endif
+
+ if (DEBUG_FLAGS & DEBUG_TINY) printf ("[");
+ ncr_exception((ncb_p) dev_id);
+ if (DEBUG_FLAGS & DEBUG_TINY) printf ("]\n");
+}
+
#else
static void ncr53c8xx_intr(int irq, struct pt_regs * regs)
-#endif
{
struct Scsi_Host *host;
struct host_data *host_data;
-#if 0
- u_long flags;
-
- save_flags(flags); cli();
-#endif
-
-printk("Yow, an NCR interrupt!\n");
-#ifdef DEBUG_NCR53C8XX
-printk("ncr53c8xx : interrupt received\n");
-#endif
for (host = first_host; host; host = host->next) {
- if (host->hostt == the_template && host->irq == irq) {
- host_data = (struct host_data *) host->hostdata;
-#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,70)
-# ifdef SCSI_NCR_SHARE_IRQ
- if (dev_id == host_data->ncb) {
-#else
- if (1) {
-# endif
-#endif
- if (DEBUG_FLAGS & DEBUG_TINY) printf ("[");
- ncr_exception(host_data->ncb);
- if (DEBUG_FLAGS & DEBUG_TINY) printf ("]\n");
- }
- }
+ if (host->hostt == the_template && host->irq == irq) {
+ host_data = (struct host_data *) host->hostdata;
+ if (DEBUG_FLAGS & DEBUG_TINY) printf ("[");
+ ncr_exception(host_data->ncb);
+ if (DEBUG_FLAGS & DEBUG_TINY) printf ("]\n");
+ }
}
-#if 0
- restore_flags(flags);
-#endif
}
+#endif
/*
** Linux entry point of the timer handler
@@ -9679,17 +9715,10 @@ int ncr53c8xx_abort(Scsi_Cmnd *cmd)
#ifdef MODULE
int ncr53c8xx_release(struct Scsi_Host *host)
{
- struct host_data *host_data;
#ifdef DEBUG_NCR53C8XX
printk("ncr53c8xx : release\n");
#endif
-
- for (host = first_host; host; host = host->next) {
- if (host->hostt == the_template) {
- host_data = (struct host_data *) host->hostdata;
- ncr_detach(host_data->ncb, host->irq);
- }
- }
+ ncr_detach(((struct host_data *) host->hostdata)->ncb);
return 1;
}
diff --git a/drivers/scsi/ncr53c8xx.h b/drivers/scsi/ncr53c8xx.h
index b22565b11..0f8cb7876 100644
--- a/drivers/scsi/ncr53c8xx.h
+++ b/drivers/scsi/ncr53c8xx.h
@@ -45,7 +45,7 @@
/*
** Name and revision of the driver
*/
-#define SCSI_NCR_DRIVER_NAME "ncr53c8xx - revision 2.5a"
+#define SCSI_NCR_DRIVER_NAME "ncr53c8xx - revision 2.5c"
/*
** Check supported Linux versions
diff --git a/drivers/scsi/pci2000.c b/drivers/scsi/pci2000.c
new file mode 100644
index 000000000..0a27c904a
--- /dev/null
+++ b/drivers/scsi/pci2000.c
@@ -0,0 +1,660 @@
+/*+M*************************************************************************
+ * Perceptive Solutions, Inc. PCI-2000 device driver proc support for Linux.
+ *
+ * Copyright (c) 1997 Perceptive Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ * File Name: pci2000i.c
+ *
+ *-M*************************************************************************/
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/head.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+
+#include "pci2000.h"
+#include "psi_roy.h"
+
+#include<linux/stat.h>
+
+struct proc_dir_entry Proc_Scsi_Pci2000 =
+ { PROC_SCSI_PCI2000, 7, "pci2000", S_IFDIR | S_IRUGO | S_IXUGO, 2 };
+
+//#define DEBUG 1
+
+#ifdef DEBUG
+#define DEB(x) x
+#define STOP_HERE {int st;for(st=0;st<100;st++){st=1;}}
+#else
+#define DEB(x)
+#define STOP_HERE
+#endif
+
+typedef struct
+ {
+ ULONG address;
+ ULONG length;
+ } SCATGATH, *PSCATGATH;
+
+typedef struct
+ {
+ Scsi_Cmnd *SCpnt;
+ SCATGATH scatGath[16];
+ UCHAR tag;
+ } DEV2000, *PDEV2000;
+
+typedef struct
+ {
+ USHORT basePort;
+ USHORT mb0;
+ USHORT mb1;
+ USHORT mb2;
+ USHORT mb3;
+ USHORT mb4;
+ USHORT cmd;
+ USHORT tag;
+ DEV2000 dev[MAX_BUS][MAX_UNITS];
+ } ADAPTER2000, *PADAPTER2000;
+
+#define HOSTDATA(host) ((PADAPTER2000)&host->hostdata)
+
+
+static struct Scsi_Host *PsiHost[MAXADAPTER] = {NULL,}; // One for each adapter
+static int NumAdapters = 0;
+
+/****************************************************************
+ * Name: WaitReady :LOCAL
+ *
+ * Description: Wait for controller ready.
+ *
+ * Parameters: padapter - Pointer adapter data structure.
+ *
+ * Returns: TRUE on not ready.
+ *
+ ****************************************************************/
+static int WaitReady (PADAPTER2000 padapter)
+ {
+ ULONG timer;
+
+ timer = jiffies + TIMEOUT_COMMAND; // calculate the timeout value
+ do {
+ if ( !inb_p (padapter->cmd) )
+ return FALSE;
+ } while ( timer > jiffies ); // test for timeout
+ return TRUE;
+ }
+/****************************************************************
+ * Name: OpDone :LOCAL
+ *
+ * Description: Clean up operation and issue done to caller.
+ *
+ * Parameters: SCpnt - Pointer to SCSI command structure.
+ * status - Caller status.
+ *
+ * Returns: Nothing.
+ *
+ ****************************************************************/
+static void OpDone (Scsi_Cmnd *SCpnt, ULONG status)
+ {
+ SCpnt->result = status;
+ SCpnt->scsi_done (SCpnt);
+ }
+/****************************************************************
+ * Name: Command :LOCAL
+ *
+ * Description: Issue queued command to the PCI-2000.
+ *
+ * Parameters: padapter - Pointer to adapter information structure.
+ * cmd - PCI-2000 command byte.
+ *
+ * Returns: Non-zero command tag if operation is accepted.
+ *
+ ****************************************************************/
+static UCHAR Command (PADAPTER2000 padapter, UCHAR cmd)
+ {
+ outb_p (cmd, padapter->cmd);
+ if ( WaitReady (padapter) )
+ return 0;
+
+ if ( inw_p (padapter->mb0) )
+ return 0;
+
+ return inb_p (padapter->mb1);
+ }
+/****************************************************************
+ * Name: BuildSgList :LOCAL
+ *
+ * Description: Build the scatter gather list for controller.
+ *
+ * Parameters: SCpnt - Pointer to SCSI command structure.
+ * padapter - Pointer to adapter information structure.
+ * pdev - Pointer to adapter device structure.
+ *
+ * Returns: Non-zero in not scatter gather.
+ *
+ ****************************************************************/
+static int BuildSgList (Scsi_Cmnd *SCpnt, PADAPTER2000 padapter, PDEV2000 pdev)
+ {
+ int z;
+
+ if ( SCpnt->use_sg )
+ {
+ for ( z = 0; z < SCpnt->use_sg; z++ )
+ {
+ pdev->scatGath[z].address = virt_to_bus (((struct scatterlist *)SCpnt->request_buffer)[z].address);
+ pdev->scatGath[z].length = ((struct scatterlist *)SCpnt->request_buffer)[z].length;
+ }
+ outl (virt_to_bus (pdev->scatGath), padapter->mb2);
+ outl ((SCpnt->use_sg << 24) | SCpnt->request_bufflen, padapter->mb3);
+ return FALSE;
+ }
+ outl (virt_to_bus (SCpnt->request_buffer), padapter->mb2);
+ outl (SCpnt->request_bufflen, padapter->mb3);
+ return TRUE;
+ }
+/****************************************************************
+ * Name: Irq_Handler :LOCAL
+ *
+ * Description: Interrupt handler.
+ *
+ * Parameters: irq - Hardware IRQ number.
+ * dev_id -
+ * regs -
+ *
+ * Returns: TRUE if drive is not ready in time.
+ *
+ ****************************************************************/
+static void Irq_Handler (int irq, void *dev_id, struct pt_regs *regs)
+ {
+ struct Scsi_Host *shost = NULL; // Pointer to host data block
+ PADAPTER2000 padapter; // Pointer to adapter control structure
+ PDEV2000 pdev;
+ Scsi_Cmnd *SCpnt;
+ UCHAR tag = 0;
+ UCHAR tag0;
+ ULONG error;
+ int pun;
+ int bus;
+ int z;
+
+ DEB(printk ("\npci2000 recieved interrupt "));
+ for ( z = 0; z < NumAdapters; z++ ) // scan for interrupt to process
+ {
+ if ( PsiHost[z]->irq == (UCHAR)(irq & 0xFF) )
+ {
+ tag = inb_p (HOSTDATA(PsiHost[z])->tag);
+ if ( tag )
+ {
+ shost = PsiHost[z];
+ break;
+ }
+ }
+ }
+
+ if ( !shost )
+ {
+ DEB (printk ("\npci2000: not my interrupt"));
+ return;
+ }
+
+ padapter = HOSTDATA(shost);
+
+ tag0 = tag & 0x7F; // mask off the error bit
+ for ( bus = 0; bus < MAX_BUS; bus++ ) // scan the busses
+ {
+ for ( pun = 0; pun < MAX_UNITS; pun++ ) // scan the targets
+ {
+ pdev = &padapter->dev[bus][pun];
+ if ( !pdev->tag )
+ continue;
+ if ( pdev->tag == tag0 ) // is this it?
+ {
+ pdev->tag = 0;
+ SCpnt = pdev->SCpnt;
+ goto irqProceed;
+ }
+ }
+ }
+
+ outb_p (0xFF, padapter->tag); // clear the op interrupt
+ outb_p (CMD_DONE, padapter->cmd); // complete the op
+ return; // done, but, with what?
+
+irqProceed:;
+ if ( tag & ERR08_TAGGED ) // is there an error here?
+ {
+ if ( WaitReady (padapter) )
+ {
+ OpDone (SCpnt, DID_TIME_OUT << 16);
+ return;
+ }
+
+ outb_p (tag0, padapter->mb0); // get real error code
+ outb_p (CMD_ERROR, padapter->cmd);
+ if ( WaitReady (padapter) ) // wait for controller to suck up the op
+ {
+ OpDone (SCpnt, DID_TIME_OUT << 16);
+ return;
+ }
+
+ error = inl (padapter->mb0); // get error data
+ outb_p (0xFF, padapter->tag); // clear the op interrupt
+ outb_p (CMD_DONE, padapter->cmd); // complete the op
+
+ DEB (printk ("status: %lX ", error));
+ if ( error == 0x00020002 ) // is this error a check condition?
+ {
+ if ( bus ) // are we doint SCSI commands?
+ {
+ OpDone (SCpnt, (DID_OK << 16) | 2);
+ return;
+ }
+ if ( *SCpnt->cmnd == SCSIOP_TEST_UNIT_READY )
+ OpDone (SCpnt, (DRIVER_SENSE << 24) | (DID_OK << 16) | 2); // test caller we have sense data too
+ else
+ OpDone (SCpnt, DID_ERROR << 16);
+ return;
+ }
+ OpDone (SCpnt, DID_ERROR << 16);
+ return;
+ }
+
+ outb_p (0xFF, padapter->tag); // clear the op interrupt
+ outb_p (CMD_DONE, padapter->cmd); // complete the op
+ OpDone (SCpnt, DID_OK << 16);
+ }
+/****************************************************************
+ * Name: Pci2220i_QueueCommand
+ *
+ * Description: Process a queued command from the SCSI manager.
+ *
+ * Parameters: SCpnt - Pointer to SCSI command structure.
+ * done - Pointer to done function to call.
+ *
+ * Returns: Status code.
+ *
+ ****************************************************************/
+int Pci2000_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+ {
+ UCHAR *cdb = (UCHAR *)SCpnt->cmnd; // Pointer to SCSI CDB
+ PADAPTER2000 padapter = HOSTDATA(SCpnt->host); // Pointer to adapter control structure
+ int rc = -1; // command return code
+ UCHAR bus = SCpnt->channel;
+ UCHAR pun = SCpnt->target;
+ UCHAR lun = SCpnt->lun;
+ UCHAR cmd;
+ PDEV2000 pdev = &padapter->dev[bus][pun];
+
+ if ( !done )
+ {
+ printk("pci2000_queuecommand: %02X: done can't be NULL\n", *cdb);
+ return 0;
+ }
+
+ SCpnt->scsi_done = done;
+ pdev->SCpnt = SCpnt; // Save this command data
+
+ if ( WaitReady (padapter) )
+ {
+ rc = DID_ERROR;
+ goto finished;
+ }
+
+ outw_p (pun | (lun << 8), padapter->mb0);
+
+ if ( bus )
+ {
+ DEB (if(*cdb) printk ("\nCDB: %X- %X %X %X %X %X %X %X %X %X %X ", SCpnt->cmd_len, cdb[0], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5], cdb[6], cdb[7], cdb[8], cdb[9]));
+ DEB (if(*cdb) printk ("\ntimeout_per_command: %d, timeout_total: %d, timeout: %d, internal_timout: %d", SCpnt->timeout_per_command,
+ SCpnt->timeout_total, SCpnt->timeout, SCpnt->internal_timeout));
+ outl (SCpnt->timeout_per_command, padapter->mb1);
+ outb_p (CMD_SCSI_TIMEOUT, padapter->cmd);
+ if ( WaitReady (padapter) )
+ {
+ rc = DID_ERROR;
+ goto finished;
+ }
+
+ outw_p (pun | (lun << 8), padapter->mb0);
+ outw_p (SCpnt->cmd_len << 8, padapter->mb0 + 2);
+ outl (virt_to_bus (cdb), padapter->mb1);
+ if ( BuildSgList (SCpnt, padapter, pdev) )
+ cmd = CMD_SCSI_THRU;
+ else
+ cmd = CMD_SCSI_THRU_SG;
+ if ( (pdev->tag = Command (padapter, cmd)) == 0 )
+ rc = DID_TIME_OUT;
+ goto finished;
+ }
+ else
+ {
+ if ( lun )
+ {
+ rc = DID_BAD_TARGET;
+ goto finished;
+ }
+ }
+
+ switch ( *cdb )
+ {
+ case SCSIOP_INQUIRY: // inquiry CDB
+ {
+ if ( SCpnt->use_sg )
+ {
+ outl (virt_to_bus (((struct scatterlist *)(SCpnt->request_buffer))->address), padapter->mb2);
+ }
+ else
+ {
+ outl (virt_to_bus (SCpnt->request_buffer), padapter->mb2);
+ }
+ outl (SCpnt->request_bufflen, padapter->mb3);
+ cmd = CMD_DASD_SCSI_INQ;
+ break;
+ }
+
+ case SCSIOP_TEST_UNIT_READY: // test unit ready CDB
+ outl (virt_to_bus (SCpnt->sense_buffer), padapter->mb2);
+ outl (sizeof (SCpnt->sense_buffer), padapter->mb3);
+ cmd = CMD_TEST_READY;
+ break;
+
+ case SCSIOP_READ_CAPACITY: // read capctiy CDB
+ if ( SCpnt->use_sg )
+ {
+ outl (virt_to_bus (((struct scatterlist *)(SCpnt->request_buffer))->address), padapter->mb2);
+ }
+ else
+ {
+ outl (virt_to_bus (SCpnt->request_buffer), padapter->mb2);
+ }
+ outl (8, padapter->mb3);
+ cmd = CMD_DASD_CAP;
+ break;
+ case SCSIOP_VERIFY: // verify CDB
+ outw_p ((USHORT)cdb[8] | ((USHORT)cdb[7] << 8), padapter->mb0 + 2);
+ outl (XSCSI2LONG (&cdb[2]), padapter->mb1);
+ cmd = CMD_READ_SG;
+ break;
+ case SCSIOP_READ: // read10 CDB
+ outw_p ((USHORT)cdb[8] | ((USHORT)cdb[7] << 8), padapter->mb0 + 2);
+ outl (XSCSI2LONG (&cdb[2]), padapter->mb1);
+ if ( BuildSgList (SCpnt, padapter, pdev) )
+ cmd = CMD_READ;
+ else
+ cmd = CMD_READ_SG;
+ break;
+ case SCSIOP_READ6: // read6 CDB
+ outw_p (cdb[4], padapter->mb0 + 2);
+ outl ((SCSI2LONG (&cdb[1])) & 0x001FFFFF, padapter->mb1);
+ if ( BuildSgList (SCpnt, padapter, pdev) )
+ cmd = CMD_READ;
+ else
+ cmd = CMD_READ_SG;
+ break;
+ case SCSIOP_WRITE: // write10 CDB
+ outw_p ((USHORT)cdb[8] | ((USHORT)cdb[7] << 8), padapter->mb0 + 2);
+ outl (XSCSI2LONG (&cdb[2]), padapter->mb1);
+ if ( BuildSgList (SCpnt, padapter, pdev) )
+ cmd = CMD_WRITE;
+ else
+ cmd = CMD_WRITE_SG;
+ break;
+ case SCSIOP_WRITE6: // write6 CDB
+ outw_p (cdb[4], padapter->mb0 + 2);
+ outl ((SCSI2LONG (&cdb[1])) & 0x001FFFFF, padapter->mb1);
+ if ( BuildSgList (SCpnt, padapter, pdev) )
+ cmd = CMD_WRITE;
+ else
+ cmd = CMD_WRITE_SG;
+ break;
+ case SCSIOP_START_STOP_UNIT:
+ cmd = CMD_EJECT_MEDIA;
+ break;
+ case SCSIOP_MEDIUM_REMOVAL:
+ switch ( cdb[4] )
+ {
+ case 0:
+ cmd = CMD_UNLOCK_DOOR;
+ break;
+ case 1:
+ cmd = CMD_LOCK_DOOR;
+ break;
+ default:
+ cmd = 0;
+ break;
+ }
+ if ( cmd )
+ break;
+ default:
+ DEB (printk ("pci2220i_queuecommand: Unsupported command %02X\n", *cdb));
+ OpDone (SCpnt, DID_ERROR << 16);
+ return 0;
+ }
+
+ if ( (pdev->tag = Command (padapter, cmd)) == 0 )
+ rc = DID_TIME_OUT;
+finished:;
+ if ( rc != -1 )
+ OpDone (SCpnt, rc << 16);
+ return 0;
+ }
+/****************************************************************
+ * Name: internal_done :LOCAL
+ *
+ * Description: Done handler for non-queued commands
+ *
+ * Parameters: SCpnt - Pointer to SCSI command structure.
+ *
+ * Returns: Nothing.
+ *
+ ****************************************************************/
+static void internal_done (Scsi_Cmnd * SCpnt)
+ {
+ SCpnt->SCp.Status++;
+ }
+/****************************************************************
+ * Name: Pci2220i_Command
+ *
+ * Description: Process a command from the SCSI manager.
+ *
+ * Parameters: SCpnt - Pointer to SCSI command structure.
+ *
+ * Returns: Status code.
+ *
+ ****************************************************************/
+int Pci2000_Command (Scsi_Cmnd *SCpnt)
+ {
+ DEB(printk("pci2000_command: ..calling pci2000_queuecommand\n"));
+
+ Pci2000_QueueCommand (SCpnt, internal_done);
+
+ SCpnt->SCp.Status = 0;
+ while (!SCpnt->SCp.Status)
+ barrier ();
+ return SCpnt->result;
+ }
+/****************************************************************
+ * Name: Pci2220i_Detect
+ *
+ * Description: Detect and initialize our boards.
+ *
+ * Parameters: tpnt - Pointer to SCSI host template structure.
+ *
+ * Returns: Number of adapters found.
+ *
+ ****************************************************************/
+int Pci2000_Detect (Scsi_Host_Template *tpnt)
+ {
+ int pci_index = 0;
+ struct Scsi_Host *pshost;
+ PADAPTER2000 padapter;
+ int z;
+ int setirq;
+
+ if ( pcibios_present () )
+ {
+ for ( pci_index = 0; pci_index <= MAXADAPTER; ++pci_index )
+ {
+ UCHAR pci_bus, pci_device_fn;
+
+ if ( pcibios_find_device (VENDOR_PSI, DEVICE_ROY_1, pci_index, &pci_bus, &pci_device_fn) != 0 )
+ break;
+
+ pshost = scsi_register (tpnt, sizeof(ADAPTER2000));
+ padapter = HOSTDATA(pshost);
+
+ pcibios_read_config_word (pci_bus, pci_device_fn, PCI_BASE_ADDRESS_1, &padapter->basePort);
+ padapter->basePort &= 0xFFFE;
+ DEB (printk ("\nBase Regs = %#04X", padapter->basePort)); // get the base I/O port address
+ padapter->mb0 = padapter->basePort + RTR_MAILBOX; // get the 32 bit mail boxes
+ padapter->mb1 = padapter->basePort + RTR_MAILBOX + 4;
+ padapter->mb2 = padapter->basePort + RTR_MAILBOX + 8;
+ padapter->mb3 = padapter->basePort + RTR_MAILBOX + 12;
+ padapter->mb4 = padapter->basePort + RTR_MAILBOX + 16;
+ padapter->cmd = padapter->basePort + RTR_LOCAL_DOORBELL; // command register
+ padapter->tag = padapter->basePort + RTR_PCI_DOORBELL; // tag/response register
+
+ if ( WaitReady (padapter) )
+ goto unregister;
+ outb_p (0x84, padapter->mb0);
+ outb_p (CMD_SPECIFY, padapter->cmd);
+ if ( WaitReady (padapter) )
+ goto unregister;
+
+ pcibios_read_config_byte (pci_bus, pci_device_fn, PCI_INTERRUPT_LINE, &pshost->irq);
+ setirq = 1;
+ for ( z = 0; z < pci_index; z++ ) // scan for shared interrupts
+ {
+ if ( PsiHost[z]->irq == pshost->irq ) // if shared then, don't posses
+ setirq = 0;
+ }
+ if ( setirq ) // if not shared, posses
+ {
+ if ( request_irq (pshost->irq, Irq_Handler, 0, "pci2000", NULL) )
+ {
+ printk ("Unable to allocate IRQ for PSI-2000 controller.\n");
+ goto unregister;
+ }
+ }
+ PsiHost[pci_index] = pshost; // save SCSI_HOST pointer
+
+ pshost->unique_id = padapter->basePort;
+ pshost->max_id = 16;
+ pshost->max_channel = 1;
+
+ printk("\nPSI-2000 EIDE CONTROLLER: at I/O = %X IRQ = %d\n", padapter->basePort, pshost->irq);
+ printk("(C) 1997 Perceptive Solutions, Inc. All rights reserved\n\n");
+ continue;
+unregister:;
+ scsi_unregister (pshost);
+ }
+ }
+ NumAdapters = pci_index;
+ return pci_index;
+ }
+/****************************************************************
+ * Name: Pci2220i_Abort
+ *
+ * Description: Process the Abort command from the SCSI manager.
+ *
+ * Parameters: SCpnt - Pointer to SCSI command structure.
+ *
+ * Returns: Allways snooze.
+ *
+ ****************************************************************/
+int Pci2000_Abort (Scsi_Cmnd *SCpnt)
+ {
+ DEB (printk ("pci2000_abort\n"));
+ return SCSI_ABORT_SNOOZE;
+ }
+/****************************************************************
+ * Name: Pci2220i_Reset
+ *
+ * Description: Process the Reset command from the SCSI manager.
+ *
+ * Parameters: SCpnt - Pointer to SCSI command structure.
+ * flags - Flags about the reset command
+ *
+ * Returns: No active command at this time, so this means
+ * that each time we got some kind of response the
+ * last time through. Tell the mid-level code to
+ * request sense information in order to decide what
+ * to do next.
+ *
+ ****************************************************************/
+int Pci2000_Reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags)
+ {
+ return SCSI_RESET_PUNT;
+ }
+
+#include "sd.h"
+
+/****************************************************************
+ * Name: Pci2220i_BiosParam
+ *
+ * Description: Process the biosparam request from the SCSI manager to
+ * return C/H/S data.
+ *
+ * Parameters: disk - Pointer to SCSI disk structure.
+ * dev - Major/minor number from kernel.
+ * geom - Pointer to integer array to place geometry data.
+ *
+ * Returns: zero.
+ *
+ ****************************************************************/
+int Pci2000_BiosParam (Scsi_Disk *disk, kdev_t dev, int geom[])
+ {
+ PADAPTER2000 padapter;
+
+ padapter = HOSTDATA(disk->device->host);
+
+ if ( WaitReady (padapter) )
+ return 0;
+ outb_p (disk->device->id, padapter->mb0);
+ outb_p (CMD_GET_PARMS, padapter->cmd);
+ if ( WaitReady (padapter) )
+ return 0;
+
+ geom[0] = inb_p (padapter->mb2 + 3);
+ geom[1] = inb_p (padapter->mb2 + 2);
+ geom[2] = inw_p (padapter->mb2);
+ return 0;
+ }
+
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = PCI2220I;
+
+#include "scsi_module.c"
+#endif
+
diff --git a/drivers/scsi/pci2000.h b/drivers/scsi/pci2000.h
new file mode 100644
index 000000000..ded993958
--- /dev/null
+++ b/drivers/scsi/pci2000.h
@@ -0,0 +1,226 @@
+/*+M*************************************************************************
+ * Perceptive Solutions, Inc. PCI-2000 device driver proc support for Linux.
+ *
+ * Copyright (c) 1997 Perceptive Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ * File Name: pci2000.h
+ *
+ * Description: Header file for the SCSI driver for the PCI-2000
+ * interface card.
+ *
+ *-M*************************************************************************/
+#ifndef _PCI2000_H
+#define _PCI2000_H
+
+#include <linux/types.h>
+#include <linux/kdev_t.h>
+
+#ifndef PSI_EIDE_SCSIOP
+#define PSI_EIDE_SCSIOP 1
+
+/************************************************/
+/* definition of standard data types */
+/************************************************/
+#define CHAR char
+#define UCHAR unsigned char
+#define SHORT short
+#define USHORT unsigned short
+#define BOOL long
+#define LONG long
+#define ULONG unsigned long
+#define VOID void
+
+typedef CHAR *PCHAR;
+typedef UCHAR *PUCHAR;
+typedef SHORT *PSHORT;
+typedef USHORT *PUSHORT;
+typedef BOOL *PBOOL;
+typedef LONG *PLONG;
+typedef ULONG *PULONG;
+typedef VOID *PVOID;
+
+
+/************************************************/
+/* Misc. macros */
+/************************************************/
+#define ANY2SCSI(up, p) \
+((UCHAR *)up)[0] = (((ULONG)(p)) >> 8); \
+((UCHAR *)up)[1] = ((ULONG)(p));
+
+#define SCSI2LONG(up) \
+( (((long)*(((UCHAR *)up))) << 16) \
++ (((long)(((UCHAR *)up)[1])) << 8) \
++ ((long)(((UCHAR *)up)[2])) )
+
+#define XANY2SCSI(up, p) \
+((UCHAR *)up)[0] = ((long)(p)) >> 24; \
+((UCHAR *)up)[1] = ((long)(p)) >> 16; \
+((UCHAR *)up)[2] = ((long)(p)) >> 8; \
+((UCHAR *)up)[3] = ((long)(p));
+
+#define XSCSI2LONG(up) \
+( (((long)(((UCHAR *)up)[0])) << 24) \
++ (((long)(((UCHAR *)up)[1])) << 16) \
++ (((long)(((UCHAR *)up)[2])) << 8) \
++ ((long)(((UCHAR *)up)[3])) )
+
+/************************************************/
+/* SCSI CDB operation codes */
+/************************************************/
+#define SCSIOP_TEST_UNIT_READY 0x00
+#define SCSIOP_REZERO_UNIT 0x01
+#define SCSIOP_REWIND 0x01
+#define SCSIOP_REQUEST_BLOCK_ADDR 0x02
+#define SCSIOP_REQUEST_SENSE 0x03
+#define SCSIOP_FORMAT_UNIT 0x04
+#define SCSIOP_READ_BLOCK_LIMITS 0x05
+#define SCSIOP_REASSIGN_BLOCKS 0x07
+#define SCSIOP_READ6 0x08
+#define SCSIOP_RECEIVE 0x08
+#define SCSIOP_WRITE6 0x0A
+#define SCSIOP_PRINT 0x0A
+#define SCSIOP_SEND 0x0A
+#define SCSIOP_SEEK6 0x0B
+#define SCSIOP_TRACK_SELECT 0x0B
+#define SCSIOP_SLEW_PRINT 0x0B
+#define SCSIOP_SEEK_BLOCK 0x0C
+#define SCSIOP_PARTITION 0x0D
+#define SCSIOP_READ_REVERSE 0x0F
+#define SCSIOP_WRITE_FILEMARKS 0x10
+#define SCSIOP_FLUSH_BUFFER 0x10
+#define SCSIOP_SPACE 0x11
+#define SCSIOP_INQUIRY 0x12
+#define SCSIOP_VERIFY6 0x13
+#define SCSIOP_RECOVER_BUF_DATA 0x14
+#define SCSIOP_MODE_SELECT 0x15
+#define SCSIOP_RESERVE_UNIT 0x16
+#define SCSIOP_RELEASE_UNIT 0x17
+#define SCSIOP_COPY 0x18
+#define SCSIOP_ERASE 0x19
+#define SCSIOP_MODE_SENSE 0x1A
+#define SCSIOP_START_STOP_UNIT 0x1B
+#define SCSIOP_STOP_PRINT 0x1B
+#define SCSIOP_LOAD_UNLOAD 0x1B
+#define SCSIOP_RECEIVE_DIAGNOSTIC 0x1C
+#define SCSIOP_SEND_DIAGNOSTIC 0x1D
+#define SCSIOP_MEDIUM_REMOVAL 0x1E
+#define SCSIOP_READ_CAPACITY 0x25
+#define SCSIOP_READ 0x28
+#define SCSIOP_WRITE 0x2A
+#define SCSIOP_SEEK 0x2B
+#define SCSIOP_LOCATE 0x2B
+#define SCSIOP_WRITE_VERIFY 0x2E
+#define SCSIOP_VERIFY 0x2F
+#define SCSIOP_SEARCH_DATA_HIGH 0x30
+#define SCSIOP_SEARCH_DATA_EQUAL 0x31
+#define SCSIOP_SEARCH_DATA_LOW 0x32
+#define SCSIOP_SET_LIMITS 0x33
+#define SCSIOP_READ_POSITION 0x34
+#define SCSIOP_SYNCHRONIZE_CACHE 0x35
+#define SCSIOP_COMPARE 0x39
+#define SCSIOP_COPY_COMPARE 0x3A
+#define SCSIOP_WRITE_DATA_BUFF 0x3B
+#define SCSIOP_READ_DATA_BUFF 0x3C
+#define SCSIOP_CHANGE_DEFINITION 0x40
+#define SCSIOP_READ_SUB_CHANNEL 0x42
+#define SCSIOP_READ_TOC 0x43
+#define SCSIOP_READ_HEADER 0x44
+#define SCSIOP_PLAY_AUDIO 0x45
+#define SCSIOP_PLAY_AUDIO_MSF 0x47
+#define SCSIOP_PLAY_TRACK_INDEX 0x48
+#define SCSIOP_PLAY_TRACK_RELATIVE 0x49
+#define SCSIOP_PAUSE_RESUME 0x4B
+#define SCSIOP_LOG_SELECT 0x4C
+#define SCSIOP_LOG_SENSE 0x4D
+#define SCSIOP_MODE_SELECT10 0x55
+#define SCSIOP_MODE_SENSE10 0x5A
+#define SCSIOP_LOAD_UNLOAD_SLOT 0xA6
+#define SCSIOP_MECHANISM_STATUS 0xBD
+#define SCSIOP_READ_CD 0xBE
+
+// SCSI read capacity structure
+typedef struct _READ_CAPACITY_DATA
+ {
+ ULONG blks; /* total blocks (converted to little endian) */
+ ULONG blksiz; /* size of each (converted to little endian) */
+ } READ_CAPACITY_DATA, *PREAD_CAPACITY_DATA;
+
+// SCSI inquiry data
+typedef struct _INQUIRYDATA
+ {
+ UCHAR DeviceType :5;
+ UCHAR DeviceTypeQualifier :3;
+ UCHAR DeviceTypeModifier :7;
+ UCHAR RemovableMedia :1;
+ UCHAR Versions;
+ UCHAR ResponseDataFormat;
+ UCHAR AdditionalLength;
+ UCHAR Reserved[2];
+ UCHAR SoftReset :1;
+ UCHAR CommandQueue :1;
+ UCHAR Reserved2 :1;
+ UCHAR LinkedCommands :1;
+ UCHAR Synchronous :1;
+ UCHAR Wide16Bit :1;
+ UCHAR Wide32Bit :1;
+ UCHAR RelativeAddressing :1;
+ UCHAR VendorId[8];
+ UCHAR ProductId[16];
+ UCHAR ProductRevisionLevel[4];
+ UCHAR VendorSpecific[20];
+ UCHAR Reserved3[40];
+ } INQUIRYDATA, *PINQUIRYDATA;
+
+#endif
+
+// function prototypes
+int Pci2000_Detect (Scsi_Host_Template *tpnt);
+int Pci2000_Command (Scsi_Cmnd *SCpnt);
+int Pci2000_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *));
+int Pci2000_Abort (Scsi_Cmnd *SCpnt);
+int Pci2000_Reset (Scsi_Cmnd *SCpnt, unsigned int flags);
+int Pci2000_BiosParam (Disk *disk, kdev_t dev, int geom[]);
+
+#ifndef NULL
+ #define NULL 0
+#endif
+
+extern struct proc_dir_entry Proc_Scsi_Pci2000;
+
+#define PCI2000 { NULL, NULL, \
+ &Proc_Scsi_Pci2000,/* proc_dir_entry */ \
+ NULL, \
+ "PCI-2000 SCSI Intelligent Disk Controller",\
+ Pci2000_Detect, \
+ NULL, \
+ NULL, \
+ Pci2000_Command, \
+ Pci2000_QueueCommand, \
+ Pci2000_Abort, \
+ Pci2000_Reset, \
+ NULL, \
+ Pci2000_BiosParam, \
+ 16, \
+ -1, \
+ 16, \
+ 1, \
+ 0, \
+ 0, \
+ DISABLE_CLUSTERING }
+
+#endif
diff --git a/drivers/scsi/pci2220i.c b/drivers/scsi/pci2220i.c
new file mode 100644
index 000000000..124cc7b44
--- /dev/null
+++ b/drivers/scsi/pci2220i.c
@@ -0,0 +1,817 @@
+/*+M*************************************************************************
+ * Perceptive Solutions, Inc. PCI-2000 device driver proc support for Linux.
+ *
+ * Copyright (c) 1997 Perceptive Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ * File Name: pci2220i.c
+ *
+ * Description: SCSI driver for the PCI2220I EIDE interface card.
+ *
+ *-M*************************************************************************/
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/head.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+
+#include "pci2220i.h"
+#include "psi_dale.h"
+
+#include<linux/stat.h>
+
+struct proc_dir_entry Proc_Scsi_Pci2220i =
+ { PROC_SCSI_PCI2220I, 7, "pci2220i", S_IFDIR | S_IRUGO | S_IXUGO, 2 };
+
+//#define DEBUG 1
+
+#ifdef DEBUG
+#define DEB(x) x
+#define STOP_HERE {int st;for(st=0;st<100;st++){st=1;}}
+#else
+#define DEB(x)
+#define STOP_HERE
+#endif
+
+#define MAXADAPTER 4 /* Increase this and the sizes of the arrays below, if you need more. */
+
+#define MAX_BUS_MASTER_BLOCKS 1 // This is the maximum we can bus master for (1024 bytes)
+
+#define PORT_DATA 0
+#define PORT_ERROR 1
+#define PORT_SECTOR_COUNT 2
+#define PORT_LBA_0 3
+#define PORT_LBA_8 4
+#define PORT_LBA_16 5
+#define PORT_LBA_24 6
+#define PORT_STAT_CMD 7
+#define PORT_STAT_SEL 8
+#define PORT_FAIL 9
+#define PORT_ALT_STAT 10
+
+typedef struct
+ {
+ UCHAR device; // device code
+ UCHAR byte6; // device select register image
+ UCHAR spigot; // spigot number
+ UCHAR sparebyte; // placeholder
+ USHORT sectors; // number of sectors per track
+ USHORT heads; // number of heads
+ USHORT cylinders; // number of cylinders for this device
+ USHORT spareword; // placeholder
+ ULONG blocks; // number of blocks on device
+ } OUR_DEVICE, *POUR_DEVICE;
+
+typedef struct
+ {
+ USHORT ports[12];
+ USHORT regDmaDesc; // address of the DMA discriptor register for direction of transfer
+ USHORT regDmaCmdStat; // Byte #1 of DMA command status register
+ USHORT regDmaAddrPci; // 32 bit register for PCI address of DMA
+ USHORT regDmaAddrLoc; // 32 bit register for local bus address of DMA
+ USHORT regDmaCount; // 32 bit register for DMA transfer count
+ USHORT regDmaMode; // 32 bit register for DMA mode control
+ USHORT regRemap; // 32 bit local space remap
+ USHORT regDesc; // 32 bit local region descriptor
+ USHORT regRange; // 32 bit local range
+ USHORT regIrqControl; // 16 bit Interrupt enable/disable and status
+ USHORT regScratchPad; // scratch pad I/O base address
+ USHORT regBase; // Base I/O register for data space
+ USHORT basePort; // PLX base I/O port
+ USHORT timingMode; // timing mode currently set for adapter
+ ULONG timingAddress; // address to use on adapter for current timing mode
+ OUR_DEVICE device[4];
+ IDE_STRUCT ide;
+ ULONG startSector;
+ USHORT sectorCount;
+ Scsi_Cmnd *SCpnt;
+ VOID *buffer;
+ USHORT expectingIRQ;
+ USHORT readPhase;
+ } ADAPTER2220I, *PADAPTER2220I;
+
+#define HOSTDATA(host) ((PADAPTER2220I)&host->hostdata)
+
+
+static struct Scsi_Host *PsiHost[MAXADAPTER] = {NULL,}; // One for each adapter
+static int NumAdapters = 0;
+static IDENTIFY_DATA identifyData;
+static SETUP DaleSetup;
+
+/****************************************************************
+ * Name: WriteData :LOCAL
+ *
+ * Description: Write data to device.
+ *
+ * Parameters: padapter - Pointer adapter data structure.
+ *
+ * Returns: TRUE if drive does not assert DRQ in time.
+ *
+ ****************************************************************/
+static int WriteData (PADAPTER2220I padapter)
+ {
+ ULONG timer;
+ USHORT *pports = padapter->ports;
+
+ timer = jiffies + TIMEOUT_DRQ; // calculate the timeout value
+ do {
+ if ( inb_p (pports[PORT_STAT_CMD]) & IDE_STATUS_DRQ )
+ {
+ outb_p (0, padapter->regDmaDesc); // write operation
+ outl (padapter->timingAddress, padapter->regDmaAddrLoc);
+ outl (virt_to_bus (padapter->buffer), padapter->regDmaAddrPci);
+ outl ((ULONG)padapter->ide.ide.ide[2] * (ULONG)512, padapter->regDmaCount);
+ outb_p (1, padapter->regDmaMode); // interrupts off
+ outb_p (0x03, padapter->regDmaCmdStat); // kick the DMA engine in gear
+ return 0;
+ }
+ } while ( timer > jiffies ); // test for timeout
+
+ padapter->ide.ide.ides.cmd = 0; // null out the command byte
+ return 1;
+ }
+/****************************************************************
+ * Name: IdeCmd :LOCAL
+ *
+ * Description: Process a queued command from the SCSI manager.
+ *
+ * Parameters: padapter - Pointer adapter data structure.
+ *
+ * Returns: Zero if no error or status register contents on error.
+ *
+ ****************************************************************/
+static UCHAR IdeCmd (PADAPTER2220I padapter)
+ {
+ ULONG timer;
+ USHORT *pports = padapter->ports;
+ UCHAR status;
+
+ outb_p (padapter->ide.ide.ides.spigot, pports[PORT_STAT_SEL]); // select the spigot
+ outb_p (padapter->ide.ide.ide[6], pports[PORT_LBA_24]); // select the drive
+ timer = jiffies + TIMEOUT_READY; // calculate the timeout value
+ DEB(printk ("\npci2220i Issueing new command: 0x%X",padapter->ide.ide.ides.cmd));
+ do {
+ status = inb_p (padapter->ports[PORT_STAT_CMD]);
+ if ( status & IDE_STATUS_DRDY )
+ {
+ outb_p (padapter->ide.ide.ide[2], pports[PORT_SECTOR_COUNT]);
+ outb_p (padapter->ide.ide.ide[3], pports[PORT_LBA_0]);
+ outb_p (padapter->ide.ide.ide[4], pports[PORT_LBA_8]);
+ outb_p (padapter->ide.ide.ide[5], pports[PORT_LBA_16]);
+ padapter->expectingIRQ = 1;
+ outb_p (padapter->ide.ide.ide[7], pports[PORT_STAT_CMD]);
+
+ if ( padapter->ide.ide.ides.cmd == IDE_CMD_WRITE_MULTIPLE )
+ return (WriteData (padapter));
+ return 0;
+ }
+ } while ( timer > jiffies ); // test for timeout
+
+ padapter->ide.ide.ides.cmd = 0; // null out the command byte
+ return status;
+ }
+/****************************************************************
+ * Name: SetupTransfer :LOCAL
+ *
+ * Description: Setup a data transfer command.
+ *
+ * Parameters: padapter - Pointer adapter data structure.
+ * drive - Drive/head register upper nibble only.
+ *
+ * Returns: TRUE if no data to transfer.
+ *
+ ****************************************************************/
+static int SetupTransfer (PADAPTER2220I padapter, UCHAR drive)
+ {
+ if ( padapter->sectorCount )
+ {
+ *(ULONG *)padapter->ide.ide.ides.lba = padapter->startSector;
+ padapter->ide.ide.ide[6] |= drive;
+// padapter->ide.ide.ides.sectors = ( padapter->sectorCount > SECTORSXFER ) ? SECTORSXFER : padapter->sectorCount;
+ padapter->ide.ide.ides.sectors = ( padapter->sectorCount > MAX_BUS_MASTER_BLOCKS ) ? MAX_BUS_MASTER_BLOCKS : padapter->sectorCount;
+ padapter->sectorCount -= padapter->ide.ide.ides.sectors; // bump the start and count for next xfer
+ padapter->startSector += padapter->ide.ide.ides.sectors;
+ return 0;
+ }
+ else
+ {
+ padapter->ide.ide.ides.cmd = 0; // null out the command byte
+ padapter->SCpnt = NULL;
+ return 1;
+ }
+ }
+/****************************************************************
+ * Name: DecodeError :LOCAL
+ *
+ * Description: Decode and process device errors.
+ *
+ * Parameters: pshost - Pointer to host data block.
+ * status - Status register code.
+ *
+ * Returns: The driver status code.
+ *
+ ****************************************************************/
+static ULONG DecodeError (struct Scsi_Host *pshost, UCHAR status)
+ {
+ PADAPTER2220I padapter = HOSTDATA(pshost);
+ UCHAR error;
+
+ padapter->expectingIRQ = 0;
+ padapter->SCpnt = NULL;
+ if ( status & IDE_STATUS_WRITE_FAULT )
+ {
+ return DID_PARITY << 16;
+ }
+ if ( status & IDE_STATUS_BUSY )
+ return DID_BUS_BUSY << 16;
+
+ error = inb_p (padapter->ports[PORT_ERROR]);
+ DEB(printk ("\npci2220i error register: %x", error));
+ switch ( error )
+ {
+ case IDE_ERROR_AMNF:
+ case IDE_ERROR_TKONF:
+ case IDE_ERROR_ABRT:
+ case IDE_ERROR_IDFN:
+ case IDE_ERROR_UNC:
+ case IDE_ERROR_BBK:
+ default:
+ return DID_ERROR << 16;
+ }
+ return DID_ERROR << 16;
+ }
+/****************************************************************
+ * Name: Irq_Handler :LOCAL
+ *
+ * Description: Interrupt handler.
+ *
+ * Parameters: irq - Hardware IRQ number.
+ * dev_id -
+ * regs -
+ *
+ * Returns: TRUE if drive is not ready in time.
+ *
+ ****************************************************************/
+static void Irq_Handler (int irq, void *dev_id, struct pt_regs *regs)
+ {
+ struct Scsi_Host *shost = NULL; // Pointer to host data block
+ PADAPTER2220I padapter; // Pointer to adapter control structure
+ USHORT *pports; // I/O port array
+ Scsi_Cmnd *SCpnt;
+ UCHAR status;
+ int z;
+
+// DEB(printk ("\npci2220i recieved interrupt\n"));
+
+ for ( z = 0; z < NumAdapters; z++ ) // scan for interrupt to process
+ {
+ if ( PsiHost[z]->irq == (UCHAR)(irq & 0xFF) )
+ {
+ if ( inw_p (HOSTDATA(PsiHost[z])->regIrqControl) & 0x8000 )
+ {
+ shost = PsiHost[z];
+ break;
+ }
+ }
+ }
+
+ if ( !shost )
+ {
+ DEB (printk ("\npci2220i: not my interrupt"));
+ return;
+ }
+
+ padapter = HOSTDATA(shost);
+ pports = padapter->ports;
+ SCpnt = padapter->SCpnt;
+
+ if ( !padapter->expectingIRQ )
+ {
+ DEB(printk ("\npci2220i Unsolicited interrupt\n"));
+ return;
+ }
+ padapter->expectingIRQ = 0;
+
+ status = inb_p (padapter->ports[PORT_STAT_CMD]); // read the device status
+ if ( status & (IDE_STATUS_ERROR | IDE_STATUS_WRITE_FAULT) )
+ goto irqerror;
+
+ switch ( padapter->ide.ide.ides.cmd ) // decide how to handle the interrupt
+ {
+ case IDE_CMD_READ_MULTIPLE:
+ if ( padapter->readPhase == 1 ) // is this a bus master channel complete?
+ {
+ DEB(printk ("\npci2220i processing read interrupt cleanup"));
+ outb_p (0x08, padapter->regDmaCmdStat); // cancel interrupt from DMA engine
+ padapter->buffer += padapter->ide.ide.ides.sectors * 512;
+ if ( SetupTransfer (padapter, padapter->ide.ide.ide[6] & 0xF0) )
+ {
+ SCpnt->result = DID_OK << 16;
+ padapter->SCpnt = NULL;
+ SCpnt->scsi_done (SCpnt);
+ return;
+ }
+ padapter->readPhase = 0;
+ if ( !(status = IdeCmd (padapter)) )
+ {
+ DEB (printk ("\npci2220i interrupt complete, waiting for another"));
+ return;
+ }
+ }
+ if ( status & IDE_STATUS_DRQ )
+ {
+ DEB(printk ("\npci2220i processing read interrupt start bus master cycle"));
+ outb_p (8, padapter->regDmaDesc); // read operation
+ padapter->readPhase = 1;
+ padapter->expectingIRQ = 1;
+ outl (padapter->timingAddress, padapter->regDmaAddrLoc);
+ outl (virt_to_bus (padapter->buffer), padapter->regDmaAddrPci);
+ outl ((ULONG)padapter->ide.ide.ides.sectors * (ULONG)512, padapter->regDmaCount);
+ outb_p (5, padapter->regDmaMode); // interrupt enable/disable
+ outb_p (0x03, padapter->regDmaCmdStat); // kick the DMA engine in gear
+ return;
+ }
+ break;
+
+ case IDE_CMD_WRITE_MULTIPLE:
+ DEB(printk ("\npci2220i processing write interrupt cleanup"));
+ padapter->buffer += padapter->ide.ide.ides.sectors * 512;
+ if ( SetupTransfer (padapter, padapter->ide.ide.ide[6] & 0xF0) )
+ {
+ SCpnt->result = DID_OK << 16;
+ padapter->SCpnt = NULL;
+ SCpnt->scsi_done (SCpnt);
+ return;
+ }
+ if ( !(status = IdeCmd (padapter)) )
+ {
+ DEB (printk ("\npci2220i interrupt complete, waiting for another"));
+ return;
+ }
+ break;
+
+ case IDE_COMMAND_IDENTIFY:
+ {
+ PINQUIRYDATA pinquiryData = SCpnt->request_buffer;
+
+ DEB(printk ("\npci2220i processing verify interrupt cleanup"));
+ if ( status & IDE_STATUS_DRQ )
+ {
+ insw (pports[PORT_DATA], &identifyData, sizeof (identifyData) >> 1);
+
+ memset (pinquiryData, 0, SCpnt->request_bufflen); // Zero INQUIRY data structure.
+ pinquiryData->DeviceType = 0;
+ pinquiryData->Versions = 2;
+ pinquiryData->AdditionalLength = 35 - 4;
+
+ // Fill in vendor identification fields.
+ for ( z = 0; z < 20; z += 2 )
+ {
+ pinquiryData->VendorId[z] = ((UCHAR *)identifyData.ModelNumber)[z + 1];
+ pinquiryData->VendorId[z + 1] = ((UCHAR *)identifyData.ModelNumber)[z];
+ }
+
+ // Initialize unused portion of product id.
+ for ( z = 0; z < 4; z++ )
+ pinquiryData->ProductId[12 + z] = ' ';
+
+ // Move firmware revision from IDENTIFY data to
+ // product revision in INQUIRY data.
+ for ( z = 0; z < 4; z += 2 )
+ {
+ pinquiryData->ProductRevisionLevel[z] = ((UCHAR *)identifyData.FirmwareRevision)[z + 1];
+ pinquiryData->ProductRevisionLevel[z + 1] = ((UCHAR *)identifyData.FirmwareRevision)[z];
+ }
+
+ SCpnt->result = DID_OK << 16;
+ padapter->SCpnt = NULL;
+ SCpnt->scsi_done (SCpnt);
+ return;
+ }
+ break;
+ }
+
+ default:
+ DEB(printk ("\npci2220i no real process here!"));
+ SCpnt->result = DID_OK << 16;
+ padapter->SCpnt = NULL;
+ SCpnt->scsi_done (SCpnt);
+ return;
+ }
+
+irqerror:;
+ DEB(printk ("\npci2220i error Device Status: %X\n", status));
+ SCpnt->result = DecodeError (shost, status);
+ SCpnt->scsi_done (SCpnt);
+ }
+/****************************************************************
+ * Name: Pci2220i_QueueCommand
+ *
+ * Description: Process a queued command from the SCSI manager.
+ *
+ * Parameters: SCpnt - Pointer to SCSI command structure.
+ * done - Pointer to done function to call.
+ *
+ * Returns: Status code.
+ *
+ ****************************************************************/
+int Pci2220i_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+ {
+ UCHAR *cdb = (UCHAR *)SCpnt->cmnd; // Pointer to SCSI CDB
+ PADAPTER2220I padapter = HOSTDATA(SCpnt->host); // Pointer to adapter control structure
+ POUR_DEVICE pdev = &padapter->device[SCpnt->target];// Pointer to device information
+ UCHAR rc; // command return code
+
+ SCpnt->scsi_done = done;
+ padapter->ide.ide.ides.spigot = pdev->spigot;
+ padapter->buffer = SCpnt->request_buffer;
+ if (done)
+ {
+ if ( !pdev->device )
+ {
+ SCpnt->result = DID_BAD_TARGET << 16;
+ done (SCpnt);
+ return 0;
+ }
+ }
+ else
+ {
+ printk("pci2220i_queuecommand: %02X: done can't be NULL\n", *cdb);
+ return 0;
+ }
+
+ DEB (if(*cdb) printk ("\nCDB: %X- %X %X %X %X %X %X %X %X %X %X ", SCpnt->cmd_len, cdb[0], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5], cdb[6], cdb[7], cdb[8], cdb[9]));
+ switch ( *cdb )
+ {
+ case SCSIOP_INQUIRY: // inquiry CDB
+ {
+ padapter->ide.ide.ide[6] = pdev->byte6;
+ padapter->ide.ide.ides.cmd = IDE_COMMAND_IDENTIFY;
+ break;
+ }
+
+ case SCSIOP_TEST_UNIT_READY: // test unit ready CDB
+ SCpnt->result = DID_OK << 16;
+ done (SCpnt);
+ return 0;
+
+ case SCSIOP_READ_CAPACITY: // read capctiy CDB
+ {
+ PREAD_CAPACITY_DATA pdata = (PREAD_CAPACITY_DATA)SCpnt->request_buffer;
+
+ pdata->blksiz = 0x20000;
+ XANY2SCSI ((UCHAR *)&pdata->blks, pdev->blocks);
+ SCpnt->result = DID_OK << 16;
+ done (SCpnt);
+ return 0;
+ }
+
+ case SCSIOP_VERIFY: // verify CDB
+ *(ULONG *)padapter->ide.ide.ides.lba = XSCSI2LONG (&cdb[2]);
+ padapter->ide.ide.ide[6] |= pdev->byte6;
+ padapter->ide.ide.ide[2] = (UCHAR)((USHORT)cdb[8] | ((USHORT)cdb[7] << 8));
+ padapter->ide.ide.ides.cmd = IDE_COMMAND_VERIFY;
+ break;
+
+ case SCSIOP_READ: // read10 CDB
+ padapter->startSector = XSCSI2LONG (&cdb[2]);
+ padapter->sectorCount = (USHORT)cdb[8] | ((USHORT)cdb[7] << 8);
+ SetupTransfer (padapter, pdev->byte6);
+ padapter->ide.ide.ides.cmd = IDE_CMD_READ_MULTIPLE;
+ padapter->readPhase = 0;
+ break;
+
+ case SCSIOP_READ6: // read6 CDB
+ padapter->startSector = SCSI2LONG (&cdb[1]);
+ padapter->sectorCount = cdb[4];
+ SetupTransfer (padapter, pdev->byte6);
+ padapter->ide.ide.ides.cmd = IDE_CMD_READ_MULTIPLE;
+ padapter->readPhase = 0;
+ break;
+
+ case SCSIOP_WRITE: // write10 CDB
+ padapter->startSector = XSCSI2LONG (&cdb[2]);
+ padapter->sectorCount = (USHORT)cdb[8] | ((USHORT)cdb[7] << 8);
+ SetupTransfer (padapter, pdev->byte6);
+ padapter->ide.ide.ides.cmd = IDE_CMD_WRITE_MULTIPLE;
+ break;
+ case SCSIOP_WRITE6: // write6 CDB
+ padapter->startSector = SCSI2LONG (&cdb[1]);
+ padapter->sectorCount = cdb[4];
+ SetupTransfer (padapter, pdev->byte6);
+ padapter->ide.ide.ides.cmd = IDE_CMD_WRITE_MULTIPLE;
+ break;
+
+ default:
+ DEB (printk ("pci2220i_queuecommand: Unsupported command %02X\n", *cdb));
+ SCpnt->result = DID_ERROR << 16;
+ done (SCpnt);
+ return 0;
+ }
+
+ padapter->SCpnt = SCpnt; // Save this command data
+
+ rc = IdeCmd (padapter);
+ if ( rc )
+ {
+ padapter->expectingIRQ = 0;
+ DEB (printk ("pci2220i_queuecommand: %02X, %02X: Device failed to respond for command\n", *cdb, padapter->ide.ide.ides.cmd));
+ SCpnt->result = DID_ERROR << 16;
+ done (SCpnt);
+ return 0;
+ }
+ if ( padapter->ide.ide.ides.cmd == IDE_CMD_WRITE_MULTIPLE )
+ {
+ if ( WriteData (padapter) )
+ {
+ padapter->expectingIRQ = 0;
+ DEB (printk ("pci2220i_queuecommand: %02X, %02X: Device failed to accept data\n", *cdb, padapter->ide.ide.ides.cmd));
+ SCpnt->result = DID_ERROR << 16;
+ done (SCpnt);
+ return 0;
+ }
+ }
+ DEB (printk(" now waiting for initial interrupt "));
+ return 0;
+ }
+
+static void internal_done(Scsi_Cmnd * SCpnt)
+ {
+ SCpnt->SCp.Status++;
+ }
+/****************************************************************
+ * Name: Pci2220i_Command
+ *
+ * Description: Process a command from the SCSI manager.
+ *
+ * Parameters: SCpnt - Pointer to SCSI command structure.
+ *
+ * Returns: Status code.
+ *
+ ****************************************************************/
+int Pci2220i_Command (Scsi_Cmnd *SCpnt)
+ {
+ DEB(printk("pci2220i_command: ..calling pci2220i_queuecommand\n"));
+
+ Pci2220i_QueueCommand (SCpnt, internal_done);
+
+ SCpnt->SCp.Status = 0;
+ while (!SCpnt->SCp.Status)
+ barrier ();
+ return SCpnt->result;
+ }
+/****************************************************************
+ * Name: ReadFlash
+ *
+ * Description: Read information from controller Flash memory.
+ *
+ * Parameters: hostdata - Pointer to host interface data structure.
+ * pdata - Pointer to data structures.
+ * base - base address in Flash.
+ * length - lenght of data space in bytes.
+ *
+ * Returns: Nothing.
+ *
+ ****************************************************************/
+VOID ReadFlash (PADAPTER2220I hostdata, VOID *pdata, ULONG base, ULONG length)
+ {
+ ULONG oldremap;
+ UCHAR olddesc;
+ ULONG z;
+ UCHAR *pd = (UCHAR *)pdata;
+
+ oldremap = inl (hostdata->regRemap); // save values to restore later
+ olddesc = inb_p (hostdata->regDesc);
+
+ outl (base | 1, hostdata->regRemap); // remap to Flash space as specified
+ outb_p (0x40, hostdata->regDesc); // describe remap region as 8 bit
+ for ( z = 0; z < length; z++) // get "length" data count
+ *pd++ = inb_p (hostdata->regBase + z); // read in the data
+
+ outl (oldremap, hostdata->regRemap); // restore remap register values
+ outb_p (olddesc, hostdata->regDesc);
+ }
+
+/****************************************************************
+ * Name: Pci2220i_Detect
+ *
+ * Description: Detect and initialize our boards.
+ *
+ * Parameters: tpnt - Pointer to SCSI host template structure.
+ *
+ * Returns: Number of adapters found.
+ *
+ ****************************************************************/
+int Pci2220i_Detect (Scsi_Host_Template *tpnt)
+ {
+ int pci_index = 0;
+ struct Scsi_Host *pshost;
+ PADAPTER2220I hostdata;
+ ULONG modearray[] = {DALE_DATA_MODE2, DALE_DATA_MODE3, DALE_DATA_MODE4, DALE_DATA_MODE4P};
+ int unit;
+ int z;
+ int setirq;
+
+ if ( pcibios_present () )
+ {
+ for ( pci_index = 0; pci_index <= MAXADAPTER; ++pci_index )
+ {
+ UCHAR pci_bus, pci_device_fn;
+
+ if ( pcibios_find_device (VENDOR_PSI, DEVICE_DALE_1, pci_index, &pci_bus, &pci_device_fn) != 0 )
+ break;
+
+ pshost = scsi_register (tpnt, sizeof(ADAPTER2220I));
+ hostdata = HOSTDATA(pshost);
+
+ pcibios_read_config_word (pci_bus, pci_device_fn, PCI_BASE_ADDRESS_1, &hostdata->basePort);
+ hostdata->basePort &= 0xFFFE;
+ DEB (printk ("\nBase Regs = %#04X", hostdata->basePort));
+ hostdata->regRemap = hostdata->basePort + RTR_LOCAL_REMAP; // 32 bit local space remap
+ DEB (printk (" %#04X", hostdata->regRemap));
+ hostdata->regDesc = hostdata->basePort + RTR_REGIONS; // 32 bit local region descriptor
+ DEB (printk (" %#04X", hostdata->regDesc));
+ hostdata->regRange = hostdata->basePort + RTR_LOCAL_RANGE; // 32 bit local range
+ DEB (printk (" %#04X", hostdata->regRange));
+ hostdata->regIrqControl = hostdata->basePort + RTR_INT_CONTROL_STATUS; // 16 bit interupt control and status
+ DEB (printk (" %#04X", hostdata->regIrqControl));
+ hostdata->regScratchPad = hostdata->basePort + RTR_MAILBOX; // 16 byte scratchpad I/O base address
+ DEB (printk (" %#04X", hostdata->regScratchPad));
+
+ pcibios_read_config_word (pci_bus, pci_device_fn, PCI_BASE_ADDRESS_2, &hostdata->regBase);
+ hostdata->regBase &= 0xFFFE;
+ for ( z = 0; z < 9; z++ ) // build regester address array
+ hostdata->ports[z] = hostdata->regBase + 0x80 + (z * 4);
+ hostdata->ports[PORT_FAIL] = hostdata->regBase + REG_FAIL;
+ hostdata->ports[PORT_ALT_STAT] = hostdata->regBase + REG_ALT_STAT;
+ DEB (printk ("\nPorts ="));
+ DEB (for (z=0;z<11;z++) printk(" %#04X", hostdata->ports[z]););
+
+ hostdata->regDmaDesc = hostdata->regBase + RTL_DMA1_DESC_PTR; // address of the DMA discriptor register for direction of transfer
+ DEB (printk ("\nDMA Regs = %#04X", hostdata->regDmaDesc));
+ hostdata->regDmaCmdStat = hostdata->regBase + RTL_DMA_COMMAND_STATUS + 1; // Byte #1 of DMA command status register
+ DEB (printk (" %#04X", hostdata->regDmaCmdStat));
+ hostdata->regDmaAddrPci = hostdata->regBase + RTL_DMA1_PCI_ADDR; // 32 bit register for PCI address of DMA
+ DEB (printk (" %#04X", hostdata->regDmaAddrPci));
+ hostdata->regDmaAddrLoc = hostdata->regBase + RTL_DMA1_LOCAL_ADDR; // 32 bit register for local bus address of DMA
+ DEB (printk (" %#04X", hostdata->regDmaAddrLoc));
+ hostdata->regDmaCount = hostdata->regBase + RTL_DMA1_COUNT; // 32 bit register for DMA transfer count
+ DEB (printk (" %#04X", hostdata->regDmaCount));
+ hostdata->regDmaMode = hostdata->regBase + RTL_DMA1_MODE + 1; // 32 bit register for DMA mode control
+ DEB (printk (" %#04X", hostdata->regDmaMode));
+
+ if ( !inb_p (hostdata->regScratchPad + DALE_NUM_DRIVES) ) // if no devices on this board
+ goto unregister;
+
+ pcibios_read_config_byte (pci_bus, pci_device_fn, PCI_INTERRUPT_LINE, &pshost->irq);
+ setirq = 1;
+ for ( z = 0; z < pci_index; z++ ) // scan for shared interrupts
+ {
+ if ( PsiHost[z]->irq == pshost->irq ) // if shared then, don't posses
+ setirq = 0;
+ }
+ if ( setirq ) // if not shared, posses
+ {
+ if ( request_irq (pshost->irq, Irq_Handler, 0, "pci2220i", NULL) )
+ {
+ printk ("Unable to allocate IRQ for PSI-2220I controller.\n");
+ goto unregister;
+ }
+ }
+ PsiHost[pci_index] = pshost; // save SCSI_HOST pointer
+
+ pshost->unique_id = hostdata->regBase;
+ pshost->max_id = 4;
+
+ outb_p (0x01, hostdata->regRange); // fix our range register because other drivers want to tromp on it
+
+ hostdata->timingMode = inb_p (hostdata->regScratchPad + DALE_TIMING_MODE);
+ hostdata->timingAddress = modearray[hostdata->timingMode - 2];
+ ReadFlash (hostdata, &DaleSetup, DALE_FLASH_SETUP, sizeof (SETUP));
+
+ for ( z = 0; z < inb_p (hostdata->regScratchPad + DALE_NUM_DRIVES); ++z )
+ {
+ unit = inb_p (hostdata->regScratchPad + DALE_CHANNEL_DEVICE_0 + z) & 0x0F;
+ hostdata->device[unit].device = inb_p (hostdata->regScratchPad + DALE_SCRATH_DEVICE_0 + unit);
+ hostdata->device[unit].byte6 = (UCHAR)(((unit & 1) << 4) | 0xE0);
+ hostdata->device[unit].spigot = (UCHAR)(1 << (unit >> 1));
+ hostdata->device[unit].sectors = DaleSetup.setupDevice[unit].sectors;
+ hostdata->device[unit].heads = DaleSetup.setupDevice[unit].heads;
+ hostdata->device[unit].cylinders = DaleSetup.setupDevice[unit].cylinders;
+ hostdata->device[unit].blocks = DaleSetup.setupDevice[unit].blocks;
+ DEB (printk ("\nHOSTDATA->device = %X", hostdata->device[unit].device));
+ DEB (printk ("\n byte6 = %X", hostdata->device[unit].byte6));
+ DEB (printk ("\n spigot = %X", hostdata->device[unit].spigot));
+ DEB (printk ("\n sectors = %X", hostdata->device[unit].sectors));
+ DEB (printk ("\n heads = %X", hostdata->device[unit].heads));
+ DEB (printk ("\n cylinders = %X", hostdata->device[unit].cylinders));
+ DEB (printk ("\n blocks = %lX", hostdata->device[unit].blocks));
+ }
+
+ printk("\nPSI-2220I EIDE CONTROLLER: at I/O = %X/%X IRQ = %d\n", hostdata->basePort, hostdata->regBase, pshost->irq);
+ printk("(C) 1997 Perceptive Solutions, Inc. All rights reserved\n\n");
+ continue;
+unregister:
+ scsi_unregister (pshost);
+ NumAdapters++;
+ }
+ }
+ return NumAdapters;
+ }
+/****************************************************************
+ * Name: Pci2220i_Abort
+ *
+ * Description: Process the Abort command from the SCSI manager.
+ *
+ * Parameters: SCpnt - Pointer to SCSI command structure.
+ *
+ * Returns: Allways snooze.
+ *
+ ****************************************************************/
+int Pci2220i_Abort (Scsi_Cmnd *SCpnt)
+ {
+ DEB (printk ("pci2220i_abort\n"));
+ return SCSI_ABORT_SNOOZE;
+ }
+/****************************************************************
+ * Name: Pci2220i_Reset
+ *
+ * Description: Process the Reset command from the SCSI manager.
+ *
+ * Parameters: SCpnt - Pointer to SCSI command structure.
+ * flags - Flags about the reset command
+ *
+ * Returns: No active command at this time, so this means
+ * that each time we got some kind of response the
+ * last time through. Tell the mid-level code to
+ * request sense information in order to decide what
+ * to do next.
+ *
+ ****************************************************************/
+int Pci2220i_Reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags)
+ {
+ return SCSI_RESET_PUNT;
+ }
+
+#include "sd.h"
+
+/****************************************************************
+ * Name: Pci2220i_BiosParam
+ *
+ * Description: Process the biosparam request from the SCSI manager to
+ * return C/H/S data.
+ *
+ * Parameters: disk - Pointer to SCSI disk structure.
+ * dev - Major/minor number from kernel.
+ * geom - Pointer to integer array to place geometry data.
+ *
+ * Returns: zero.
+ *
+ ****************************************************************/
+int Pci2220i_BiosParam (Scsi_Disk *disk, kdev_t dev, int geom[])
+ {
+ POUR_DEVICE pdev;
+
+ pdev = &(HOSTDATA(disk->device->host)->device[disk->device->id]);
+
+ geom[0] = pdev->heads;
+ geom[1] = pdev->sectors;
+ geom[2] = pdev->cylinders;
+ return 0;
+ }
+
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = PCI2220I;
+
+#include "scsi_module.c"
+#endif
diff --git a/drivers/scsi/pci2220i.h b/drivers/scsi/pci2220i.h
new file mode 100644
index 000000000..0fafc2648
--- /dev/null
+++ b/drivers/scsi/pci2220i.h
@@ -0,0 +1,345 @@
+/*+M*************************************************************************
+ * Perceptive Solutions, Inc. PCI-2000 device driver proc support for Linux.
+ *
+ * Copyright (c) 1997 Perceptive Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ * File Name: pci2220i.h
+ *
+ * Description: Header file for the SCSI driver for the PCI2220I
+ * EIDE interface card.
+ *
+ *-M*************************************************************************/
+
+#ifndef _PCI2220I_H
+#define _PCI2220I_H
+
+#include <linux/types.h>
+#include <linux/kdev_t.h>
+
+#ifndef PSI_EIDE_SCSIOP
+#define PSI_EIDE_SCSIOP 1
+
+/************************************************/
+/* Some defines that we like */
+/************************************************/
+#define CHAR char
+#define UCHAR unsigned char
+#define SHORT short
+#define USHORT unsigned short
+#define BOOL unsigned short
+#define LONG long
+#define ULONG unsigned long
+#define VOID void
+
+/************************************************/
+/* Timeout konstants */
+/************************************************/
+#define TIMEOUT_READY 10 // 100 mSec
+#define TIMEOUT_DRQ 40 // 400 mSec
+
+/************************************************/
+/* Misc. macros */
+/************************************************/
+#define ANY2SCSI(up, p) \
+((UCHAR *)up)[0] = (((ULONG)(p)) >> 8); \
+((UCHAR *)up)[1] = ((ULONG)(p));
+
+#define SCSI2LONG(up) \
+( (((long)*(((UCHAR *)up))) << 16) \
++ (((long)(((UCHAR *)up)[1])) << 8) \
++ ((long)(((UCHAR *)up)[2])) )
+
+#define XANY2SCSI(up, p) \
+((UCHAR *)up)[0] = ((long)(p)) >> 24; \
+((UCHAR *)up)[1] = ((long)(p)) >> 16; \
+((UCHAR *)up)[2] = ((long)(p)) >> 8; \
+((UCHAR *)up)[3] = ((long)(p));
+
+#define XSCSI2LONG(up) \
+( (((long)(((UCHAR *)up)[0])) << 24) \
++ (((long)(((UCHAR *)up)[1])) << 16) \
++ (((long)(((UCHAR *)up)[2])) << 8) \
++ ((long)(((UCHAR *)up)[3])) )
+
+/************************************************/
+/* SCSI CDB operation codes */
+/************************************************/
+#define SCSIOP_TEST_UNIT_READY 0x00
+#define SCSIOP_REZERO_UNIT 0x01
+#define SCSIOP_REWIND 0x01
+#define SCSIOP_REQUEST_BLOCK_ADDR 0x02
+#define SCSIOP_REQUEST_SENSE 0x03
+#define SCSIOP_FORMAT_UNIT 0x04
+#define SCSIOP_READ_BLOCK_LIMITS 0x05
+#define SCSIOP_REASSIGN_BLOCKS 0x07
+#define SCSIOP_READ6 0x08
+#define SCSIOP_RECEIVE 0x08
+#define SCSIOP_WRITE6 0x0A
+#define SCSIOP_PRINT 0x0A
+#define SCSIOP_SEND 0x0A
+#define SCSIOP_SEEK6 0x0B
+#define SCSIOP_TRACK_SELECT 0x0B
+#define SCSIOP_SLEW_PRINT 0x0B
+#define SCSIOP_SEEK_BLOCK 0x0C
+#define SCSIOP_PARTITION 0x0D
+#define SCSIOP_READ_REVERSE 0x0F
+#define SCSIOP_WRITE_FILEMARKS 0x10
+#define SCSIOP_FLUSH_BUFFER 0x10
+#define SCSIOP_SPACE 0x11
+#define SCSIOP_INQUIRY 0x12
+#define SCSIOP_VERIFY6 0x13
+#define SCSIOP_RECOVER_BUF_DATA 0x14
+#define SCSIOP_MODE_SELECT 0x15
+#define SCSIOP_RESERVE_UNIT 0x16
+#define SCSIOP_RELEASE_UNIT 0x17
+#define SCSIOP_COPY 0x18
+#define SCSIOP_ERASE 0x19
+#define SCSIOP_MODE_SENSE 0x1A
+#define SCSIOP_START_STOP_UNIT 0x1B
+#define SCSIOP_STOP_PRINT 0x1B
+#define SCSIOP_LOAD_UNLOAD 0x1B
+#define SCSIOP_RECEIVE_DIAGNOSTIC 0x1C
+#define SCSIOP_SEND_DIAGNOSTIC 0x1D
+#define SCSIOP_MEDIUM_REMOVAL 0x1E
+#define SCSIOP_READ_CAPACITY 0x25
+#define SCSIOP_READ 0x28
+#define SCSIOP_WRITE 0x2A
+#define SCSIOP_SEEK 0x2B
+#define SCSIOP_LOCATE 0x2B
+#define SCSIOP_WRITE_VERIFY 0x2E
+#define SCSIOP_VERIFY 0x2F
+#define SCSIOP_SEARCH_DATA_HIGH 0x30
+#define SCSIOP_SEARCH_DATA_EQUAL 0x31
+#define SCSIOP_SEARCH_DATA_LOW 0x32
+#define SCSIOP_SET_LIMITS 0x33
+#define SCSIOP_READ_POSITION 0x34
+#define SCSIOP_SYNCHRONIZE_CACHE 0x35
+#define SCSIOP_COMPARE 0x39
+#define SCSIOP_COPY_COMPARE 0x3A
+#define SCSIOP_WRITE_DATA_BUFF 0x3B
+#define SCSIOP_READ_DATA_BUFF 0x3C
+#define SCSIOP_CHANGE_DEFINITION 0x40
+#define SCSIOP_READ_SUB_CHANNEL 0x42
+#define SCSIOP_READ_TOC 0x43
+#define SCSIOP_READ_HEADER 0x44
+#define SCSIOP_PLAY_AUDIO 0x45
+#define SCSIOP_PLAY_AUDIO_MSF 0x47
+#define SCSIOP_PLAY_TRACK_INDEX 0x48
+#define SCSIOP_PLAY_TRACK_RELATIVE 0x49
+#define SCSIOP_PAUSE_RESUME 0x4B
+#define SCSIOP_LOG_SELECT 0x4C
+#define SCSIOP_LOG_SENSE 0x4D
+#define SCSIOP_MODE_SELECT10 0x55
+#define SCSIOP_MODE_SENSE10 0x5A
+#define SCSIOP_LOAD_UNLOAD_SLOT 0xA6
+#define SCSIOP_MECHANISM_STATUS 0xBD
+#define SCSIOP_READ_CD 0xBE
+
+// IDE command definitions
+#define IDE_COMMAND_ATAPI_RESET 0x08
+#define IDE_COMMAND_READ 0x20
+#define IDE_COMMAND_WRITE 0x30
+#define IDE_COMMAND_RECALIBRATE 0x10
+#define IDE_COMMAND_SEEK 0x70
+#define IDE_COMMAND_SET_PARAMETERS 0x91
+#define IDE_COMMAND_VERIFY 0x40
+#define IDE_COMMAND_ATAPI_PACKET 0xA0
+#define IDE_COMMAND_ATAPI_IDENTIFY 0xA1
+#define IDE_CMD_READ_MULTIPLE 0xC4
+#define IDE_CMD_WRITE_MULTIPLE 0xC5
+#define IDE_CMD_SET_MULTIPLE 0xC6
+#define IDE_COMMAND_WRITE_DMA 0xCA
+#define IDE_COMMAND_READ_DMA 0xC8
+#define IDE_COMMAND_IDENTIFY 0xEC
+
+// IDE status definitions
+#define IDE_STATUS_ERROR 0x01
+#define IDE_STATUS_INDEX 0x02
+#define IDE_STATUS_CORRECTED_ERROR 0x04
+#define IDE_STATUS_DRQ 0x08
+#define IDE_STATUS_DSC 0x10
+#define IDE_STATUS_WRITE_FAULT 0x20
+#define IDE_STATUS_DRDY 0x40
+#define IDE_STATUS_BUSY 0x80
+
+// IDE error definitions
+#define IDE_ERROR_AMNF 0x01
+#define IDE_ERROR_TKONF 0x02
+#define IDE_ERROR_ABRT 0x04
+#define IDE_ERROR_MCR 0x08
+#define IDE_ERROR_IDFN 0x10
+#define IDE_ERROR_MC 0x20
+#define IDE_ERROR_UNC 0x40
+#define IDE_ERROR_BBK 0x80
+
+// IDE interface structure
+typedef struct _IDE_STRUCT
+ {
+ union
+ {
+ UCHAR ide[9];
+ struct
+ {
+ USHORT data;
+ UCHAR sectors;
+ UCHAR lba[4];
+ UCHAR cmd;
+ UCHAR spigot;
+ } ides;
+ } ide;
+ } IDE_STRUCT;
+
+// SCSI read capacity structure
+typedef struct _READ_CAPACITY_DATA
+ {
+ ULONG blks; /* total blocks (converted to little endian) */
+ ULONG blksiz; /* size of each (converted to little endian) */
+ } READ_CAPACITY_DATA, *PREAD_CAPACITY_DATA;
+
+// SCSI inquiry data
+typedef struct _INQUIRYDATA
+ {
+ UCHAR DeviceType :5;
+ UCHAR DeviceTypeQualifier :3;
+ UCHAR DeviceTypeModifier :7;
+ UCHAR RemovableMedia :1;
+ UCHAR Versions;
+ UCHAR ResponseDataFormat;
+ UCHAR AdditionalLength;
+ UCHAR Reserved[2];
+ UCHAR SoftReset :1;
+ UCHAR CommandQueue :1;
+ UCHAR Reserved2 :1;
+ UCHAR LinkedCommands :1;
+ UCHAR Synchronous :1;
+ UCHAR Wide16Bit :1;
+ UCHAR Wide32Bit :1;
+ UCHAR RelativeAddressing :1;
+ UCHAR VendorId[8];
+ UCHAR ProductId[16];
+ UCHAR ProductRevisionLevel[4];
+ UCHAR VendorSpecific[20];
+ UCHAR Reserved3[40];
+ } INQUIRYDATA, *PINQUIRYDATA;
+
+// IDE IDENTIFY data
+typedef struct _IDENTIFY_DATA
+ {
+ USHORT GeneralConfiguration; // 00
+ USHORT NumberOfCylinders; // 02
+ USHORT Reserved1; // 04
+ USHORT NumberOfHeads; // 06
+ USHORT UnformattedBytesPerTrack; // 08
+ USHORT UnformattedBytesPerSector; // 0A
+ USHORT SectorsPerTrack; // 0C
+ USHORT VendorUnique1[3]; // 0E
+ USHORT SerialNumber[10]; // 14
+ USHORT BufferType; // 28
+ USHORT BufferSectorSize; // 2A
+ USHORT NumberOfEccBytes; // 2C
+ USHORT FirmwareRevision[4]; // 2E
+ USHORT ModelNumber[20]; // 36
+ UCHAR MaximumBlockTransfer; // 5E
+ UCHAR VendorUnique2; // 5F
+ USHORT DoubleWordIo; // 60
+ USHORT Capabilities; // 62
+ USHORT Reserved2; // 64
+ UCHAR VendorUnique3; // 66
+ UCHAR PioCycleTimingMode; // 67
+ UCHAR VendorUnique4; // 68
+ UCHAR DmaCycleTimingMode; // 69
+ USHORT TranslationFieldsValid:1; // 6A
+ USHORT Reserved3:15;
+ USHORT NumberOfCurrentCylinders; // 6C
+ USHORT NumberOfCurrentHeads; // 6E
+ USHORT CurrentSectorsPerTrack; // 70
+ ULONG CurrentSectorCapacity; // 72
+ USHORT Reserved4[197]; // 76
+ } IDENTIFY_DATA, *PIDENTIFY_DATA;
+
+// Identify data without the Reserved4.
+typedef struct _IDENTIFY_DATA2 {
+ USHORT GeneralConfiguration; // 00
+ USHORT NumberOfCylinders; // 02
+ USHORT Reserved1; // 04
+ USHORT NumberOfHeads; // 06
+ USHORT UnformattedBytesPerTrack; // 08
+ USHORT UnformattedBytesPerSector; // 0A
+ USHORT SectorsPerTrack; // 0C
+ USHORT VendorUnique1[3]; // 0E
+ USHORT SerialNumber[10]; // 14
+ USHORT BufferType; // 28
+ USHORT BufferSectorSize; // 2A
+ USHORT NumberOfEccBytes; // 2C
+ USHORT FirmwareRevision[4]; // 2E
+ USHORT ModelNumber[20]; // 36
+ UCHAR MaximumBlockTransfer; // 5E
+ UCHAR VendorUnique2; // 5F
+ USHORT DoubleWordIo; // 60
+ USHORT Capabilities; // 62
+ USHORT Reserved2; // 64
+ UCHAR VendorUnique3; // 66
+ UCHAR PioCycleTimingMode; // 67
+ UCHAR VendorUnique4; // 68
+ UCHAR DmaCycleTimingMode; // 69
+ USHORT TranslationFieldsValid:1; // 6A
+ USHORT Reserved3:15;
+ USHORT NumberOfCurrentCylinders; // 6C
+ USHORT NumberOfCurrentHeads; // 6E
+ USHORT CurrentSectorsPerTrack; // 70
+ ULONG CurrentSectorCapacity; // 72
+ } IDENTIFY_DATA2, *PIDENTIFY_DATA2;
+
+#endif // PSI_EIDE_SCSIOP
+
+// function prototypes
+int Pci2220i_Detect (Scsi_Host_Template *tpnt);
+int Pci2220i_Command (Scsi_Cmnd *SCpnt);
+int Pci2220i_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *));
+int Pci2220i_Abort (Scsi_Cmnd *SCpnt);
+int Pci2220i_Reset (Scsi_Cmnd *SCpnt, unsigned int flags);
+int Pci2220i_BiosParam (Disk *disk, kdev_t dev, int geom[]);
+
+#ifndef NULL
+ #define NULL 0
+#endif
+
+extern struct proc_dir_entry Proc_Scsi_Pci2220i;
+
+#define PCI2220I { NULL, NULL, \
+ &Proc_Scsi_Pci2220i,/* proc_dir_entry */ \
+ NULL, \
+ "PCI-2220I EIDE Disk Controller", \
+ Pci2220i_Detect, \
+ NULL, \
+ NULL, \
+ Pci2220i_Command, \
+ Pci2220i_QueueCommand, \
+ Pci2220i_Abort, \
+ Pci2220i_Reset, \
+ NULL, \
+ Pci2220i_BiosParam, \
+ 1, \
+ -1, \
+ SG_NONE, \
+ 1, \
+ 0, \
+ 0, \
+ DISABLE_CLUSTERING }
+
+#endif
diff --git a/drivers/scsi/ppa.c b/drivers/scsi/ppa.c
index c28958703..3c2f083bd 100644
--- a/drivers/scsi/ppa.c
+++ b/drivers/scsi/ppa.c
@@ -6,369 +6,291 @@
* (c) 1995,1996 Grant R. Guenther, grant@torque.net,
* under the terms of the GNU Public License.
*
- */
-
-/* This driver was developed without the benefit of any technical
- * specifications for the interface. Instead, a modified version of
- * DOSemu was used to monitor the protocol used by the DOS driver
- * for this adapter. I have no idea how my programming model relates
- * to IOMEGA's design.
- *
- * IOMEGA's driver does not generate linked commands. I've never
- * observed a SCSI message byte in the protocol transactions, so
- * I am assuming that as long as linked commands are not used
- * we won't see any.
- *
- * For more information, see the file drivers/scsi/README.ppa.
- *
- */
-
-/*
- * this driver has been hacked by Matteo Frigo (athena@theory.lcs.mit.edu)
- * to support EPP and scatter-gather. [0.26-athena]
- *
- * additional hacks by David Campbell (campbell@tirian.che.curtin.edu.au)
- * in response to this driver "mis-behaving" on his machine.
- * Fixed EPP to handle "software" changing of EPP port data direction.
- * Chased down EPP timeouts
- * Made this driver "kernel version friendly" [0.28-athena]
- *
- * Really hacked it out of existance (David Campbell)
- * EPP timeouts no longer occur (yet better handling)
- * Probes known parallel ports
- * Autodetects EPP / ECP / PS2 / NIBBLE
- * Support for multiple devices (does anyone need this??)
- * [0.29-Curtin]
- * [ Stuff removed ]
- *
- * Modified PEDANTIC for less PEDANTIC drivers as people
- * were complaining about speed (I received a report indicating
- * that PEDANTIC is necessary for WinBond chipsets.
- * Updated config_ppa and Makefile
- * [0.36b-Curtin]
- *
- * First round of cleanups
- * Remove prior 1.3.34 kernel support
- * SMC support changed
- * ECP+EPP detection always invoked.
- * If compat mode => PS/2
- * else ecp_sync() called (ECP+EPP uses FIFO).
- * Added routine to detect interrupt channel for ECP (not used)
- * Changed version numbering
- * 1 Major number
- * 00 Minor revision number
- * ALPHA Expected stability (alpha, beta, stable)
- * [Curtin-1-00-ALPHA]
- * Second round of cleanups
- * Clean up timer queues
- * Fixed problem with non-detection of PS/2 ports
- * SMC ECP+EPP confirmed to work, remove option from config_ppa
- * [Curtin-1-01-ALPHA]
- *
- * Parport hits with a vengance!!
- * Several internal revisions have been made with huge amounts of
- * fixes including:
- * ioport_2_hostno removed (unique_id is quicker)
- * SMC compat option is history
- * Driver name / info hardwired
- * Played with inlines and saved 4k on module
- * Parport support
- * Using PnP Parport allows use of printer attached to
- * ZIP drive.
- * Numerous fixups for device registration and to allow
- * proper aborts.
- * Version jumps a few numbers here - considered BETA
- * Shipping Parport with monolithic driver :)
- * [Curtin-1-05-BETA]
- *
- * Fixed code to ensure SCSI abort will release the SCSI command
- * if the driver is STILL trying to claim the parport (PNP ver)
- * Now I have to fix the lp driver then there will NEVER be a
- * problem.
- * Got around to doing the ppa_queuecommand() clean up
- * Fixed bug relating to SMC EPP+ECP and monolithic driver
- * [Curtin-1-06-BETA]
- *
- * Where did the ppa_setup() code disappear to ??
- * Back in now...
- * Distribution of ppa now independent of parport (less work for me).
- * Also cleaned up the port detection to allow for variations on
- * IO aliasing (in an attempt to fix a few problems with some
- * machines...)
- * [Curtin-1-07-BETA]
- *
- * Rewrote detection code for monolithic driver and ported changes to
- * parport driver. Result is more stable detection of hardware and
- * better immunity to port aliasing (old XT cards).
- * Parport 0.16 (or better) is required for parport operation and
- * ECP+EPP modes, otherwise the latest parport edition is recommended.
- *
- * When using EPP and writing to disk CPU usage > 40%, while reading <10%.
- * This is due to ZIP drive IO scheduling, the drive does a verify after
- * write to ensure data integrity (removable media is ALWAYS questionable
- * since you never know where it has been).
- * Some fancy programing *MAY* fix the problem but at 30 Mb/min is just
- * over 10 sectors per jiffy.
- *
- * Hmm... I think I know a way but it will send the driver into
- * ALPHA state again.
- * [Curtin-1-08-STABLE]
+ * Current Maintainer: David Campbell (Perth, Western Australia, GMT+0800)
+ * campbell@gear.torque.net
+ * dcampbel@p01.as17.honeywell.com.au
*/
#include <linux/config.h>
/* The following #define is to avoid a clash with hosts.c */
#define PPA_CODE 1
-#include "ppa.h"
-/* batteries not included :-) */
-/*
- * modes in which the driver can operate
- */
-#define PPA_AUTODETECT 0 /* Autodetect mode */
-#define PPA_NIBBLE 1 /* work in standard 4 bit mode */
-#define PPA_PS2 2 /* PS/2 byte mode */
-#define PPA_EPP_8 3 /* EPP mode, 8 bit */
-#define PPA_EPP_16 4 /* EPP mode, 16 bit */
-#define PPA_EPP_32 5 /* EPP mode, 32 bit */
-#define PPA_UNKNOWN 6 /* Just in case... */
-
-static char *PPA_MODE_STRING[] =
-{
- "Autodetect",
- "SPP",
- "PS/2",
- "EPP 8 bit",
- "EPP 16 bit",
- "EPP 32 bit",
- "Unknown"};
+#include <linux/blk.h>
+#include <asm/io.h>
+#include <linux/parport.h>
+#include "sd.h"
+#include "hosts.h"
+int ppa_release(struct Scsi_Host *);
typedef struct {
- struct pardevice *dev; /* Parport device entry */
- int speed; /* General PPA delay constant */
- int speed_fast; /* Const for nibble/byte modes */
- int epp_speed; /* Reset time period */
- int mode; /* Transfer mode */
- int timeout; /* Number of timeouts */
- int host; /* Host number (for proc) */
- int abort_flag; /* Abort flag */
- int error_code; /* Error code */
- int ppa_failed; /* Failure flag */
- Scsi_Cmnd *cur_cmd; /* Current queued command */
- void (*done) (Scsi_Cmnd *); /* Done func for queuecommand */
- struct tq_struct ppa_tq; /* Polling interupt stuff */
- struct wait_queue *ppa_wait_q; /* Used for PnP stuff */
+ struct pardevice *dev; /* Parport device entry */
+ int base; /* Actual port address */
+ int mode; /* Transfer mode */
+ int host; /* Host number (for proc) */
+ Scsi_Cmnd *cur_cmd; /* Current queued command */
+ struct tq_struct ppa_tq; /* Polling interupt stuff */
+ unsigned long jstart; /* Jiffies at start */
+ unsigned int failed:1; /* Failure flag */
+ unsigned int p_busy:1; /* Parport sharing busy flag */
} ppa_struct;
-static void ppa_interrupt(void *data);
-/* I know that this is a mess but it works!! */
+#define PPA_EMPTY \
+{NULL, /* dev */ \
+-1, /* base */ \
+PPA_AUTODETECT, /* mode */ \
+-1, /* host */ \
+NULL, /* cur_cmd */ \
+{0, 0, ppa_interrupt, NULL}, \
+0, /* jstart */ \
+0, /* failed */ \
+0 /* p_busy */ \
+}
+
+#include "ppa.h"
+#include <linux/parport.h>
+
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#ifndef PARPORT_MODULES
+#define PARPORT_MODULES "parport_pc"
+#endif
+#endif
+
#define NO_HOSTS 4
static ppa_struct ppa_hosts[NO_HOSTS] =
-{
- {0, 6, 1, CONFIG_SCSI_PPA_EPP_TIME, PPA_AUTODETECT, 0, -1, 0, DID_ERROR, 1, NULL, NULL,
- {0, 0, ppa_interrupt, NULL}, NULL},
- {0, 6, 1, CONFIG_SCSI_PPA_EPP_TIME, PPA_AUTODETECT, 0, -1, 0, DID_ERROR, 1, NULL, NULL,
- {0, 0, ppa_interrupt, NULL}, NULL},
- {0, 6, 1, CONFIG_SCSI_PPA_EPP_TIME, PPA_AUTODETECT, 0, -1, 0, DID_ERROR, 1, NULL, NULL,
- {0, 0, ppa_interrupt, NULL}, NULL},
- {0, 6, 1, CONFIG_SCSI_PPA_EPP_TIME, PPA_AUTODETECT, 0, -1, 0, DID_ERROR, 1, NULL, NULL,
- {0, 0, ppa_interrupt, NULL}, NULL}
-};
-
-/* This is a global option */
-static int ppa_speed = -1; /* Set to >0 to act as a global value */
-static int ppa_speed_fast = -1; /* ditto.. */
-static int ppa_sg = SG_ALL; /* enable/disable scatter-gather. */
-
-/* other options */
-#define PPA_CAN_QUEUE 1 /* use "queueing" interface */
-#define PPA_BURST_SIZE 512 /* block size for bulk transfers */
-#define PPA_SELECT_TMO 5000 /* how long to wait for target ? */
-#define PPA_SPIN_TMO 500000 /* ppa_wait loop limiter */
-
-#define IN_EPP_MODE(x) (x == PPA_EPP_8 || x == PPA_EPP_16 || x == PPA_EPP_32)
-
-/* args to ppa_connect */
-#define CONNECT_EPP_MAYBE 1
-#define CONNECT_NORMAL 0
-
-#define PPA_BASE(x) ppa_hosts[(x)].dev->port->base
-
-/* Port IO - Sorry Grant but I prefer the following symbols */
-#define r_dtr(x) inb(PPA_BASE(x))
-#define r_str(x) inb(PPA_BASE(x)+1)
-#define r_ctr(x) inb(PPA_BASE(x)+2)
-#define r_epp(x) inb(PPA_BASE(x)+4)
-#define r_fifo(x) inb(PPA_BASE(x)+0x400)
-#define r_ecr(x) inb(PPA_BASE(x)+0x402)
-
-#define w_dtr(x,y) outb(y, PPA_BASE(x))
-#define w_str(x,y) outb(y, PPA_BASE(x)+1)
-#define w_ctr(x,y) outb(y, PPA_BASE(x)+2);\
- udelay( ppa_hosts[(x)].speed)
-#define w_epp(x,y) outb(y, PPA_BASE(x)+4)
-#define w_fifo(x,y) outb(y, PPA_BASE(x)+0x400)
-#define w_ecr(x,y) outb(y, PPA_BASE(x)+0x402)
-
-static void ppa_wakeup(void *ref)
-{
- ppa_struct *ppa_dev = (ppa_struct *) ref;
+{PPA_EMPTY, PPA_EMPTY, PPA_EMPTY, PPA_EMPTY};
- if (!ppa_dev->ppa_wait_q)
- return; /* Wake up whom ? */
+#define PPA_BASE(x) ppa_hosts[(x)].base
- /* Claim the Parport */
- if (parport_claim(ppa_dev->dev))
- return; /* Shouldn't happen */
+void ppa_wakeup(void *ref)
+{
+ ppa_struct *ppa_dev = (ppa_struct *) ref;
- wake_up(&ppa_dev->ppa_wait_q);
+ if (!ppa_dev->p_busy)
return;
+
+ if (parport_claim(ppa_dev->dev)) {
+ printk("ppa: bug in ppa_wakeup\n");
+ return;
+ }
+
+ ppa_dev->p_busy = 0;
+ ppa_dev->base = ppa_dev->dev->port->base;
+ if (ppa_dev->cur_cmd)
+ ppa_dev->cur_cmd->SCp.phase++;
+ return;
}
-static int ppa_release(struct Scsi_Host *host)
+int ppa_release(struct Scsi_Host *host)
{
- int host_no = host->unique_id;
+ int host_no = host->unique_id;
- printk("Releasing ppa%i\n", host_no);
- parport_unregister_device(ppa_hosts[host_no].dev);
- return 0;
+ printk("Releasing ppa%i\n", host_no);
+ parport_unregister_device(ppa_hosts[host_no].dev);
+ return 0;
}
static int ppa_pb_claim(int host_no)
{
- if (parport_claim(ppa_hosts[host_no].dev)) {
- sleep_on(&ppa_hosts[host_no].ppa_wait_q);
- ppa_hosts[host_no].ppa_wait_q = NULL;
-
- /* Check to see if we were aborted or reset */
- if (ppa_hosts[host_no].dev->port->cad !=
- ppa_hosts[host_no].dev) {
- printk("Abort detected on ppa%i\n", host_no);
- return 1;
- }
- }
- return 0;
-}
+ if (parport_claim(ppa_hosts[host_no].dev)) {
+ ppa_hosts[host_no].p_busy = 1;
+ return 1;
+ }
-static void ppa_pb_release(int host_no)
-{
- parport_release(ppa_hosts[host_no].dev);
+ PPA_BASE(host_no) = ppa_hosts[host_no].dev->port->base;
+ if (ppa_hosts[host_no].cur_cmd)
+ ppa_hosts[host_no].cur_cmd->SCp.phase++;
+ return 0;
}
+#define ppa_pb_release(x) parport_release(ppa_hosts[(x)].dev)
+
+/***************************************************************************
+ * Parallel port probing routines *
+ ***************************************************************************/
+
+#ifdef MODULE
+Scsi_Host_Template driver_template = PPA;
+#include "scsi_module.c"
+#endif
+
+/*
+ * Start of Chipset kludges
+ */
-/* Placed here so everyone knows what ecp_sync does.. */
-static void ecp_sync(int host_no)
+int ppa_detect(Scsi_Host_Template * host)
{
- int i, r;
+ struct Scsi_Host *hreg;
+ int ports;
+ int i, nhosts, try_again;
+ struct parport *pb = parport_enumerate();
+
+ printk("ppa: Version %s\n", PPA_VERSION);
+ nhosts = 0;
+ try_again = 0;
+
+#ifdef CONFIG_KERNELD
+ if (!pb) {
+ request_module(PARPORT_MODULES);
+ pb = parport_enumerate();
+ }
+#endif
- r = r_ecr(host_no);
- if ((r & 0xe0) != 0x80)
- return;
+ if (!pb) {
+ printk("ppa: parport reports no devices.\n");
+ return 0;
+ }
+ retry_entry:
+ for (i = 0; pb; i++, pb = pb->next) {
+ int modes, ppb;
- for (i = 0; i < 100; i++) {
- r = r_ecr(host_no);
- if (r & 0x01)
- return;
- udelay(5);
- }
+ ppa_hosts[i].dev =
+ parport_register_device(pb, "ppa", NULL, ppa_wakeup,
+ NULL, PARPORT_DEV_TRAN, (void *) &ppa_hosts[i]);
- printk("ppa: ECP sync failed as data still present in FIFO.\n");
-}
+ /* Claim the bus so it remembers what we do to the control
+ * registers. [ CTR and ECP ]
+ */
+ if (ppa_pb_claim(i))
+ while (ppa_hosts[i].p_busy)
+ schedule(); /* Whe can safe schedule() here */
+ ppb = PPA_BASE(i);
+ w_ctr(ppb, 0x0c);
+ modes = ppa_hosts[i].dev->port->modes;
+
+ /* Mode detection works up the chain of speed
+ * This avoids a nasty if-then-else-if-... tree
+ */
+ ppa_hosts[i].mode = PPA_NIBBLE;
-static inline void ppa_d_pulse(int host_no, char b)
-{
- w_dtr(host_no, b);
- w_ctr(host_no, 0xc);
- w_ctr(host_no, 0xe);
- w_ctr(host_no, 0xc);
- w_ctr(host_no, 0x4);
- w_ctr(host_no, 0xc);
-}
+ if (modes & PARPORT_MODE_PCPS2)
+ ppa_hosts[i].mode = PPA_PS2;
-static void ppa_disconnect(int host_no)
-{
- ppa_d_pulse(host_no, 0);
- ppa_d_pulse(host_no, 0x3c);
- ppa_d_pulse(host_no, 0x20);
- ppa_d_pulse(host_no, 0xf);
+ if (modes & PARPORT_MODE_PCECPPS2) {
+ w_ecr(ppb, 0x20);
+ ppa_hosts[i].mode = PPA_PS2;
+ }
+ if (modes & PARPORT_MODE_PCECPEPP)
+ w_ecr(ppb, 0x80);
- ppa_pb_release(host_no);
-}
+ /* Done configuration */
+ ppa_pb_release(i);
-static inline void ppa_c_pulse(int host_no, char b)
-{
- w_dtr(host_no, b);
- w_ctr(host_no, 0x4);
- w_ctr(host_no, 0x6);
- w_ctr(host_no, 0x4);
- w_ctr(host_no, 0xc);
+ if (ppa_init(i)) {
+ parport_unregister_device(ppa_hosts[i].dev);
+ continue;
+ }
+ /* now the glue ... */
+ switch (ppa_hosts[i].mode) {
+ case PPA_NIBBLE:
+ ports = 3;
+ break;
+ case PPA_PS2:
+ ports = 3;
+ break;
+ case PPA_EPP_8:
+ case PPA_EPP_16:
+ case PPA_EPP_32:
+ ports = 8;
+ break;
+ default: /* Never gets here */
+ continue;
+ }
+
+ host->can_queue = PPA_CAN_QUEUE;
+ host->sg_tablesize = ppa_sg;
+ hreg = scsi_register(host, 0);
+ hreg->io_port = pb->base;
+ hreg->n_io_port = ports;
+ hreg->dma_channel = -1;
+ hreg->unique_id = i;
+ ppa_hosts[i].host = hreg->host_no;
+ nhosts++;
+ }
+ if (nhosts == 0) {
+ if (try_again == 1)
+ return 0;
+ try_again = 1;
+ goto retry_entry;
+ } else
+ return 1; /* return number of hosts detected */
}
-static int ppa_connect(int host_no, int flag)
+/* This is to give the ppa driver a way to modify the timings (and other
+ * parameters) by writing to the /proc/scsi/ppa/0 file.
+ * Very simple method really... (To simple, no error checking :( )
+ * Reason: Kernel hackers HATE having to unload and reload modules for
+ * testing...
+ * Also gives a method to use a script to obtain optimum timings (TODO)
+ */
+
+static inline int ppa_strncmp(const char *a, const char *b, int len)
{
- int retv = ppa_pb_claim(host_no);
-
- ppa_c_pulse(host_no, 0);
- ppa_c_pulse(host_no, 0x3c);
- ppa_c_pulse(host_no, 0x20);
- if ((flag == CONNECT_EPP_MAYBE) &&
- IN_EPP_MODE(ppa_hosts[host_no].mode))
- ppa_c_pulse(host_no, 0xcf);
- else
- ppa_c_pulse(host_no, 0x8f);
+ int loop;
+ for (loop = 0; loop < len; loop++)
+ if (a[loop] != b[loop])
+ return 1;
- return retv;
+ return 0;
}
-static void ppa_do_reset(int host_no)
+static inline int ppa_proc_write(int hostno, char *buffer, int length)
{
- /*
- * SCSI reset taken from ppa_init and checked with
- * Iomega document that Grant has (via email :(
- */
- ppa_pb_claim(host_no);
- ppa_disconnect(host_no);
-
- ppa_connect(host_no, CONNECT_NORMAL);
-
- w_ctr(host_no, 0x6);
- w_ctr(host_no, 0x4);
- w_dtr(host_no, 0x40);
- w_ctr(host_no, 0x8);
- udelay(50);
- w_ctr(host_no, 0xc);
-
- ppa_disconnect(host_no);
+ unsigned long x;
+
+ if ((length > 5) && (ppa_strncmp(buffer, "mode=", 5) == 0)) {
+ x = simple_strtoul(buffer + 5, NULL, 0);
+ ppa_hosts[hostno].mode = x;
+ return length;
+ }
+ printk("ppa /proc: invalid variable\n");
+ return (-EINVAL);
}
-static char ppa_select(int host_no, int initiator, int target)
+int ppa_proc_info(char *buffer, char **start, off_t offset,
+ int length, int hostno, int inout)
{
- char r;
- int k;
+ int i;
+ int len = 0;
- r = r_str(host_no); /* TODO */
+ for (i = 0; i < 4; i++)
+ if (ppa_hosts[i].host == hostno)
+ break;
- w_dtr(host_no, (1 << target));
- w_ctr(host_no, 0xe);
- w_ctr(host_no, 0xc);
- w_dtr(host_no, (1 << initiator));
- w_ctr(host_no, 0x8);
+ if (inout)
+ return ppa_proc_write(i, buffer, length);
- k = 0;
- while (!(r = (r_str(host_no) & 0xf0)) && (k++ < PPA_SELECT_TMO))
- barrier();
+ len += sprintf(buffer + len, "Version : %s\n", PPA_VERSION);
+ len += sprintf(buffer + len, "Parport : %s\n", ppa_hosts[i].dev->port->name);
+ len += sprintf(buffer + len, "Mode : %s\n", PPA_MODE_STRING[ppa_hosts[i].mode]);
- if (k >= PPA_SELECT_TMO)
- return 0;
+ /* Request for beyond end of buffer */
+ if (offset > length)
+ return 0;
- return r;
+ *start = buffer + offset;
+ len -= offset;
+ if (len > length)
+ len = length;
+ return len;
}
-static void ppa_fail(int host_no, int error_code)
+static int device_check(int host_no);
+
+#if PPA_DEBUG > 0
+#define ppa_fail(x,y) printk("ppa: ppa_fail(%i) from %s at line %d\n",\
+ y, __FUNCTION__, __LINE__); ppa_fail_func(x,y);
+static inline void ppa_fail_func(int host_no, int error_code)
+#else
+static inline void ppa_fail(int host_no, int error_code)
+#endif
{
- ppa_hosts[host_no].error_code = error_code;
- ppa_hosts[host_no].ppa_failed = 1;
- ppa_disconnect(host_no);
+ /* If we fail a device then we trash status / message bytes */
+ if (ppa_hosts[host_no].cur_cmd) {
+ ppa_hosts[host_no].cur_cmd->result = error_code << 16;
+ ppa_hosts[host_no].failed = 1;
+ }
}
/*
@@ -378,483 +300,539 @@ static void ppa_fail(int host_no, int error_code)
* doesn't appear to be designed to support interrupts. We spin on
* the 0x80 ready bit.
*/
-static char ppa_wait(int host_no)
+static unsigned char ppa_wait(int host_no)
{
- int k;
- char r;
-
- k = 0;
- while (!((r = r_str(host_no)) & 0x80)
- && (k++ < PPA_SPIN_TMO) && !ppa_hosts[host_no].abort_flag)
- barrier();
-
- /* check if we were interrupted */
- if (ppa_hosts[host_no].abort_flag) {
- if (ppa_hosts[host_no].abort_flag == 1)
- ppa_fail(host_no, DID_ABORT);
- else {
- ppa_do_reset(host_no);
- ppa_fail(host_no, DID_RESET);
- }
- return 0;
- }
- /* check if timed out */
- if (k >= PPA_SPIN_TMO) {
- ppa_fail(host_no, DID_TIME_OUT);
- return 0; /* command timed out */
- }
- /*
- * return some status information.
- * Semantics: 0xc0 = ZIP wants more data
- * 0xd0 = ZIP wants to send more data
- * 0xf0 = end of transfer, ZIP is sending status
- */
+ int k;
+ unsigned short ppb = PPA_BASE(host_no);
+ unsigned char r;
+
+ k = PPA_SPIN_TMO;
+ do {
+ r = r_str(ppb);
+ k--;
+ udelay(1);
+ }
+ while (!(r & 0x80) && (k));
+
+ /*
+ * return some status information.
+ * Semantics: 0xc0 = ZIP wants more data
+ * 0xd0 = ZIP wants to send more data
+ * 0xe0 = ZIP is expecting SCSI command data
+ * 0xf0 = end of transfer, ZIP is sending status
+ */
+ if (k)
return (r & 0xf0);
-}
+ /* Counter expired - Time out occured */
+ ppa_fail(host_no, DID_TIME_OUT);
+ printk("ppa timeout in ppa_wait\n");
+ return 0; /* command timed out */
+}
-/*
- * This is based on a trace of what the Iomega DOS 'guest' driver does.
- * I've tried several different kinds of parallel ports with guest and
- * coded this to react in the same ways that it does.
- *
- * The return value from this function is just a hint about where the
- * handshaking failed.
- *
+/*
+ * output a string, in whatever mode is available, according to the
+ * PPA protocol.
*/
-static int ppa_init(int host_no)
+static inline void epp_reset(unsigned short ppb)
{
- if (ppa_pb_claim(host_no))
- return 1;
- ppa_disconnect(host_no);
+ int i;
- ppa_connect(host_no, CONNECT_NORMAL);
+ i = r_str(ppb);
+ w_str(ppb, i);
+ w_str(ppb, i & 0xfe);
+}
- w_ctr(host_no, 0x6);
- if ((r_str(host_no) & 0xf0) != 0xf0) {
- ppa_pb_release(host_no);
- return 2;
- }
- w_ctr(host_no, 0x4);
- if ((r_str(host_no) & 0xf0) != 0x80) {
- ppa_pb_release(host_no);
- return 3;
- }
- /* This is a SCSI reset signal */
- w_dtr(host_no, 0x40);
- w_ctr(host_no, 0x8);
- udelay(50);
- w_ctr(host_no, 0xc);
+static inline void ecp_sync(unsigned short ppb)
+{
+ int i;
- ppa_disconnect(host_no);
+ if ((r_ecr(ppb) & 0xe0) != 0x80)
+ return;
- return 0;
+ for (i = 0; i < 100; i++) {
+ if (r_ecr(ppb) & 0x01)
+ return;
+ udelay(5);
+ }
+ printk("ppa: ECP sync failed as data still present in FIFO.\n");
}
-/*
- * check the epp status. After a EPP transfer, it should be true that
- * 1) the TIMEOUT bit (SPP_STR.0) is clear
- * 2) the READY bit (SPP_STR.7) is set
+/*
+ * Here is the asm code for the SPP/PS2 protocols for the i386.
+ * This has been optimised for speed on 386/486 machines. There will
+ * be very little improvement on the current 586+ machines as it is the
+ * IO statements which will limit throughput.
*/
-static int ppa_check_epp_status(int host_no)
+#ifdef __i386__
+#define BYTE_OUT(reg) \
+ " movb " #reg ",%%al\n" \
+ " outb %%al,(%%dx)\n" \
+ " addl $2,%%edx\n" \
+ " movb $0x0e,%%al\n" \
+ " outb %%al,(%%dx)\n" \
+ " movb $0x0c,%%al\n" \
+ " outb %%al,(%%dx)\n" \
+ " subl $2,%%edx\n"
+
+static inline int ppa_byte_out(unsigned short base, char *buffer, unsigned int len)
{
- char r;
- r = r_str(host_no);
-
- if (r & 1) {
- /* EPP timeout, according to the PC87332 manual */
- /* Semantics of clearing EPP timeout bit.
- * PC87332 - reading SPP_STR does it...
- * SMC - write 1 to EPP timeout bit
- * Others - (???) write 0 to EPP timeout bit
- */
- w_str(host_no, r);
- w_str(host_no, r & 0xfe);
- ppa_hosts[host_no].timeout++;
- printk("PPA: EPP timeout on port 0x%04x\n",
- PPA_BASE(host_no));
- ppa_fail(host_no, DID_BUS_BUSY);
- return 0;
- }
- if (!(r & 0x80)) {
- ppa_fail(host_no, DID_ERROR);
- return 0;
- }
- return 1;
+ /*
+ * %eax scratch
+ * %ebx Data to transfer
+ * %ecx Counter (Don't touch!!)
+ * %edx Port
+ * %esi Source buffer (mem pointer)
+ *
+ * In case you are wondering what the last line of the asm does...
+ * <output allocation> : <input allocation> : <trashed registers>
+ */
+ asm("shr $2,%%ecx\n" \
+ " jz .no_more_bulk_bo\n" \
+ " .align 4\n" \
+ ".loop_bulk_bo:\n" \
+ " movl (%%esi),%%ebx\n" \
+ BYTE_OUT(%%bl) \
+ BYTE_OUT(%%bh) \
+ " rorl $16,%%ebx\n" \
+ BYTE_OUT(%%bl) \
+ BYTE_OUT(%%bh) \
+ " addl $4,%%esi\n" \
+ " loop .loop_bulk_bo\n" \
+ " .align 4\n" \
+ ".no_more_bulk_bo:" \
+ : "=S"(buffer): "c"(len), "d"(base), "S"(buffer):"eax", "ebx", "ecx");
+
+ asm("andl $3,%%ecx\n" \
+ " jz .no_more_loose_bo\n" \
+ " .align 4\n" \
+ ".loop_loose_bo:\n" \
+ BYTE_OUT((%%esi)) \
+ " incl %%esi\n" \
+ " loop .loop_loose_bo\n" \
+ ".no_more_loose_bo:\n" \
+ : /* no output */ : "c"(len), "d"(base), "S"(buffer):"eax", "ebx", "ecx");
+ return 1; /* All went well - we hope! */
}
-static inline int ppa_force_epp_byte(int host_no, char x)
+#define BYTE_IN(reg) \
+ " inb (%%dx),%%al\n" \
+ " movb %%al," #reg "\n" \
+ " addl $2,%%edx\n" \
+ " movb $0x27,%%al\n" \
+ " outb %%al,(%%dx)\n" \
+ " movb $0x25,%%al\n" \
+ " outb %%al,(%%dx)\n" \
+ " subl $2,%%edx\n"
+
+static inline int ppa_byte_in(unsigned short base, char *buffer, int len)
{
-/* This routine forces a byte down the EPP data port whether the
- * device is ready or not...
- */
- char r;
-
- w_epp(host_no, x);
-
- r = r_str(host_no);
- if (!(r & 1))
- return 1;
-
- if (ppa_hosts[host_no].epp_speed > 0) {
- /* EPP timeout, according to the PC87332 manual */
- /* Semantics of clearing EPP timeout bit.
- * PC87332 - reading SPP_STR does it...
- * SMC - write 1 to EPP timeout bit
- * Others - (???) write 0 to EPP timeout bit
- */
- w_str(host_no, r);
- w_str(host_no, r & 0xfe);
-
- /* Take a deep breath, count to 10 and then... */
- udelay(ppa_hosts[host_no].epp_speed);
-
- /* Second time around */
- w_epp(host_no, x);
-
- r = r_str(host_no);
- }
- if (r & 1) {
- w_str(host_no, r);
- w_str(host_no, r & 0xfe);
- ppa_hosts[host_no].timeout++;
- printk("PPA: warning: EPP timeout\n");
- ppa_fail(host_no, DID_BUS_BUSY);
- return 0;
- } else
- return 1;
+ /*
+ * %eax scratch
+ * %ebx Data to transfer
+ * %ecx Counter (Don't touch!!)
+ * %edx Port
+ * %esi Source buffer (mem pointer)
+ *
+ * In case you are wondering what the last line of the asm does...
+ * <output allocation> : <input allocation> : <trashed registers>
+ */
+ asm("shr $2,%%ecx\n" \
+ " jz .no_more_bulk_bi\n" \
+ " .align 4\n" \
+ ".loop_bulk_bi:\n" \
+ BYTE_IN(%%bl) \
+ BYTE_IN(%%bh) \
+ " rorl $16,%%ebx\n" \
+ BYTE_IN(%%bl) \
+ BYTE_IN(%%bh) \
+ " rorl $16,%%ebx\n" \
+ " movl %%ebx,(%%esi)\n" \
+ " addl $4,%%esi\n" \
+ " loop .loop_bulk_bi\n" \
+ " .align 4\n" \
+ ".no_more_bulk_bi:" \
+ : "=S"(buffer): "c"(len), "d"(base), "S"(buffer):"eax", "ebx", "ecx");
+
+ asm("andl $3,%%ecx\n" \
+ " jz .no_more_loose_bi\n" \
+ " .align 4\n" \
+ ".loop_loose_bi:\n" \
+ BYTE_IN((%%esi)) \
+ " incl %%esi\n" \
+ " loop .loop_loose_bi\n" \
+ ".no_more_loose_bi:\n" \
+ : /* no output */ : "c"(len), "d"(base), "S"(buffer):"eax", "ebx", "ecx");
+ return 1; /* All went well - we hope! */
}
-static inline int ppa_send_command_epp(Scsi_Cmnd * cmd)
+#define NIBBLE_IN(reg) \
+ " incl %%edx\n" \
+ " movb $0x04,%%al\n" \
+ " outb %%al,(%%dx)\n" \
+ " decl %%edx\n" \
+ " inb (%%dx),%%al\n" \
+ " andb $0xf0,%%al\n" \
+ " movb %%al," #reg "\n" \
+ " incl %%edx\n" \
+ " movb $0x06,%%al\n" \
+ " outb %%al,(%%dx)\n" \
+ " decl %%edx\n" \
+ " inb (%%dx),%%al\n" \
+ " shrb $4,%%al\n" \
+ " orb %%al," #reg "\n"
+
+static inline int ppa_nibble_in(unsigned short str_p, char *buffer, int len)
{
- int host_no = cmd->host->unique_id;
- int k;
+ /*
+ * %eax scratch
+ * %ebx Data to transfer
+ * %ecx Counter (Don't touch!!)
+ * %edx Port
+ * %esi Source buffer (mem pointer)
+ *
+ * In case you are wondering what the last line of the asm does...
+ * <output allocation> : <input allocation> : <trashed registers>
+ */
+ asm("shr $2,%%ecx\n" \
+ " jz .no_more_bulk_ni\n" \
+ " .align 4\n" \
+ ".loop_bulk_ni:\n" \
+ NIBBLE_IN(%%bl) \
+ NIBBLE_IN(%%bh) \
+ " rorl $16,%%ebx\n" \
+ NIBBLE_IN(%%bl) \
+ NIBBLE_IN(%%bh) \
+ " rorl $16,%%ebx\n" \
+ " movl %%ebx,(%%esi)\n" \
+ " addl $4,%%esi\n" \
+ " loop .loop_bulk_ni\n" \
+ " .align 4\n" \
+ ".no_more_bulk_ni:" \
+ : "=S"(buffer): "c"(len), "d"(str_p), "S"(buffer):"eax", "ebx", "ecx");
+
+ asm("andl $3,%%ecx\n" \
+ " jz .no_more_loose_ni\n" \
+ " .align 4\n" \
+ ".loop_loose_ni:\n" \
+ NIBBLE_IN((%%esi)) \
+ " incl %%esi\n" \
+ " loop .loop_loose_ni\n" \
+ ".no_more_loose_ni:\n" \
+ : /* no output */ : "c"(len), "d"(str_p), "S"(buffer):"eax", "ebx", "ecx");
+ return 1; /* All went well - we hope! */
+}
+#else /* Old style C routines */
- w_ctr(host_no, 0x4);
- for (k = 0; k < cmd->cmd_len; k++) { /* send the command */
- if (!ppa_force_epp_byte(host_no, cmd->cmnd[k]))
- return 0;
- }
- w_ctr(host_no, 0xc);
- ecp_sync(host_no);
- return 1;
+static inline int ppa_byte_out(unsigned short base, const char *buffer, int len)
+{
+ unsigned short ctr_p = base + 2;
+ int i;
+
+ for (i = len; i; i--) {
+ outb(*buffer++, base);
+ outb(0xe, ctr_p);
+ outb(0xc, ctr_p);
+ }
+ return 1; /* All went well - we hope! */
}
-static inline int ppa_send_command_normal(Scsi_Cmnd * cmd)
+static inline int ppa_byte_in(unsigned short base, char *buffer, int len)
{
- int host_no = cmd->host->unique_id;
- int k;
+ unsigned short ctr_p = base + 2;
+ int i;
+
+ for (i = len; i; i--) {
+ *buffer++ = inb(base);
+ outb(0x27, ctr_p);
+ outb(0x25, ctr_p);
+ }
+ return 1; /* All went well - we hope! */
+}
- w_ctr(host_no, 0xc);
+static inline int ppa_nibble_in(unsigned short str_p, char *buffer, int len)
+{
+ unsigned short ctr_p = str_p + 1;
+ unsigned char h, l;
+ int i;
+
+ for (i = len; i; i--) {
+ outb(0x4, ctr_p);
+ h = inb(str_p);
+ outb(0x6, ctr_p);
+ l = inb(str_p);
+ *buffer++ = (h & 0xf0) | ((l & 0xf0) >> 4);
+ }
+ return 1; /* All went well - we hope! */
+}
+#endif
- for (k = 0; k < cmd->cmd_len; k++) { /* send the command */
- if (!ppa_wait(host_no))
- return 0;
- w_dtr(host_no, cmd->cmnd[k]);
- w_ctr(host_no, 0xe);
- w_ctr(host_no, 0xc);
- }
- return 1;
+static inline int ppa_epp_out(unsigned short epp_p, unsigned short str_p, const char *buffer, int len)
+{
+ int i;
+ for (i = len; i; i--) {
+ outb(*buffer++, epp_p);
+#if CONFIG_SCSI_PPA_HAVE_PEDANTIC > 2
+ if (inb(str_p) & 0x01)
+ return 0;
+#endif
+ }
+ return 1;
}
-static int ppa_start(Scsi_Cmnd * cmd)
+static int ppa_out(int host_no, char *buffer, int len)
{
- int host_no = cmd->host->unique_id;
+ int r;
+ unsigned short ppb = PPA_BASE(host_no);
- /*
- * by default, the command failed,
- * unless explicitly completed in ppa_completion().
- */
- ppa_hosts[host_no].error_code = DID_ERROR;
- ppa_hosts[host_no].abort_flag = 0;
- ppa_hosts[host_no].ppa_failed = 0;
-
- if (cmd->target == PPA_INITIATOR) {
- ppa_hosts[host_no].error_code = DID_BAD_TARGET;
- ppa_hosts[host_no].ppa_failed = 1;
- return 0;
- }
- if (ppa_connect(host_no, CONNECT_EPP_MAYBE))
- return 0;
+ r = ppa_wait(host_no);
- if (!ppa_select(host_no, PPA_INITIATOR, cmd->target)) {
- ppa_fail(host_no, DID_NO_CONNECT);
- return 0;
- }
- if (IN_EPP_MODE(ppa_hosts[host_no].mode))
- return ppa_send_command_epp(cmd);
+ if ((r & 0x50) != 0x40) {
+ ppa_fail(host_no, DID_ERROR);
+ return 0;
+ }
+ switch (ppa_hosts[host_no].mode) {
+ case PPA_NIBBLE:
+ case PPA_PS2:
+ /* 8 bit output, with a loop */
+ r = ppa_byte_out(ppb, buffer, len);
+ break;
+
+ case PPA_EPP_32:
+ case PPA_EPP_16:
+ case PPA_EPP_8:
+ epp_reset(ppb);
+ w_ctr(ppb, 0x4);
+#if CONFIG_SCSI_PPA_HAVE_PEDANTIC > 1
+ r = ppa_epp_out(ppb + 4, ppb + 1, buffer, len);
+#else
+#if CONFIG_SCSI_PPA_HAVE_PEDANTIC == 0
+ if (!(((long) buffer | len) & 0x03))
+ outsl(ppb + 4, buffer, len >> 2);
else
- return ppa_send_command_normal(cmd);
+#endif
+ outsb(ppb + 4, buffer, len);
+ w_ctr(ppb, 0xc);
+ r = !(r_str(ppb) & 0x01);
+#endif
+ w_ctr(ppb, 0xc);
+ ecp_sync(ppb);
+ break;
+
+ default:
+ printk("PPA: bug in ppa_out()\n");
+ r = 0;
+ }
+ return r;
}
-/*
- * output a string, in whatever mode is available, according to the
- * PPA protocol.
- */
-static inline int ppa_outs(int host_no, char *buffer, int len)
+static inline int ppa_epp_in(int epp_p, int str_p, char *buffer, int len)
{
- int k;
-#if CONFIG_SCSI_PPA_HAVE_PEDANTIC > 0
- int r;
+ int i;
+ for (i = len; i; i--) {
+ *buffer++ = inb(epp_p);
+#if CONFIG_SCSI_PPA_HAVE_PEDANTIC > 2
+ if (inb(str_p) & 0x01)
+ return 0;
#endif
+ }
+ return 1;
+}
- switch (ppa_hosts[host_no].mode) {
- case PPA_NIBBLE:
- case PPA_PS2:
- /* 8 bit output, with a loop */
- for (k = len; k; k--) {
- w_dtr(host_no, *buffer++);
- w_ctr(host_no, 0xe);
- w_ctr(host_no, 0xc);
- }
- return 1; /* assume transfer went OK */
+static int ppa_in(int host_no, char *buffer, int len)
+{
+ int r;
+ unsigned short ppb = PPA_BASE(host_no);
-#if CONFIG_SCSI_PPA_HAVE_PEDANTIC > 0
- case PPA_EPP_32:
-#if CONFIG_SCSI_PPA_HAVE_PEDANTIC < 2
- w_ctr(host_no, 0x4);
- for (k = len; k; k -= 4) {
- w_epp(host_no, *buffer++);
- w_epp(host_no, *buffer++);
- w_epp(host_no, *buffer++);
- w_epp(host_no, *buffer++);
- r = ppa_check_epp_status(host_no);
- if (!r)
- return r;
- }
- w_ctr(host_no, 0xc);
- ecp_sync(host_no);
- return 1;
-#endif
- case PPA_EPP_16:
-#if CONFIG_SCSI_PPA_HAVE_PEDANTIC < 3
- w_ctr(host_no, 0x4);
- for (k = len; k; k -= 2) {
- w_epp(host_no, *buffer++);
- w_epp(host_no, *buffer++);
- r = ppa_check_epp_status(host_no);
- if (!r)
- return r;
- }
- w_ctr(host_no, 0xc);
- ecp_sync(host_no);
- return 1;
-#endif
- case PPA_EPP_8:
- w_ctr(host_no, 0x4);
- for (k = len; k; k--) {
- w_epp(host_no, *buffer++);
- r = ppa_check_epp_status(host_no);
- if (!r)
- return r;
- }
- w_ctr(host_no, 0xc);
- ecp_sync(host_no);
- return 1;
+ r = ppa_wait(host_no);
+
+ if ((r & 0x50) != 0x50) {
+ ppa_fail(host_no, DID_ERROR);
+ return 0;
+ }
+ switch (ppa_hosts[host_no].mode) {
+ case PPA_NIBBLE:
+ /* 4 bit input, with a loop */
+ r = ppa_nibble_in(ppb + 1, buffer, len);
+ w_ctr(ppb, 0xc);
+ break;
+
+ case PPA_PS2:
+ /* 8 bit input, with a loop */
+ w_ctr(ppb, 0x25);
+ r = ppa_byte_in(ppb, buffer, len);
+ w_ctr(ppb, 0x4);
+ w_ctr(ppb, 0xc);
+ break;
+
+ case PPA_EPP_32:
+ case PPA_EPP_16:
+ case PPA_EPP_8:
+ epp_reset(ppb);
+ w_ctr(ppb, 0x24);
+#if CONFIG_SCSI_PPA_HAVE_PEDANTIC > 1
+ r = ppa_epp_in(ppb + 4, ppb + 1, buffer, len);
#else
- case PPA_EPP_32:
- case PPA_EPP_16:
- case PPA_EPP_8:
- w_ctr(host_no, 0x4);
- switch (ppa_hosts[host_no].mode) {
- case PPA_EPP_8:
- outsb(PPA_BASE(host_no) + 0x04,
- buffer, len);
- break;
- case PPA_EPP_16:
- outsw(PPA_BASE(host_no) + 0x04,
- buffer, len / 2);
- break;
- case PPA_EPP_32:
- outsl(PPA_BASE(host_no) + 0x04,
- buffer, len / 4);
- break;
- }
- k = ppa_check_epp_status(host_no);
- w_ctr(host_no, 0xc);
- ecp_sync(host_no);
- return k;
+#if CONFIG_SCSI_PPA_HAVE_PEDANTIC == 0
+ if (!(((long) buffer | len) & 0x03))
+ insl(ppb + 4, buffer, len >> 2);
+ else
+#endif
+ insb(ppb + 4, buffer, len);
+ w_ctr(ppb, 0x2c);
+ r = !(r_str(ppb) & 0x01);
#endif
+ w_ctr(ppb, 0x2c);
+ ecp_sync(ppb);
+ break;
+
+ default:
+ printk("PPA: bug in ppa_ins()\n");
+ r = 0;
+ break;
+ }
+ return r;
+}
- default:
- printk("PPA: bug in ppa_outs()\n");
- }
- return 0;
+/* end of ppa_io.h */
+static inline void ppa_d_pulse(unsigned short ppb, unsigned char b)
+{
+ w_dtr(ppb, b);
+ w_ctr(ppb, 0xc);
+ w_ctr(ppb, 0xe);
+ w_ctr(ppb, 0xc);
+ w_ctr(ppb, 0x4);
+ w_ctr(ppb, 0xc);
}
-static inline int ppa_outb(int host_no, char d)
+static void ppa_disconnect(int host_no)
{
- int k;
+ unsigned short ppb = PPA_BASE(host_no);
- switch (ppa_hosts[host_no].mode) {
- case PPA_NIBBLE:
- case PPA_PS2:
- w_dtr(host_no, d);
- w_ctr(host_no, 0xe);
- w_ctr(host_no, 0xc);
- return 1; /* assume transfer went OK */
+ ppa_d_pulse(ppb, 0);
+ ppa_d_pulse(ppb, 0x3c);
+ ppa_d_pulse(ppb, 0x20);
+ ppa_d_pulse(ppb, 0xf);
+}
- case PPA_EPP_8:
- case PPA_EPP_16:
- case PPA_EPP_32:
- w_ctr(host_no, 0x4);
- w_epp(host_no, d);
- k = ppa_check_epp_status(host_no);
- w_ctr(host_no, 0xc);
- ecp_sync(host_no);
- return k;
-
- default:
- printk("PPA: bug in ppa_outb()\n");
- }
+static inline void ppa_c_pulse(unsigned short ppb, unsigned char b)
+{
+ w_dtr(ppb, b);
+ w_ctr(ppb, 0x4);
+ w_ctr(ppb, 0x6);
+ w_ctr(ppb, 0x4);
+ w_ctr(ppb, 0xc);
+}
+
+static inline void ppa_connect(int host_no, int flag)
+{
+ unsigned short ppb = PPA_BASE(host_no);
+
+ ppa_c_pulse(ppb, 0);
+ ppa_c_pulse(ppb, 0x3c);
+ ppa_c_pulse(ppb, 0x20);
+ if ((flag == CONNECT_EPP_MAYBE) &&
+ IN_EPP_MODE(ppa_hosts[host_no].mode))
+ ppa_c_pulse(ppb, 0xcf);
+ else
+ ppa_c_pulse(ppb, 0x8f);
+}
+
+static int ppa_select(int host_no, int target)
+{
+ int k;
+ unsigned short ppb = PPA_BASE(host_no);
+
+ /*
+ * Bit 6 (0x40) is the device selected bit.
+ * First we must wait till the current device goes off line...
+ */
+ k = PPA_SELECT_TMO;
+ do {
+ k--;
+ } while ((r_str(ppb) & 0x40) && (k));
+ if (!k)
return 0;
+
+ w_dtr(ppb, (1 << target));
+ w_ctr(ppb, 0xe);
+ w_ctr(ppb, 0xc);
+ w_dtr(ppb, 0x80); /* This is NOT the initator */
+ w_ctr(ppb, 0x8);
+
+ k = PPA_SELECT_TMO;
+ do {
+ k--;
+ }
+ while (!(r_str(ppb) & 0x40) && (k));
+ if (!k)
+ return 0;
+
+ return 1;
}
-static inline int ppa_ins(int host_no, char *buffer, int len)
+/*
+ * This is based on a trace of what the Iomega DOS 'guest' driver does.
+ * I've tried several different kinds of parallel ports with guest and
+ * coded this to react in the same ways that it does.
+ *
+ * The return value from this function is just a hint about where the
+ * handshaking failed.
+ *
+ */
+static int ppa_init(int host_no)
{
- int k, h, l;
-#if CONFIG_SCSI_PPA_HAVE_PEDANTIC > 0
- int r;
+ int retv;
+ unsigned short ppb = PPA_BASE(host_no);
+
+#if defined(CONFIG_PARPORT) || defined(CONFIG_PARPORT_MODULE)
+ if (ppa_pb_claim(host_no))
+ while (ppa_hosts[host_no].p_busy)
+ schedule(); /* Whe can safe schedule() here */
#endif
- switch (ppa_hosts[host_no].mode) {
- case PPA_NIBBLE:
- /* 4 bit input, with a loop */
- for (k = len; k; k--) {
- w_ctr(host_no, 0x4);
- h = r_str(host_no);
- w_ctr(host_no, 0x6);
- l = r_str(host_no);
- *buffer++ = ((l >> 4) & 0x0f) + (h & 0xf0);
- }
- w_ctr(host_no, 0xc);
- return 1; /* assume transfer went OK */
+ ppa_disconnect(host_no);
+ ppa_connect(host_no, CONNECT_NORMAL);
- case PPA_PS2:
- /* 8 bit input, with a loop */
- for (k = len; k; k--) {
- w_ctr(host_no, 0x25);
- *buffer++ = r_dtr(host_no);
- w_ctr(host_no, 0x27);
- }
- w_ctr(host_no, 0x5);
- w_ctr(host_no, 0x4);
- w_ctr(host_no, 0xc);
- return 1; /* assume transfer went OK */
+ retv = 2; /* Failed */
-#if CONFIG_SCSI_PPA_HAVE_PEDANTIC > 0
- case PPA_EPP_32:
-#if CONFIG_SCSI_PPA_HAVE_PEDANTIC < 2
- w_ctr(host_no, 0x24);
- for (k = len; k; k -= 4) {
- *buffer++ = r_epp(host_no);
- *buffer++ = r_epp(host_no);
- *buffer++ = r_epp(host_no);
- *buffer++ = r_epp(host_no);
- r = ppa_check_epp_status(host_no);
- if (!r)
- return r;
- }
- w_ctr(host_no, 0x2c);
- ecp_sync(host_no);
- return 1;
-#endif
- case PPA_EPP_16:
-#if CONFIG_SCSI_PPA_HAVE_PEDANTIC < 3
- w_ctr(host_no, 0x24);
- for (k = len; k; k -= 2) {
- *buffer++ = r_epp(host_no);
- *buffer++ = r_epp(host_no);
- r = ppa_check_epp_status(host_no);
- if (!r)
- return r;
- }
- w_ctr(host_no, 0x2c);
- ecp_sync(host_no);
- return 1;
-#endif
- case PPA_EPP_8:
- w_ctr(host_no, 0x24);
- for (k = len; k; k--) {
- *buffer++ = r_epp(host_no);
- r = ppa_check_epp_status(host_no);
- if (!r)
- return r;
- }
- w_ctr(host_no, 0x2c);
- ecp_sync(host_no);
- return 1;
- break;
-#else
- case PPA_EPP_8:
- case PPA_EPP_16:
- case PPA_EPP_32:
- w_ctr(host_no, 0x24);
- switch (ppa_hosts[host_no].mode) {
- case PPA_EPP_8:
- insb(PPA_BASE(host_no) + 0x04,
- buffer, len);
- break;
- case PPA_EPP_16:
- insw(PPA_BASE(host_no) + 0x04,
- buffer, len / 2);
- break;
- case PPA_EPP_32:
- insl(PPA_BASE(host_no) + 0x04,
- buffer, len / 4);
- break;
- }
- k = ppa_check_epp_status(host_no);
- w_ctr(host_no, 0x2c);
- ecp_sync(host_no);
- return k;
-#endif
+ w_ctr(ppb, 0xe);
+ if ((r_str(ppb) & 0x08) == 0x08)
+ retv--;
- default:
- printk("PPA: bug in ppa_ins()\n");
- }
- return 0;
+ w_ctr(ppb, 0xc);
+ if ((r_str(ppb) & 0x08) == 0x00)
+ retv--;
+
+ /* This is a SCSI BUS reset signal */
+ if (!retv) {
+ w_dtr(ppb, 0x40);
+ w_ctr(ppb, 0x08);
+ udelay(30);
+ w_ctr(ppb, 0x0c);
+ udelay(1000); /* Allow devices to settle down */
+ }
+ ppa_disconnect(host_no);
+ udelay(1000); /* Another delay to allow devices to settle */
+
+ if (!retv)
+ retv = device_check(host_no);
+
+ ppa_pb_release(host_no);
+ return retv;
}
-static int ppa_inb(int host_no, char *buffer)
+static inline int ppa_send_command(Scsi_Cmnd * cmd)
{
- int h, l, k;
-
- switch (ppa_hosts[host_no].mode) {
- case PPA_NIBBLE:
- /* 4 bit input */
- w_ctr(host_no, 0x4);
- h = r_str(host_no);
- w_ctr(host_no, 0x6);
- l = r_str(host_no);
- *buffer = ((l >> 4) & 0x0f) + (h & 0xf0);
- w_ctr(host_no, 0xc);
- return 1; /* assume transfer went OK */
+ int host_no = cmd->host->unique_id;
+ int k;
- case PPA_PS2:
- /* 8 bit input */
- w_ctr(host_no, 0x25);
- *buffer++ = r_dtr(host_no);
- w_ctr(host_no, 0x27);
- w_ctr(host_no, 0x5);
- w_ctr(host_no, 0x4);
- w_ctr(host_no, 0xc);
- return 1; /* assume transfer went OK */
+ w_ctr(PPA_BASE(host_no), 0x0c);
- case PPA_EPP_8:
- case PPA_EPP_16:
- case PPA_EPP_32:
- w_ctr(host_no, 0x24);
- *buffer = r_epp(host_no);
- k = ppa_check_epp_status(host_no);
- w_ctr(host_no, 0xc);
- ecp_sync(host_no);
- return k;
-
- default:
- printk("PPA: bug in ppa_inb()\n");
- }
- return 0;
+ for (k = 0; k < cmd->cmd_len; k++)
+ if (!ppa_out(host_no, &cmd->cmnd[k], 1))
+ return 0;
+ return 1;
}
/*
@@ -867,115 +845,107 @@ static int ppa_inb(int host_no, char *buffer)
*/
static int ppa_completion(Scsi_Cmnd * cmd)
{
- int host_no = cmd->host->unique_id;
-
- char r, l, h, v;
- int dir, cnt, blen, fast, bulk, status;
- char *buffer;
- struct scatterlist *sl;
- int current_segment, nsegment;
-
- v = cmd->cmnd[0];
- bulk = ((v == READ_6) ||
- (v == READ_10) ||
- (v == WRITE_6) ||
- (v == WRITE_10));
-
- /* code for scatter/gather: */
- if (cmd->use_sg) {
- /* if many buffers are available, start filling the first */
- sl = (struct scatterlist *) cmd->request_buffer;
- blen = sl->length;
- buffer = sl->address;
- } else {
- /* else fill the only available buffer */
- sl = NULL;
- buffer = cmd->request_buffer;
- blen = cmd->request_bufflen;
- }
- current_segment = 0;
- nsegment = cmd->use_sg;
-
- cnt = 0;
-
- /* detect transfer direction */
- dir = 0;
- if (!(r = ppa_wait(host_no)))
- return 0;
- if (r == (char) 0xc0)
- dir = 1; /* d0 = read c0 = write f0 = status */
-
- while (r != (char) 0xf0) {
- if (((r & 0xc0) != 0xc0) || (cnt >= blen)) {
- ppa_fail(host_no, DID_ERROR);
- return 0;
- }
- /* determine if we should use burst I/O */
- fast = (bulk && ((blen - cnt) >= PPA_BURST_SIZE) &&
- ((((long)buffer + cnt)) & 0x3) == 0);
-
- if (fast) {
- if (dir)
- status = ppa_outs(host_no, &buffer[cnt], PPA_BURST_SIZE);
- else
- status = ppa_ins(host_no, &buffer[cnt], PPA_BURST_SIZE);
- cnt += PPA_BURST_SIZE;
- } else {
- if (dir)
- status = ppa_outb(host_no, buffer[cnt]);
- else
- status = ppa_inb(host_no, &buffer[cnt]);
- cnt++;
- }
+ /* Return codes:
+ * -1 Error
+ * 0 Told to schedule
+ * 1 Finished data transfer
+ */
+ int host_no = cmd->host->unique_id;
+ unsigned short ppb = PPA_BASE(host_no);
+ unsigned long start_jiffies = jiffies;
+
+ unsigned char r, v;
+ int fast, bulk, status;
+
+ v = cmd->cmnd[0];
+ bulk = ((v == READ_6) ||
+ (v == READ_10) ||
+ (v == WRITE_6) ||
+ (v == WRITE_10));
+
+ /*
+ * We only get here if the drive is ready to comunicate,
+ * hence no need for a full ppa_wait.
+ */
+ r = (r_str(ppb) & 0xf0);
+
+ while (r != (unsigned char) 0xf0) {
+ /*
+ * If we have been running for more than a full timer tick
+ * then take a rest.
+ */
+ if (jiffies > start_jiffies + 1)
+ return 0;
- if (!status || !(r = ppa_wait(host_no)))
- return 0;
-
- if (sl && cnt == blen) {
- /* if scatter/gather, advance to the next segment */
- if (++current_segment < nsegment) {
- ++sl;
- blen = sl->length;
- buffer = sl->address;
- cnt = 0;
- }
- /*
- * the else case will be captured by the (cnt >= blen)
- * test above.
- */
- }
+ if (((r & 0xc0) != 0xc0) || (cmd->SCp.this_residual <= 0)) {
+ ppa_fail(host_no, DID_ERROR);
+ return -1; /* ERROR_RETURN */
}
+ /* determine if we should use burst I/O */ fast = (bulk && (cmd->SCp.this_residual >= PPA_BURST_SIZE))
+ ? PPA_BURST_SIZE : 1;
- /* read status and message bytes */
- if (!ppa_inb(host_no, &l)) /* read status byte */
- return 0;
- if (!(ppa_wait(host_no)))
- return 0;
- if (!ppa_inb(host_no, &h)) /* read message byte */
- return 0;
+ if (r == (unsigned char) 0xc0)
+ status = ppa_out(host_no, cmd->SCp.ptr, fast);
+ else
+ status = ppa_in(host_no, cmd->SCp.ptr, fast);
- ppa_disconnect(host_no);
+ cmd->SCp.ptr += fast;
+ cmd->SCp.this_residual -= fast;
- ppa_hosts[host_no].error_code = DID_OK;
- return (h << 8) | (l & STATUS_MASK);
+ if (!status) {
+ ppa_fail(host_no, DID_BUS_BUSY);
+ return -1; /* ERROR_RETURN */
+ }
+ if (cmd->SCp.buffer && !cmd->SCp.this_residual) {
+ /* if scatter/gather, advance to the next segment */
+ if (cmd->SCp.buffers_residual--) {
+ cmd->SCp.buffer++;
+ cmd->SCp.this_residual = cmd->SCp.buffer->length;
+ cmd->SCp.ptr = cmd->SCp.buffer->address;
+ }
+ }
+ /* Now check to see if the drive is ready to comunicate */
+ r = (r_str(ppb) & 0xf0);
+ /* If not, drop back down to the scheduler and wait a timer tick */
+ if (!(r & 0x80))
+ return 0;
+ }
+ return 1; /* FINISH_RETURN */
}
/* deprecated synchronous interface */
-
int ppa_command(Scsi_Cmnd * cmd)
{
- int host_no = cmd->host->unique_id;
- int s;
-
- sti();
- s = 0;
- if (ppa_start(cmd))
- if (ppa_wait(host_no))
- s = ppa_completion(cmd);
- return s + (ppa_hosts[host_no].error_code << 16);
+ static int first_pass = 1;
+ int host_no = cmd->host->unique_id;
+
+ if (first_pass) {
+ printk("ppa: using non-queuing interface\n");
+ first_pass = 0;
+ }
+ if (ppa_hosts[host_no].cur_cmd) {
+ printk("PPA: bug in ppa_command\n");
+ return 0;
+ }
+ ppa_hosts[host_no].failed = 0;
+ ppa_hosts[host_no].jstart = jiffies;
+ ppa_hosts[host_no].cur_cmd = cmd;
+ cmd->result = DID_ERROR << 16; /* default return code */
+ cmd->SCp.phase = 0;
+
+ ppa_pb_claim(host_no);
+
+ while (ppa_engine(&ppa_hosts[host_no], cmd))
+ schedule();
+
+ if (cmd->SCp.phase) /* Only disconnect if we have connected */
+ ppa_disconnect(cmd->host->unique_id);
+
+ ppa_pb_release(host_no);
+ ppa_hosts[host_no].cur_cmd = 0;
+ return cmd->result;
}
-/* pseudo-interrupt queueing interface */
/*
* Since the PPA itself doesn't generate interrupts, we use
* the scheduler's task queue to generate a stream of call-backs and
@@ -983,82 +953,200 @@ int ppa_command(Scsi_Cmnd * cmd)
*/
static void ppa_interrupt(void *data)
{
- ppa_struct *tmp = (ppa_struct *) data;
- Scsi_Cmnd *cmd = tmp->cur_cmd;
- void (*done) (Scsi_Cmnd *) = tmp->done;
- int host_no = cmd->host->unique_id;
-
- if (!cmd) {
- printk("PPA: bug in ppa_interrupt\n");
- return;
- }
- /* First check for any errors that may of occured
- * Here we check for internal errors
- */
- if (tmp->ppa_failed) {
- printk("PPA: ppa_failed bug: ppa_error_code = %d\n",
- tmp->error_code);
- cmd->result = DID_ERROR << 16;
- tmp->cur_cmd = 0;
- done(cmd);
- return;
+ ppa_struct *tmp = (ppa_struct *) data;
+ Scsi_Cmnd *cmd = tmp->cur_cmd;
+
+ if (!cmd) {
+ printk("PPA: bug in ppa_interrupt\n");
+ return;
+ }
+ if (ppa_engine(tmp, cmd)) {
+ tmp->ppa_tq.data = (void *) tmp;
+ tmp->ppa_tq.sync = 0;
+ queue_task(&tmp->ppa_tq, &tq_timer);
+ return;
+ }
+ /* Command must of completed hence it is safe to let go... */
+#if PPA_DEBUG > 0
+ switch ((cmd->result >> 16) & 0xff) {
+ case DID_OK:
+ break;
+ case DID_NO_CONNECT:
+ printk("ppa: no device at SCSI ID %i\n", cmd->target);
+ break;
+ case DID_BUS_BUSY:
+ printk("ppa: BUS BUSY - EPP timeout detected\n");
+ break;
+ case DID_TIME_OUT:
+ printk("ppa: unknown timeout\n");
+ break;
+ case DID_ABORT:
+ printk("ppa: told to abort\n");
+ break;
+ case DID_PARITY:
+ printk("ppa: parity error (???)\n");
+ break;
+ case DID_ERROR:
+ printk("ppa: internal driver error\n");
+ break;
+ case DID_RESET:
+ printk("ppa: told to reset device\n");
+ break;
+ case DID_BAD_INTR:
+ printk("ppa: bad interrupt (???)\n");
+ break;
+ default:
+ printk("ppa: bad return code (%02x)\n", (cmd->result >> 16) & 0xff);
+ }
+#endif
+
+ if (cmd->SCp.phase > 1)
+ ppa_disconnect(cmd->host->unique_id);
+ if (cmd->SCp.phase > 0)
+ ppa_pb_release(cmd->host->unique_id);
+ tmp->cur_cmd = 0;
+ cmd->scsi_done(cmd);
+ return;
+}
+
+static int ppa_engine(ppa_struct * tmp, Scsi_Cmnd * cmd)
+{
+ int host_no = cmd->host->unique_id;
+ unsigned short ppb = PPA_BASE(host_no);
+ unsigned char l = 0, h = 0;
+ int retv;
+
+ /* First check for any errors that may of occured
+ * Here we check for internal errors
+ */
+ if (tmp->failed)
+ return 0;
+
+ switch (cmd->SCp.phase) {
+ case 0: /* Phase 0 - Waiting for parport */
+ if ((jiffies - tmp->jstart) > HZ) {
+ /*
+ * We waited more than a second
+ * for parport to call us
+ */
+ ppa_fail(host_no, DID_BUS_BUSY);
+ return 0;
}
- /* Occasionally the mid level driver will abort a SCSI
- * command because we are taking to long, if this occurs
- * we should abort the command.
- */
- if (tmp->abort_flag) {
- ppa_disconnect(host_no);
- if (tmp->abort_flag == 1)
- cmd->result = DID_ABORT << 16;
- else {
- ppa_do_reset(host_no);
- cmd->result = DID_RESET << 16;
+ return 1; /* wait that ppa_wakeup claims parport */
+ case 1: /* Phase 1 - Connected */
+ { /* Perform a sanity check for cable unplugged */
+ int retv = 2; /* Failed */
+
+ ppa_connect(host_no, CONNECT_EPP_MAYBE);
+
+ w_ctr(ppb, 0xe);
+ if ((r_str(ppb) & 0x08) == 0x08)
+ retv--;
+
+ w_ctr(ppb, 0xc);
+ if ((r_str(ppb) & 0x08) == 0x00)
+ retv--;
+
+ if (retv)
+ if ((jiffies - tmp->jstart) > (1 * HZ)) {
+ printk("ppa: Parallel port cable is unplugged!!\n");
+ ppa_fail(host_no, DID_BUS_BUSY);
+ return 0;
+ } else {
+ ppa_disconnect(host_no);
+ return 1; /* Try again in a jiffy */
}
- tmp->cur_cmd = 0;
- done(cmd);
- return;
+ cmd->SCp.phase++;
}
- /* Check to see if the device is now free, if not
- * then throw this function onto the scheduler queue
- * to be called back in a jiffy.
- * (i386: 1 jiffy = 0.01 seconds)
- */
- if (!(r_str(host_no) & 0x80)) {
- tmp->ppa_tq.data = (void *) tmp;
- queue_task(&tmp->ppa_tq, &tq_scheduler);
- return;
+
+ case 2: /* Phase 2 - We are now talking to the scsi bus */
+ if (!ppa_select(host_no, cmd->target)) {
+ ppa_fail(host_no, DID_NO_CONNECT);
+ return 0;
}
- /* Device is now free and no errors have occured so
- * it is safe to do the data phase
- */
- cmd->result = ppa_completion(cmd) + (tmp->error_code << 16);
- tmp->cur_cmd = 0;
- done(cmd);
- return;
-}
+ cmd->SCp.phase++;
-int ppa_queuecommand(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
-{
- int host_no = cmd->host->unique_id;
+ case 3: /* Phase 3 - Ready to accept a command */
+ w_ctr(ppb, 0x0c);
+ if (!(r_str(ppb) & 0x80))
+ return 1;
+
+ if (!ppa_send_command(cmd))
+ return 0;
+ cmd->SCp.phase++;
- if (ppa_hosts[host_no].cur_cmd) {
- printk("PPA: bug in ppa_queuecommand\n");
- return 0;
+ case 4: /* Phase 4 - Setup scatter/gather buffers */
+ if (cmd->use_sg) {
+ /* if many buffers are available, start filling the first */
+ cmd->SCp.buffer = (struct scatterlist *) cmd->request_buffer;
+ cmd->SCp.this_residual = cmd->SCp.buffer->length;
+ cmd->SCp.ptr = cmd->SCp.buffer->address;
+ } else {
+ /* else fill the only available buffer */
+ cmd->SCp.buffer = NULL;
+ cmd->SCp.this_residual = cmd->request_bufflen;
+ cmd->SCp.ptr = cmd->request_buffer;
+ }
+ cmd->SCp.buffers_residual = cmd->use_sg;
+ cmd->SCp.phase++;
+
+ case 5: /* Phase 5 - Data transfer stage */
+ w_ctr(ppb, 0x0c);
+ if (!(r_str(ppb) & 0x80))
+ return 1;
+
+ retv = ppa_completion(cmd);
+ if (retv == -1)
+ return 0;
+ if (retv == 0)
+ return 1;
+ cmd->SCp.phase++;
+
+ case 6: /* Phase 6 - Read status/message */
+ cmd->result = DID_OK << 16;
+ /* Check for data overrun */
+ if (ppa_wait(host_no) != (unsigned char) 0xf0) {
+ ppa_fail(host_no, DID_ERROR);
+ return 0;
}
- sti();
- ppa_hosts[host_no].cur_cmd = cmd;
- ppa_hosts[host_no].done = done;
-
- if (!ppa_start(cmd)) {
- cmd->result = ppa_hosts[host_no].error_code << 16;
- ppa_hosts[host_no].cur_cmd = 0;
- done(cmd);
- return 0;
+ if (ppa_in(host_no, &l, 1)) { /* read status byte */
+ /* Check for optional message byte */
+ if (ppa_wait(host_no) == (unsigned char) 0xf0)
+ ppa_in(host_no, &h, 1);
+ cmd->result = (DID_OK << 16) + (h << 8) + (l & STATUS_MASK);
}
- ppa_interrupt(ppa_hosts + host_no);
+ return 0; /* Finished */
+ break;
+
+ default:
+ printk("ppa: Invalid scsi phase\n");
+ }
+ return 0;
+}
+
+int ppa_queuecommand(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
+{
+ int host_no = cmd->host->unique_id;
+ if (ppa_hosts[host_no].cur_cmd) {
+ printk("PPA: bug in ppa_queuecommand\n");
return 0;
+ }
+ ppa_hosts[host_no].failed = 0;
+ ppa_hosts[host_no].jstart = jiffies;
+ ppa_hosts[host_no].cur_cmd = cmd;
+ cmd->scsi_done = done;
+ cmd->result = DID_ERROR << 16; /* default return code */
+ cmd->SCp.phase = 0; /* bus free */
+
+ ppa_pb_claim(host_no);
+
+ ppa_hosts[host_no].ppa_tq.data = ppa_hosts + host_no;
+ ppa_hosts[host_no].ppa_tq.sync = 0;
+ queue_task(&ppa_hosts[host_no].ppa_tq, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+
+ return 0;
}
/*
@@ -1069,263 +1157,166 @@ int ppa_queuecommand(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
*/
int ppa_biosparam(Disk * disk, kdev_t dev, int ip[])
{
- ip[0] = 0x40;
- ip[1] = 0x20;
+ ip[0] = 0x40;
+ ip[1] = 0x20;
+ ip[2] = (disk->capacity + 1) / (ip[0] * ip[1]);
+ if (ip[2] > 1024) {
+ ip[0] = 0xff;
+ ip[1] = 0x3f;
ip[2] = (disk->capacity + 1) / (ip[0] * ip[1]);
- if (ip[2] > 1024) {
- ip[0] = 0xff;
- ip[1] = 0x3f;
- ip[2] = (disk->capacity + 1) / (ip[0] * ip[1]);
- if (ip[2] > 1023)
- ip[2] = 1023;
- }
- return 0;
+ if (ip[2] > 1023)
+ ip[2] = 1023;
+ }
+ return 0;
}
int ppa_abort(Scsi_Cmnd * cmd)
{
- int host_no = cmd->host->unique_id;
-
- ppa_hosts[host_no].abort_flag = 1;
- ppa_hosts[host_no].error_code = DID_ABORT;
- if (ppa_hosts[host_no].ppa_wait_q)
- wake_up(&ppa_hosts[host_no].ppa_wait_q);
-
- return SCSI_ABORT_SNOOZE;
+ /*
+ * There is no method for aborting commands since Iomega
+ * have tied the SCSI_MESSAGE line high in the interface
+ */
+
+ switch (cmd->SCp.phase) {
+ case 0: /* Do not have access to parport */
+ case 1: /* Have not connected to interface */
+ cmd->result = DID_ABORT;
+ cmd->done(cmd);
+ return SCSI_ABORT_SUCCESS;
+ break;
+ default: /* SCSI command sent, can not abort */
+ return SCSI_ABORT_BUSY;
+ break;
+ }
}
int ppa_reset(Scsi_Cmnd * cmd, unsigned int x)
{
- int host_no = cmd->host->unique_id;
-
- ppa_hosts[host_no].abort_flag = 2;
- ppa_hosts[host_no].error_code = DID_RESET;
- if (ppa_hosts[host_no].ppa_wait_q)
- wake_up(&ppa_hosts[host_no].ppa_wait_q);
-
- return SCSI_RESET_PUNT;
+ int host_no = cmd->host->unique_id;
+ int ppb = PPA_BASE(host_no);
+
+ /*
+ * PHASE1:
+ * Bring the interface crashing down on whatever is running
+ * hopefully this will kill the request.
+ * Bring back up the interface, reset the drive (and anything
+ * attached for that manner)
+ */
+ if (cmd)
+ if (cmd->SCp.phase)
+ ppa_disconnect(cmd->host->unique_id);
+
+ ppa_connect(host_no, CONNECT_NORMAL);
+ w_dtr(ppb, 0x40);
+ w_ctr(ppb, 0x8);
+ udelay(30);
+ w_ctr(ppb, 0xc);
+ udelay(1000); /* delay for devices to settle down */
+ ppa_disconnect(host_no);
+ udelay(1000); /* Additional delay to allow devices to settle down */
+
+ /*
+ * PHASE2:
+ * Sanity check for the sake of mid-level driver
+ */
+ if (!cmd) {
+ printk("ppa bus reset called for invalid command.\n");
+ return SCSI_RESET_NOT_RUNNING;
+ }
+ /*
+ * PHASE3:
+ * Flag the current command as having died due to reset
+ */
+ ppa_connect(host_no, CONNECT_NORMAL);
+ ppa_fail(host_no, DID_RESET);
+
+ /* Since the command was already on the timer queue ppa_interrupt
+ * will be called shortly.
+ */
+ return SCSI_RESET_PENDING;
}
-
-
-/***************************************************************************
- * Parallel port probing routines *
- ***************************************************************************/
-
-
-#ifdef MODULE
-Scsi_Host_Template driver_template = PPA;
-#include "scsi_module.c"
-#endif
-
-/*
- * Start of Chipset kludges
- */
-
-int ppa_detect(Scsi_Host_Template * host)
-{
- struct Scsi_Host *hreg;
- int rs;
- int ports;
- int i, nhosts;
- struct parport *pb = parport_enumerate();
-
- printk("PPA driver version: %s\n", PPA_VERSION);
- nhosts = 0;
-
- for (i = 0; pb; i++, pb=pb->next) {
- int modes = pb->modes;
-
- /* We only understand PC-style ports */
- if (modes & PARPORT_MODE_PCSPP) {
-
- /* transfer global values here */
- if (ppa_speed >= 0)
- ppa_hosts[i].speed = ppa_speed;
- if (ppa_speed_fast >= 0)
- ppa_hosts[i].speed_fast = ppa_speed_fast;
-
- ppa_hosts[i].dev = parport_register_device(pb, "ppa",
- NULL, ppa_wakeup, NULL,
- PARPORT_DEV_TRAN, (void *) &ppa_hosts[i]);
-
- /* Claim the bus so it remembers what we do to the
- * control registers. [ CTR and ECP ]
- */
- ppa_pb_claim(i);
- w_ctr(i, 0x0c);
-
- ppa_hosts[i].mode = PPA_NIBBLE;
- if (modes & (PARPORT_MODE_PCEPP | PARPORT_MODE_PCECPEPP)) {
- ppa_hosts[i].mode = PPA_EPP_32;
- printk("PPA: Parport [ PCEPP ]\n");
- } else if (modes & PARPORT_MODE_PCECP) {
- w_ecr(i, 0x20);
- ppa_hosts[i].mode = PPA_PS2;
- printk("PPA: Parport [ PCECP in PS2 submode ]\n");
- } else if (modes & PARPORT_MODE_PCPS2) {
- ppa_hosts[i].mode = PPA_PS2;
- printk("PPA: Parport [ PCPS2 ]\n");
- }
- /* Done configuration */
- ppa_pb_release(i);
-
- rs = ppa_init(i);
- if (rs) {
- parport_unregister_device(ppa_hosts[i].dev);
- continue;
- }
- /* now the glue ... */
- switch (ppa_hosts[i].mode) {
- case PPA_NIBBLE:
- case PPA_PS2:
- ports = 3;
- break;
- case PPA_EPP_8:
- case PPA_EPP_16:
- case PPA_EPP_32:
- ports = 8;
- break;
- default: /* Never gets here */
- continue;
- }
-
- host->can_queue = PPA_CAN_QUEUE;
- host->sg_tablesize = ppa_sg;
- hreg = scsi_register(host, 0);
- hreg->io_port = pb->base;
- hreg->n_io_port = ports;
- hreg->dma_channel = -1;
- hreg->unique_id = i;
- ppa_hosts[i].host = hreg->host_no;
- nhosts++;
- }
- }
- if (nhosts == 0)
- return 0;
- else
- return 1; /* return number of hosts detected */
-}
-
-/* This is to give the ppa driver a way to modify the timings (and other
- * parameters) by writing to the /proc/scsi/ppa/0 file.
- * Very simple method really... (To simple, no error checking :( )
- * Reason: Kernel hackers HATE having to unload and reload modules for
- * testing...
- * Also gives a method to use a script to obtain optimum timings (TODO)
- */
-
-static int ppa_strncmp(const char *a, const char *b, int len)
-{
- int loop;
- for (loop = 0; loop < len; loop++)
- if (a[loop] != b[loop])
- return 1;
-
- return 0;
-}
-
-static int ppa_proc_write(int hostno, char *buffer, int length)
+static int device_check(int host_no)
{
- unsigned long x;
- const char *inv_num = "ppa /proc entry passed invalid number\n";
-
- if ((length > 15) && (ppa_strncmp(buffer, "ppa_speed_fast=", 15) == 0)) {
- x = simple_strtoul(buffer + 15, NULL, 0);
- if (x <= ppa_hosts[hostno].speed)
- ppa_hosts[hostno].speed_fast = x;
- else
- printk(inv_num);
- return length;
+ /* This routine looks for a device and then attempts to use EPP
+ to send a command. If all goes as planned then EPP is available. */
+
+ static char cmd[6] =
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ int loop, old_mode, status, k, ppb = PPA_BASE(host_no);
+ unsigned char l;
+
+ old_mode = ppa_hosts[host_no].mode;
+ for (loop = 0; loop < 8; loop++) {
+ /* Attempt to use EPP for Test Unit Ready */
+ if ((ppb & 0x0007) == 0x0000)
+ ppa_hosts[host_no].mode = PPA_EPP_32;
+
+ second_pass:
+ ppa_connect(host_no, CONNECT_EPP_MAYBE);
+ /* Select SCSI device */
+ if (!ppa_select(host_no, loop)) {
+ ppa_disconnect(host_no);
+ continue;
}
- if ((length > 10) && (ppa_strncmp(buffer, "ppa_speed=", 10) == 0)) {
- x = simple_strtoul(buffer + 10, NULL, 0);
- if (x >= ppa_hosts[hostno].speed_fast)
- ppa_hosts[hostno].speed = x;
- else
- printk(inv_num);
- return length;
+ printk("ppa: Found device at ID %i, Attempting to use %s\n", loop,
+ PPA_MODE_STRING[ppa_hosts[host_no].mode]);
+
+ /* Send SCSI command */
+ status = 1;
+ w_ctr(ppb, 0x0c);
+ for (l = 0; (l < 6) && (status); l++)
+ status = ppa_out(host_no, cmd, 1);
+
+ if (!status) {
+ ppa_disconnect(host_no);
+ ppa_connect(host_no, CONNECT_EPP_MAYBE);
+ w_dtr(ppb, 0x40);
+ w_ctr(ppb, 0x08);
+ udelay(30);
+ w_ctr(ppb, 0x0c);
+ udelay(1000);
+ ppa_disconnect(host_no);
+ udelay(1000);
+ if (ppa_hosts[host_no].mode == PPA_EPP_32) {
+ ppa_hosts[host_no].mode = old_mode;
+ goto second_pass;
+ }
+ printk("ppa: Unable to establish communication, aborting driver load.\n");
+ return 1;
}
- if ((length > 10) && (ppa_strncmp(buffer, "epp_speed=", 10) == 0)) {
- x = simple_strtoul(buffer + 10, NULL, 0);
- ppa_hosts[hostno].epp_speed = x;
- return length;
+ w_ctr(ppb, 0x0c);
+ k = 1000000; /* 1 Second */
+ do {
+ l = r_str(ppb);
+ k--;
+ udelay(1);
+ } while (!(l & 0x80) && (k));
+
+ l &= 0xf0;
+
+ if (l != 0xf0) {
+ ppa_disconnect(host_no);
+ ppa_connect(host_no, CONNECT_EPP_MAYBE);
+ w_dtr(ppb, 0x40);
+ w_ctr(ppb, 0x08);
+ udelay(30);
+ w_ctr(ppb, 0x0c);
+ udelay(1000);
+ ppa_disconnect(host_no);
+ udelay(1000);
+ if (ppa_hosts[host_no].mode == PPA_EPP_32) {
+ ppa_hosts[host_no].mode = old_mode;
+ goto second_pass;
+ }
+ printk("ppa: Unable to establish communication, aborting driver load.\n");
+ return 1;
}
- if ((length > 12) && (ppa_strncmp(buffer, "epp_timeout=", 12) == 0)) {
- x = simple_strtoul(buffer + 12, NULL, 0);
- ppa_hosts[hostno].timeout = x;
- return length;
- }
- if ((length > 5) && (ppa_strncmp(buffer, "mode=", 5) == 0)) {
- x = simple_strtoul(buffer + 5, NULL, 0);
- ppa_hosts[hostno].mode = x;
- return length;
- }
- printk("ppa /proc: invalid variable\n");
- return (-EINVAL);
-}
-
-int ppa_proc_info(char *buffer, char **start, off_t offset,
- int length, int hostno, int inout)
-{
- int i;
- int size, len = 0;
- off_t begin = 0;
- off_t pos = 0;
-
- for (i = 0; i < 4; i++)
- if (ppa_hosts[i].host == hostno)
- break;
-
- if (inout)
- return ppa_proc_write(i, buffer, length);
-
- size = sprintf(buffer + len, "Version : %s\n", PPA_VERSION);
- len += size;
- pos = begin + len;
- size = sprintf(buffer + len, "Parport : %s\n",
- ppa_hosts[i].dev->port->name);
- len += size;
- pos = begin + len;
-
- size = sprintf(buffer + len, "Mode : %s\n",
- PPA_MODE_STRING[ppa_hosts[i].mode]);
- len += size;
- pos = begin + len;
-
- size = sprintf(buffer + len, "\nTiming Parameters\n");
- len += size;
- pos = begin + len;
-
- size = sprintf(buffer + len, "ppa_speed %i\n",
- ppa_hosts[i].speed);
- len += size;
- pos = begin + len;
-
- size = sprintf(buffer + len, "ppa_speed_fast %i\n",
- ppa_hosts[i].speed_fast);
- len += size;
- pos = begin + len;
-
- if (IN_EPP_MODE(ppa_hosts[i].mode)) {
- size = sprintf(buffer + len, "epp_speed %i\n",
- ppa_hosts[i].epp_speed);
- len += size;
- pos = begin + len;
-
- size = sprintf(buffer + len, "\nInternal Counters\n");
- len += size;
- pos = begin + len;
-
- size = sprintf(buffer + len, "epp_timeout %i\n",
- ppa_hosts[i].timeout);
- len += size;
- pos = begin + len;
- }
- *start = buffer + (offset + begin);
- len -= (offset - begin);
- if (len > length)
- len = length;
- return len;
+ ppa_disconnect(host_no);
+ printk("ppa: Communication established with ID %i using %s\n", loop,
+ PPA_MODE_STRING[ppa_hosts[host_no].mode]);
+ return 0;
+ }
+ printk("ppa: No devices found, aborting driver load.\n");
+ return 1;
}
-/* end of ppa.c */
diff --git a/drivers/scsi/ppa.h b/drivers/scsi/ppa.h
index cdc2fdaa6..ea86dd17e 100644
--- a/drivers/scsi/ppa.h
+++ b/drivers/scsi/ppa.h
@@ -2,30 +2,67 @@
* the Iomega ZIP drive
*
* (c) 1996 Grant R. Guenther grant@torque.net
+ * David Campbell campbell@torque.net
+ *
+ * All comments to David.
*/
#ifndef _PPA_H
#define _PPA_H
-#define PPA_VERSION "Curtin 1-08-BETA"
-
-/* This driver reqires a 1.3.37 kernel or higher!! */
+#define PPA_VERSION "1.39a"
/* Use the following to enable certain chipset support
* Default is PEDANTIC = 3
*/
-
-#include <linux/config.h>
-
#ifndef CONFIG_SCSI_PPA_HAVE_PEDANTIC
#define CONFIG_SCSI_PPA_HAVE_PEDANTIC 3
#endif
-#ifndef CONFIG_SCSI_PPA_EPP_TIME
-#define CONFIG_SCSI_PPA_EPP_TIME 64
-#endif
+/*
+ * this driver has been hacked by Matteo Frigo (athena@theory.lcs.mit.edu)
+ * to support EPP and scatter-gather. [0.26-athena]
+ *
+ * additional hacks by David Campbell
+ * in response to this driver "mis-behaving" on his machine.
+ * Fixed EPP to handle "software" changing of EPP port data direction.
+ * Chased down EPP timeouts
+ * Made this driver "kernel version friendly" [0.28-athena]
+ *
+ * [ Stuff removed ]
+ *
+ * Compiled against 2.1.53.
+ * Rebuilt ppa_abort() function, should handle unplugged cable.
+ * [1.35s]
+ *
+ * PPA now auto probes for EPP on base address which are aligned on
+ * 8 byte boundaries (0x278 & 0x378) using the attached devices.
+ * This hopefully avoids the nasty problem of trying to detect EPP.
+ * Tested on 2.1.53 [1.36]
+ *
+ * The id_probe utility no longer performs read/write tests.
+ * Additional code included for checking the Intel ECP bug
+ * (Bit 0 of STR stuck low which fools the EPP detection routine)
+ * [1.37]
+ *
+ * Oops! Got the bit sign mixed up for the Intel bug check.
+ * Found that an additional delay is required during SCSI resets
+ * to allow devices to settle down.
+ * [1.38]
+ *
+ * Fixed all problems in the parport sharing scheme. Now ppa can be safe
+ * used with lp or other parport devices on the same parallel port.
+ * 1997 by Andrea Arcangeli <arcangeli@mbox.queen.it>
+ * [1.39]
+ *
+ * Little fix in ppa engine to ensure that ppa don' t release parport
+ * or disconnect in wrong cases.
+ * 1997 by Andrea Arcangeli <arcangeli@mbox.queen.it>
+ * [1.39a]
+ */
/* ------ END OF USER CONFIGURABLE PARAMETERS ----- */
+#ifdef PPA_CODE
#include <linux/stddef.h>
#include <linux/module.h>
#include <linux/kernel.h>
@@ -35,14 +72,76 @@
#include <linux/proc_fs.h>
#include <linux/stat.h>
#include <linux/blk.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
#include <asm/io.h>
#include "sd.h"
#include "hosts.h"
-#include <linux/parport.h>
/* batteries not included :-) */
-#define PPA_INITIATOR 7
+/*
+ * modes in which the driver can operate
+ */
+#define PPA_AUTODETECT 0 /* Autodetect mode */
+#define PPA_NIBBLE 1 /* work in standard 4 bit mode */
+#define PPA_PS2 2 /* PS/2 byte mode */
+#define PPA_EPP_8 3 /* EPP mode, 8 bit */
+#define PPA_EPP_16 4 /* EPP mode, 16 bit */
+#define PPA_EPP_32 5 /* EPP mode, 32 bit */
+#define PPA_UNKNOWN 6 /* Just in case... */
+
+static char *PPA_MODE_STRING[] =
+{
+ "Autodetect",
+ "SPP",
+ "PS/2",
+ "EPP 8 bit",
+ "EPP 16 bit",
+ "EPP 32 bit",
+ "Unknown"};
+
+/* This is a global option */
+int ppa_sg = SG_ALL; /* enable/disable scatter-gather. */
+
+/* other options */
+#define PPA_CAN_QUEUE 1 /* use "queueing" interface */
+#define PPA_BURST_SIZE 512 /* data burst size */
+#define PPA_SELECT_TMO 5000 /* how long to wait for target ? */
+#define PPA_SPIN_TMO 50000 /* ppa_wait loop limiter */
+#define PPA_DEBUG 0 /* debuging option */
+#define IN_EPP_MODE(x) (x == PPA_EPP_8 || x == PPA_EPP_16 || x == PPA_EPP_32)
+
+/* args to ppa_connect */
+#define CONNECT_EPP_MAYBE 1
+#define CONNECT_NORMAL 0
+
+#define r_dtr(x) (unsigned char)inb((x))
+#define r_str(x) (unsigned char)inb((x)+1)
+#define r_ctr(x) (unsigned char)inb((x)+2)
+#define r_epp(x) (unsigned char)inb((x)+4)
+#define r_fifo(x) (unsigned char)inb((x)+0x400)
+#define r_ecr(x) (unsigned char)inb((x)+0x402)
+
+#define w_dtr(x,y) outb(y, (x))
+#define w_str(x,y) outb(y, (x)+1)
+#define w_ctr(x,y) outb(y, (x)+2)
+#define w_epp(x,y) outb(y, (x)+4)
+#define w_fifo(x,y) outb(y, (x)+0x400)
+#define w_ecr(x,y) outb(y, (x)+0x402)
+
+static int ppa_engine(ppa_struct *, Scsi_Cmnd *);
+static int ppa_in(int, char *, int);
+static int ppa_init(int);
+static void ppa_interrupt(void *);
+static int ppa_out(int, char *, int);
+
+struct proc_dir_entry proc_scsi_ppa =
+{PROC_SCSI_PPA, 3, "ppa", S_IFDIR | S_IRUGO | S_IXUGO, 2};
+#else
+extern struct proc_dir_entry proc_scsi_ppa;
+#define ppa_release 0
+#endif
int ppa_detect(Scsi_Host_Template *);
const char *ppa_info(struct Scsi_Host *);
@@ -51,25 +150,13 @@ int ppa_queuecommand(Scsi_Cmnd *, void (*done) (Scsi_Cmnd *));
int ppa_abort(Scsi_Cmnd *);
int ppa_reset(Scsi_Cmnd *, unsigned int);
int ppa_proc_info(char *, char **, off_t, int, int, int);
-int ppa_biosparam(Disk *, kdev_t, int*);
-static int ppa_release(struct Scsi_Host *);
-
-#ifndef MODULE
-#ifdef PPA_CODE
-#define SKIP_PROC_DIR
-#endif
-#endif
-
-#ifndef SKIP_PROC_DIR
-struct proc_dir_entry proc_scsi_ppa =
-{PROC_SCSI_PPA, 3, "ppa", S_IFDIR | S_IRUGO | S_IXUGO, 2};
-#endif /* !PPA_CODE => hosts.c */
+int ppa_biosparam(Disk *, kdev_t, int *);
#define PPA { /* next */ 0, \
/* usage_count */ 0, \
/* proc_dir */ &proc_scsi_ppa, \
/* proc_info */ ppa_proc_info, \
- /* name */ "Iomega ZIP/JAZ Traveller", \
+ /* name */ "Iomega parport ZIP drive", \
/* detect */ ppa_detect, \
/* release */ ppa_release, \
/* info */ 0, \
@@ -80,7 +167,7 @@ struct proc_dir_entry proc_scsi_ppa =
/* slave_attach */ 0, \
/* bios_param */ ppa_biosparam, \
/* can_queue */ 0, \
- /* this_id */ PPA_INITIATOR, \
+ /* this_id */ -1, \
/* sg_tablesize */ SG_ALL, \
/* cmd_per_lun */ 1, \
/* present */ 0, \
diff --git a/drivers/scsi/psi240i.c b/drivers/scsi/psi240i.c
new file mode 100644
index 000000000..8e12f4b23
--- /dev/null
+++ b/drivers/scsi/psi240i.c
@@ -0,0 +1,717 @@
+/*+M*************************************************************************
+ * Perceptive Solutions, Inc. PSI-240I device driver proc support for Linux.
+ *
+ * Copyright (c) 1997 Perceptive Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ * File Name: psi240i.c
+ *
+ * Description: SCSI driver for the PSI240I EIDE interface card.
+ *
+ *-M*************************************************************************/
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/head.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+
+#include "psi240i.h"
+#include "psi_chip.h"
+
+#include<linux/stat.h>
+
+struct proc_dir_entry Proc_Scsi_Psi240i =
+ { PROC_SCSI_PSI240I, 7, "psi240i", S_IFDIR | S_IRUGO | S_IXUGO, 2 };
+
+//#define DEBUG 1
+
+#ifdef DEBUG
+#define DEB(x) x
+#else
+#define DEB(x)
+#endif
+
+#define MAXBOARDS 2 /* Increase this and the sizes of the arrays below, if you need more. */
+
+#define PORT_DATA 0
+#define PORT_ERROR 1
+#define PORT_SECTOR_COUNT 2
+#define PORT_LBA_0 3
+#define PORT_LBA_8 4
+#define PORT_LBA_16 5
+#define PORT_LBA_24 6
+#define PORT_STAT_CMD 7
+#define PORT_SEL_FAIL 8
+#define PORT_IRQ_STATUS 9
+#define PORT_ADDRESS 10
+#define PORT_FAIL 11
+#define PORT_ALT_STAT 12
+
+typedef struct
+ {
+ UCHAR device; // device code
+ UCHAR byte6; // device select register image
+ UCHAR spigot; // spigot number
+ UCHAR expectingIRQ; // flag for expecting and interrupt
+ USHORT sectors; // number of sectors per track
+ USHORT heads; // number of heads
+ USHORT cylinders; // number of cylinders for this device
+ USHORT spareword; // placeholder
+ ULONG blocks; // number of blocks on device
+ } OUR_DEVICE, *POUR_DEVICE;
+
+typedef struct
+ {
+ USHORT ports[13];
+ OUR_DEVICE device[8];
+ Scsi_Cmnd *pSCmnd;
+ IDE_STRUCT ide;
+ ULONG startSector;
+ USHORT sectorCount;
+ Scsi_Cmnd *SCpnt;
+ VOID *buffer;
+ USHORT expectingIRQ;
+ } ADAPTER240I, *PADAPTER240I;
+
+#define HOSTDATA(host) ((PADAPTER240I)&host->hostdata)
+
+static struct Scsi_Host *PsiHost[6] = {NULL,}; /* One for each IRQ level (10-15) */
+static IDENTIFY_DATA identifyData;
+static SETUP ChipSetup;
+
+static USHORT portAddr[6] = {CHIP_ADRS_0, CHIP_ADRS_1, CHIP_ADRS_2, CHIP_ADRS_3, CHIP_ADRS_4, CHIP_ADRS_5};
+
+/****************************************************************
+ * Name: WriteData :LOCAL
+ *
+ * Description: Write data to device.
+ *
+ * Parameters: padapter - Pointer adapter data structure.
+ *
+ * Returns: TRUE if drive does not assert DRQ in time.
+ *
+ ****************************************************************/
+static int WriteData (PADAPTER240I padapter)
+ {
+ ULONG timer;
+ USHORT *pports = padapter->ports;
+
+ timer = jiffies + TIMEOUT_DRQ; // calculate the timeout value
+ do {
+ if ( inb_p (pports[PORT_STAT_CMD]) & IDE_STATUS_DRQ )
+ {
+ outsw (pports[PORT_DATA], padapter->buffer, (USHORT)padapter->ide.ide.ide[2] * 256);
+ return 0;
+ }
+ } while ( timer > jiffies ); // test for timeout
+
+ padapter->ide.ide.ides.cmd = 0; // null out the command byte
+ return 1;
+ }
+/****************************************************************
+ * Name: IdeCmd :LOCAL
+ *
+ * Description: Process a queued command from the SCSI manager.
+ *
+ * Parameters: padapter - Pointer adapter data structure.
+ *
+ * Returns: Zero if no error or status register contents on error.
+ *
+ ****************************************************************/
+static UCHAR IdeCmd (PADAPTER240I padapter)
+ {
+ ULONG timer;
+ USHORT *pports = padapter->ports;
+ UCHAR status;
+
+ outb_p (padapter->ide.ide.ides.spigot, pports[PORT_SEL_FAIL]); // select the spigot
+ outb_p (padapter->ide.ide.ide[6], pports[PORT_LBA_24]); // select the drive
+ timer = jiffies + TIMEOUT_READY; // calculate the timeout value
+ do {
+ status = inb_p (padapter->ports[PORT_STAT_CMD]);
+ if ( status & IDE_STATUS_DRDY )
+ {
+ outb_p (padapter->ide.ide.ide[2], pports[PORT_SECTOR_COUNT]);
+ outb_p (padapter->ide.ide.ide[3], pports[PORT_LBA_0]);
+ outb_p (padapter->ide.ide.ide[4], pports[PORT_LBA_8]);
+ outb_p (padapter->ide.ide.ide[5], pports[PORT_LBA_16]);
+ padapter->expectingIRQ = 1;
+ outb_p (padapter->ide.ide.ide[7], pports[PORT_STAT_CMD]);
+
+ if ( padapter->ide.ide.ides.cmd == IDE_CMD_WRITE_MULTIPLE )
+ return (WriteData (padapter));
+
+ return 0;
+ }
+ } while ( timer > jiffies ); // test for timeout
+
+ padapter->ide.ide.ides.cmd = 0; // null out the command byte
+ return status;
+ }
+/****************************************************************
+ * Name: SetupTransfer :LOCAL
+ *
+ * Description: Setup a data transfer command.
+ *
+ * Parameters: padapter - Pointer adapter data structure.
+ * drive - Drive/head register upper nibble only.
+ *
+ * Returns: TRUE if no data to transfer.
+ *
+ ****************************************************************/
+static int SetupTransfer (PADAPTER240I padapter, UCHAR drive)
+ {
+ if ( padapter->sectorCount )
+ {
+ *(ULONG *)padapter->ide.ide.ides.lba = padapter->startSector;
+ padapter->ide.ide.ide[6] |= drive;
+ padapter->ide.ide.ides.sectors = ( padapter->sectorCount > SECTORSXFER ) ? SECTORSXFER : padapter->sectorCount;
+ padapter->sectorCount -= padapter->ide.ide.ides.sectors; // bump the start and count for next xfer
+ padapter->startSector += padapter->ide.ide.ides.sectors;
+ return 0;
+ }
+ else
+ {
+ padapter->ide.ide.ides.cmd = 0; // null out the command byte
+ padapter->SCpnt = NULL;
+ return 1;
+ }
+ }
+/****************************************************************
+ * Name: DecodeError :LOCAL
+ *
+ * Description: Decode and process device errors.
+ *
+ * Parameters: pshost - Pointer to host data block.
+ * status - Status register code.
+ *
+ * Returns: The driver status code.
+ *
+ ****************************************************************/
+static ULONG DecodeError (struct Scsi_Host *pshost, UCHAR status)
+ {
+ PADAPTER240I padapter = HOSTDATA(pshost);
+ UCHAR error;
+
+ padapter->expectingIRQ = 0;
+ padapter->SCpnt = NULL;
+ if ( status & IDE_STATUS_WRITE_FAULT )
+ {
+ return DID_PARITY << 16;
+ }
+ if ( status & IDE_STATUS_BUSY )
+ return DID_BUS_BUSY << 16;
+
+ error = inb_p (padapter->ports[PORT_ERROR]);
+ DEB(printk ("\npsi240i error register: %x", error));
+ switch ( error )
+ {
+ case IDE_ERROR_AMNF:
+ case IDE_ERROR_TKONF:
+ case IDE_ERROR_ABRT:
+ case IDE_ERROR_IDFN:
+ case IDE_ERROR_UNC:
+ case IDE_ERROR_BBK:
+ default:
+ return DID_ERROR << 16;
+ }
+ return DID_ERROR << 16;
+ }
+/****************************************************************
+ * Name: Irq_Handler :LOCAL
+ *
+ * Description: Interrupt handler.
+ *
+ * Parameters: irq - Hardware IRQ number.
+ * dev_id -
+ * regs -
+ *
+ * Returns: TRUE if drive is not ready in time.
+ *
+ ****************************************************************/
+static void Irq_Handler (int irq, void *dev_id, struct pt_regs *regs)
+ {
+ struct Scsi_Host *shost; // Pointer to host data block
+ PADAPTER240I padapter; // Pointer to adapter control structure
+ USHORT *pports; // I/O port array
+ Scsi_Cmnd *SCpnt;
+ UCHAR status;
+ int z;
+
+ DEB(printk ("\npsi240i recieved interrupt\n"));
+
+ shost = PsiHost[irq - 10];
+ if ( !shost )
+ panic ("Splunge!");
+
+ padapter = HOSTDATA(shost);
+ pports = padapter->ports;
+ SCpnt = padapter->SCpnt;
+
+ if ( !padapter->expectingIRQ )
+ {
+ DEB(printk ("\npsi240i Unsolicited interrupt\n"));
+ return;
+ }
+ padapter->expectingIRQ = 0;
+
+ status = inb_p (padapter->ports[PORT_STAT_CMD]); // read the device status
+ if ( status & (IDE_STATUS_ERROR | IDE_STATUS_WRITE_FAULT) )
+ goto irqerror;
+
+ DEB(printk ("\npsi240i processing interrupt"));
+ switch ( padapter->ide.ide.ides.cmd ) // decide how to handle the interrupt
+ {
+ case IDE_CMD_READ_MULTIPLE:
+ if ( status & IDE_STATUS_DRQ )
+ {
+ insw (pports[PORT_DATA], padapter->buffer, (USHORT)padapter->ide.ide.ides.sectors * 256);
+ padapter->buffer += padapter->ide.ide.ides.sectors * 512;
+ if ( SetupTransfer (padapter, padapter->ide.ide.ide[6] & 0xF0) )
+ {
+ SCpnt->result = DID_OK << 16;
+ padapter->SCpnt = NULL;
+ SCpnt->scsi_done (SCpnt);
+ return;
+ }
+ if ( !(status = IdeCmd (padapter)) )
+ return;
+ }
+ break;
+
+ case IDE_CMD_WRITE_MULTIPLE:
+ padapter->buffer += padapter->ide.ide.ides.sectors * 512;
+ if ( SetupTransfer (padapter, padapter->ide.ide.ide[6] & 0xF0) )
+ {
+ SCpnt->result = DID_OK << 16;
+ padapter->SCpnt = NULL;
+ SCpnt->scsi_done (SCpnt);
+ return;
+ }
+ if ( !(status = IdeCmd (padapter)) )
+ return;
+ break;
+
+ case IDE_COMMAND_IDENTIFY:
+ {
+ PINQUIRYDATA pinquiryData = SCpnt->request_buffer;
+
+ if ( status & IDE_STATUS_DRQ )
+ {
+ insw (pports[PORT_DATA], &identifyData, sizeof (identifyData) >> 1);
+
+ memset (pinquiryData, 0, SCpnt->request_bufflen); // Zero INQUIRY data structure.
+ pinquiryData->DeviceType = 0;
+ pinquiryData->Versions = 2;
+ pinquiryData->AdditionalLength = 35 - 4;
+
+ // Fill in vendor identification fields.
+ for ( z = 0; z < 20; z += 2 )
+ {
+ pinquiryData->VendorId[z] = ((UCHAR *)identifyData.ModelNumber)[z + 1];
+ pinquiryData->VendorId[z + 1] = ((UCHAR *)identifyData.ModelNumber)[z];
+ }
+
+ // Initialize unused portion of product id.
+ for ( z = 0; z < 4; z++ )
+ pinquiryData->ProductId[12 + z] = ' ';
+
+ // Move firmware revision from IDENTIFY data to
+ // product revision in INQUIRY data.
+ for ( z = 0; z < 4; z += 2 )
+ {
+ pinquiryData->ProductRevisionLevel[z] = ((UCHAR *)identifyData.FirmwareRevision)[z + 1];
+ pinquiryData->ProductRevisionLevel[z + 1] = ((UCHAR *)identifyData.FirmwareRevision)[z];
+ }
+
+ SCpnt->result = DID_OK << 16;
+ padapter->SCpnt = NULL;
+ SCpnt->scsi_done (SCpnt);
+ return;
+ }
+ break;
+ }
+
+ default:
+ SCpnt->result = DID_OK << 16;
+ padapter->SCpnt = NULL;
+ SCpnt->scsi_done (SCpnt);
+ return;
+ }
+
+irqerror:;
+ DEB(printk ("\npsi240i error Device Status: %X\n", status));
+ SCpnt->result = DecodeError (shost, status);
+ SCpnt->scsi_done (SCpnt);
+ }
+/****************************************************************
+ * Name: Psi240i_QueueCommand
+ *
+ * Description: Process a queued command from the SCSI manager.
+ *
+ * Parameters: SCpnt - Pointer to SCSI command structure.
+ * done - Pointer to done function to call.
+ *
+ * Returns: Status code.
+ *
+ ****************************************************************/
+int Psi240i_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+ {
+ UCHAR *cdb = (UCHAR *)SCpnt->cmnd; // Pointer to SCSI CDB
+ PADAPTER240I padapter = HOSTDATA(SCpnt->host); // Pointer to adapter control structure
+ POUR_DEVICE pdev = &padapter->device[SCpnt->target];// Pointer to device information
+ UCHAR rc; // command return code
+
+ SCpnt->scsi_done = done;
+ padapter->ide.ide.ides.spigot = pdev->spigot;
+ padapter->buffer = SCpnt->request_buffer;
+ if (done)
+ {
+ if ( !pdev->device )
+ {
+ SCpnt->result = DID_BAD_TARGET << 16;
+ done (SCpnt);
+ return 0;
+ }
+ }
+ else
+ {
+ printk("psi240i_queuecommand: %02X: done can't be NULL\n", *cdb);
+ return 0;
+ }
+
+ switch ( *cdb )
+ {
+ case SCSIOP_INQUIRY: // inquiry CDB
+ {
+ padapter->ide.ide.ide[6] = pdev->byte6;
+ padapter->ide.ide.ides.cmd = IDE_COMMAND_IDENTIFY;
+ break;
+ }
+
+ case SCSIOP_TEST_UNIT_READY: // test unit ready CDB
+ SCpnt->result = DID_OK << 16;
+ done (SCpnt);
+ return 0;
+
+ case SCSIOP_READ_CAPACITY: // read capctiy CDB
+ {
+ PREAD_CAPACITY_DATA pdata = (PREAD_CAPACITY_DATA)SCpnt->request_buffer;
+
+ pdata->blksiz = 0x20000;
+ XANY2SCSI ((UCHAR *)&pdata->blks, pdev->blocks);
+ SCpnt->result = DID_OK << 16;
+ done (SCpnt);
+ return 0;
+ }
+
+ case SCSIOP_VERIFY: // verify CDB
+ *(ULONG *)padapter->ide.ide.ides.lba = XSCSI2LONG (&cdb[2]);
+ padapter->ide.ide.ide[6] |= pdev->byte6;
+ padapter->ide.ide.ide[2] = (UCHAR)((USHORT)cdb[8] | ((USHORT)cdb[7] << 8));
+ padapter->ide.ide.ides.cmd = IDE_COMMAND_VERIFY;
+ break;
+
+ case SCSIOP_READ: // read10 CDB
+ padapter->startSector = XSCSI2LONG (&cdb[2]);
+ padapter->sectorCount = (USHORT)cdb[8] | ((USHORT)cdb[7] << 8);
+ SetupTransfer (padapter, pdev->byte6);
+ padapter->ide.ide.ides.cmd = IDE_CMD_READ_MULTIPLE;
+ break;
+
+ case SCSIOP_READ6: // read6 CDB
+ padapter->startSector = SCSI2LONG (&cdb[1]);
+ padapter->sectorCount = cdb[4];
+ SetupTransfer (padapter, pdev->byte6);
+ padapter->ide.ide.ides.cmd = IDE_CMD_READ_MULTIPLE;
+ break;
+
+ case SCSIOP_WRITE: // write10 CDB
+ padapter->startSector = XSCSI2LONG (&cdb[2]);
+ padapter->sectorCount = (USHORT)cdb[8] | ((USHORT)cdb[7] << 8);
+ SetupTransfer (padapter, pdev->byte6);
+ padapter->ide.ide.ides.cmd = IDE_CMD_WRITE_MULTIPLE;
+ break;
+ case SCSIOP_WRITE6: // write6 CDB
+ padapter->startSector = SCSI2LONG (&cdb[1]);
+ padapter->sectorCount = cdb[4];
+ SetupTransfer (padapter, pdev->byte6);
+ padapter->ide.ide.ides.cmd = IDE_CMD_WRITE_MULTIPLE;
+ break;
+
+ default:
+ DEB (printk ("psi240i_queuecommand: Unsupported command %02X\n", *cdb));
+ SCpnt->result = DID_ERROR << 16;
+ done (SCpnt);
+ return 0;
+ }
+
+ padapter->SCpnt = SCpnt; // Save this command data
+
+ rc = IdeCmd (padapter);
+ if ( rc )
+ {
+ padapter->expectingIRQ = 0;
+ DEB (printk ("psi240i_queuecommand: %02X, %02X: Device failed to respond for command\n", *cdb, padapter->ide.ide.ides.cmd));
+ SCpnt->result = DID_ERROR << 16;
+ done (SCpnt);
+ return 0;
+ }
+ DEB (printk("psi240i_queuecommand: %02X, %02X now waiting for interrupt ", *cdb, padapter->ide.ide.ides.cmd));
+ return 0;
+ }
+
+static void internal_done(Scsi_Cmnd * SCpnt)
+ {
+ SCpnt->SCp.Status++;
+ }
+/****************************************************************
+ * Name: Psi240i_Command
+ *
+ * Description: Process a command from the SCSI manager.
+ *
+ * Parameters: SCpnt - Pointer to SCSI command structure.
+ *
+ * Returns: Status code.
+ *
+ ****************************************************************/
+int Psi240i_Command (Scsi_Cmnd *SCpnt)
+ {
+ DEB(printk("psi240i_command: ..calling psi240i_queuecommand\n"));
+
+ Psi240i_QueueCommand (SCpnt, internal_done);
+
+ SCpnt->SCp.Status = 0;
+ while (!SCpnt->SCp.Status)
+ barrier ();
+ return SCpnt->result;
+ }
+/***************************************************************************
+ * Name: ReadChipMemory
+ *
+ * Description: Read information from controller memory.
+ *
+ * Parameters: psetup - Pointer to memory image of setup information.
+ * base - base address of memory.
+ * length - lenght of data space in bytes.
+ * port - I/O address of data port.
+ *
+ * Returns: Nothing.
+ *
+ **************************************************************************/
+void ReadChipMemory (void *pdata, USHORT base, USHORT length, USHORT port)
+ {
+ USHORT z, zz;
+ UCHAR *pd = (UCHAR *)pdata;
+ outb_p (SEL_NONE, port + REG_SEL_FAIL); // setup data port
+ zz = 0;
+ while ( zz < length )
+ {
+ outw_p (base, port + REG_ADDRESS); // setup address
+
+ for ( z = 0; z < 8; z++ )
+ {
+ if ( (zz + z) < length )
+ *pd++ = inb_p (port + z); // read data byte
+ }
+ zz += 8;
+ base += 8;
+ }
+ }
+/****************************************************************
+ * Name: Psi240i_Detect
+ *
+ * Description: Detect and initialize our boards.
+ *
+ * Parameters: tpnt - Pointer to SCSI host template structure.
+ *
+ * Returns: Number of adapters found.
+ *
+ ****************************************************************/
+int Psi240i_Detect (Scsi_Host_Template *tpnt)
+ {
+ int board;
+ int count = 0;
+ int unit;
+ int z;
+ USHORT port;
+ CHIP_CONFIG_N chipConfig;
+ CHIP_DEVICE_N chipDevice[8];
+ struct Scsi_Host *pshost;
+ ULONG flags;
+
+ for ( board = 0; board < 6; board++ ) // scan for I/O ports
+ {
+ port = portAddr[board]; // get base address to test
+ if ( check_region (port, 16) ) // test for I/O addresses available
+ continue; // nope
+ if ( inb_p (port + REG_FAIL) != CHIP_ID ) // do the first test for likley hood that it is us
+ continue;
+ outb_p (SEL_NONE, port + REG_SEL_FAIL); // setup EEPROM/RAM access
+ outw (0, port + REG_ADDRESS); // setup EEPROM address zero
+ if ( inb_p (port) != 0x55 ) // test 1st byte
+ continue; // nope
+ if ( inb_p (port + 1) != 0xAA ) // test 2nd byte
+ continue; // nope
+
+ // at this point our board is found and can be accessed. Now we need to initialize
+ // our informatation and register with the kernel.
+
+
+ ReadChipMemory (&chipConfig, CHIP_CONFIG, sizeof (chipConfig), port);
+ ReadChipMemory (&chipDevice, CHIP_DEVICE, sizeof (chipDevice), port);
+ ReadChipMemory (&ChipSetup, CHIP_EEPROM_DATA, sizeof (ChipSetup), port);
+
+ if ( !chipConfig.numDrives ) // if no devices on this board
+ continue;
+
+ pshost = scsi_register (tpnt, sizeof(ADAPTER240I));
+
+ save_flags (flags);
+ cli ();
+ if ( request_irq (chipConfig.irq, Irq_Handler, 0, "psi240i", NULL) )
+ {
+ printk ("Unable to allocate IRQ for PSI-240I controller.\n");
+ restore_flags (flags);
+ goto unregister;
+ }
+
+ PsiHost[chipConfig.irq - 10] = pshost;
+ pshost->unique_id = port;
+ pshost->io_port = port;
+ pshost->n_io_port = 16; /* Number of bytes of I/O space used */
+ pshost->irq = chipConfig.irq;
+
+ for ( z = 0; z < 11; z++ ) // build regester address array
+ HOSTDATA(pshost)->ports[z] = port + z;
+ HOSTDATA(pshost)->ports[11] = port + REG_FAIL;
+ HOSTDATA(pshost)->ports[12] = port + REG_ALT_STAT;
+ DEB (printk ("\nPorts ="));
+ DEB (for (z=0;z<13;z++) printk(" %#04X",HOSTDATA(pshost)->ports[z]););
+
+ for ( z = 0; z < chipConfig.numDrives; ++z )
+ {
+ unit = chipDevice[z].channel & 0x0F;
+ HOSTDATA(pshost)->device[unit].device = ChipSetup.setupDevice[unit].device;
+ HOSTDATA(pshost)->device[unit].byte6 = (UCHAR)(((unit & 1) << 4) | 0xE0);
+ HOSTDATA(pshost)->device[unit].spigot = (UCHAR)(1 << (unit >> 1));
+ HOSTDATA(pshost)->device[unit].sectors = ChipSetup.setupDevice[unit].sectors;
+ HOSTDATA(pshost)->device[unit].heads = ChipSetup.setupDevice[unit].heads;
+ HOSTDATA(pshost)->device[unit].cylinders = ChipSetup.setupDevice[unit].cylinders;
+ HOSTDATA(pshost)->device[unit].blocks = ChipSetup.setupDevice[unit].blocks;
+ DEB (printk ("\nHOSTDATA->device = %X", HOSTDATA(pshost)->device[unit].device));
+ DEB (printk ("\n byte6 = %X", HOSTDATA(pshost)->device[unit].byte6));
+ DEB (printk ("\n spigot = %X", HOSTDATA(pshost)->device[unit].spigot));
+ DEB (printk ("\n sectors = %X", HOSTDATA(pshost)->device[unit].sectors));
+ DEB (printk ("\n heads = %X", HOSTDATA(pshost)->device[unit].heads));
+ DEB (printk ("\n cylinders = %X", HOSTDATA(pshost)->device[unit].cylinders));
+ DEB (printk ("\n blocks = %lX", HOSTDATA(pshost)->device[unit].blocks));
+ }
+
+ restore_flags (flags);
+ printk("\nPSI-240I EIDE CONTROLLER: at I/O = %x IRQ = %d\n", port, chipConfig.irq);
+ printk("(C) 1997 Perceptive Solutions, Inc. All rights reserved\n\n");
+ count++;
+ continue;
+
+unregister:;
+ scsi_unregister (pshost);
+ }
+ return count;
+ }
+/****************************************************************
+ * Name: Psi240i_Abort
+ *
+ * Description: Process the Abort command from the SCSI manager.
+ *
+ * Parameters: SCpnt - Pointer to SCSI command structure.
+ *
+ * Returns: Allways snooze.
+ *
+ ****************************************************************/
+int Psi240i_Abort (Scsi_Cmnd *SCpnt)
+ {
+ DEB (printk ("psi240i_abort\n"));
+ return SCSI_ABORT_SNOOZE;
+ }
+/****************************************************************
+ * Name: Psi240i_Reset
+ *
+ * Description: Process the Reset command from the SCSI manager.
+ *
+ * Parameters: SCpnt - Pointer to SCSI command structure.
+ * flags - Flags about the reset command
+ *
+ * Returns: No active command at this time, so this means
+ * that each time we got some kind of response the
+ * last time through. Tell the mid-level code to
+ * request sense information in order to decide what
+ * to do next.
+ *
+ ****************************************************************/
+int Psi240i_Reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags)
+ {
+ return SCSI_RESET_PUNT;
+ }
+
+#include "sd.h"
+
+/****************************************************************
+ * Name: Psi240i_BiosParam
+ *
+ * Description: Process the biosparam request from the SCSI manager to
+ * return C/H/S data.
+ *
+ * Parameters: disk - Pointer to SCSI disk structure.
+ * dev - Major/minor number from kernel.
+ * geom - Pointer to integer array to place geometry data.
+ *
+ * Returns: zero.
+ *
+ ****************************************************************/
+int Psi240i_BiosParam (Scsi_Disk *disk, kdev_t dev, int geom[])
+ {
+ POUR_DEVICE pdev;
+
+ pdev = &(HOSTDATA(disk->device->host)->device[disk->device->id]);
+
+ geom[0] = pdev->heads;
+ geom[1] = pdev->sectors;
+ geom[2] = pdev->cylinders;
+ return 0;
+ }
+
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = PSI240I;
+
+#include "scsi_module.c"
+#endif
+
diff --git a/drivers/scsi/psi240i.h b/drivers/scsi/psi240i.h
new file mode 100644
index 000000000..282ee1bb0
--- /dev/null
+++ b/drivers/scsi/psi240i.h
@@ -0,0 +1,344 @@
+/*+M*************************************************************************
+ * Perceptive Solutions, Inc. PSI-240I device driver proc support for Linux.
+ *
+ * Copyright (c) 1997 Perceptive Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ * File Name: psi240i.h
+ *
+ * Description: Header file for the SCSI driver for the PSI240I
+ * EIDE interface card.
+ *
+ *-M*************************************************************************/
+#ifndef _PSI240I_H
+#define _PSI240I_H
+
+#include <linux/types.h>
+#include <linux/kdev_t.h>
+
+#ifndef PSI_EIDE_SCSIOP
+#define PSI_EIDE_SCSIOP 1
+
+/************************************************/
+/* Some defines that we like */
+/************************************************/
+#define CHAR char
+#define UCHAR unsigned char
+#define SHORT short
+#define USHORT unsigned short
+#define BOOL unsigned short
+#define LONG long
+#define ULONG unsigned long
+#define VOID void
+
+/************************************************/
+/* Timeout konstants */
+/************************************************/
+#define TIMEOUT_READY 10 // 100 mSec
+#define TIMEOUT_DRQ 40 // 400 mSec
+
+/************************************************/
+/* Misc. macros */
+/************************************************/
+#define ANY2SCSI(up, p) \
+((UCHAR *)up)[0] = (((ULONG)(p)) >> 8); \
+((UCHAR *)up)[1] = ((ULONG)(p));
+
+#define SCSI2LONG(up) \
+( (((long)*(((UCHAR *)up))) << 16) \
++ (((long)(((UCHAR *)up)[1])) << 8) \
++ ((long)(((UCHAR *)up)[2])) )
+
+#define XANY2SCSI(up, p) \
+((UCHAR *)up)[0] = ((long)(p)) >> 24; \
+((UCHAR *)up)[1] = ((long)(p)) >> 16; \
+((UCHAR *)up)[2] = ((long)(p)) >> 8; \
+((UCHAR *)up)[3] = ((long)(p));
+
+#define XSCSI2LONG(up) \
+( (((long)(((UCHAR *)up)[0])) << 24) \
++ (((long)(((UCHAR *)up)[1])) << 16) \
++ (((long)(((UCHAR *)up)[2])) << 8) \
++ ((long)(((UCHAR *)up)[3])) )
+
+/************************************************/
+/* SCSI CDB operation codes */
+/************************************************/
+#define SCSIOP_TEST_UNIT_READY 0x00
+#define SCSIOP_REZERO_UNIT 0x01
+#define SCSIOP_REWIND 0x01
+#define SCSIOP_REQUEST_BLOCK_ADDR 0x02
+#define SCSIOP_REQUEST_SENSE 0x03
+#define SCSIOP_FORMAT_UNIT 0x04
+#define SCSIOP_READ_BLOCK_LIMITS 0x05
+#define SCSIOP_REASSIGN_BLOCKS 0x07
+#define SCSIOP_READ6 0x08
+#define SCSIOP_RECEIVE 0x08
+#define SCSIOP_WRITE6 0x0A
+#define SCSIOP_PRINT 0x0A
+#define SCSIOP_SEND 0x0A
+#define SCSIOP_SEEK6 0x0B
+#define SCSIOP_TRACK_SELECT 0x0B
+#define SCSIOP_SLEW_PRINT 0x0B
+#define SCSIOP_SEEK_BLOCK 0x0C
+#define SCSIOP_PARTITION 0x0D
+#define SCSIOP_READ_REVERSE 0x0F
+#define SCSIOP_WRITE_FILEMARKS 0x10
+#define SCSIOP_FLUSH_BUFFER 0x10
+#define SCSIOP_SPACE 0x11
+#define SCSIOP_INQUIRY 0x12
+#define SCSIOP_VERIFY6 0x13
+#define SCSIOP_RECOVER_BUF_DATA 0x14
+#define SCSIOP_MODE_SELECT 0x15
+#define SCSIOP_RESERVE_UNIT 0x16
+#define SCSIOP_RELEASE_UNIT 0x17
+#define SCSIOP_COPY 0x18
+#define SCSIOP_ERASE 0x19
+#define SCSIOP_MODE_SENSE 0x1A
+#define SCSIOP_START_STOP_UNIT 0x1B
+#define SCSIOP_STOP_PRINT 0x1B
+#define SCSIOP_LOAD_UNLOAD 0x1B
+#define SCSIOP_RECEIVE_DIAGNOSTIC 0x1C
+#define SCSIOP_SEND_DIAGNOSTIC 0x1D
+#define SCSIOP_MEDIUM_REMOVAL 0x1E
+#define SCSIOP_READ_CAPACITY 0x25
+#define SCSIOP_READ 0x28
+#define SCSIOP_WRITE 0x2A
+#define SCSIOP_SEEK 0x2B
+#define SCSIOP_LOCATE 0x2B
+#define SCSIOP_WRITE_VERIFY 0x2E
+#define SCSIOP_VERIFY 0x2F
+#define SCSIOP_SEARCH_DATA_HIGH 0x30
+#define SCSIOP_SEARCH_DATA_EQUAL 0x31
+#define SCSIOP_SEARCH_DATA_LOW 0x32
+#define SCSIOP_SET_LIMITS 0x33
+#define SCSIOP_READ_POSITION 0x34
+#define SCSIOP_SYNCHRONIZE_CACHE 0x35
+#define SCSIOP_COMPARE 0x39
+#define SCSIOP_COPY_COMPARE 0x3A
+#define SCSIOP_WRITE_DATA_BUFF 0x3B
+#define SCSIOP_READ_DATA_BUFF 0x3C
+#define SCSIOP_CHANGE_DEFINITION 0x40
+#define SCSIOP_READ_SUB_CHANNEL 0x42
+#define SCSIOP_READ_TOC 0x43
+#define SCSIOP_READ_HEADER 0x44
+#define SCSIOP_PLAY_AUDIO 0x45
+#define SCSIOP_PLAY_AUDIO_MSF 0x47
+#define SCSIOP_PLAY_TRACK_INDEX 0x48
+#define SCSIOP_PLAY_TRACK_RELATIVE 0x49
+#define SCSIOP_PAUSE_RESUME 0x4B
+#define SCSIOP_LOG_SELECT 0x4C
+#define SCSIOP_LOG_SENSE 0x4D
+#define SCSIOP_MODE_SELECT10 0x55
+#define SCSIOP_MODE_SENSE10 0x5A
+#define SCSIOP_LOAD_UNLOAD_SLOT 0xA6
+#define SCSIOP_MECHANISM_STATUS 0xBD
+#define SCSIOP_READ_CD 0xBE
+
+// IDE command definitions
+#define IDE_COMMAND_ATAPI_RESET 0x08
+#define IDE_COMMAND_READ 0x20
+#define IDE_COMMAND_WRITE 0x30
+#define IDE_COMMAND_RECALIBRATE 0x10
+#define IDE_COMMAND_SEEK 0x70
+#define IDE_COMMAND_SET_PARAMETERS 0x91
+#define IDE_COMMAND_VERIFY 0x40
+#define IDE_COMMAND_ATAPI_PACKET 0xA0
+#define IDE_COMMAND_ATAPI_IDENTIFY 0xA1
+#define IDE_CMD_READ_MULTIPLE 0xC4
+#define IDE_CMD_WRITE_MULTIPLE 0xC5
+#define IDE_CMD_SET_MULTIPLE 0xC6
+#define IDE_COMMAND_WRITE_DMA 0xCA
+#define IDE_COMMAND_READ_DMA 0xC8
+#define IDE_COMMAND_IDENTIFY 0xEC
+
+// IDE status definitions
+#define IDE_STATUS_ERROR 0x01
+#define IDE_STATUS_INDEX 0x02
+#define IDE_STATUS_CORRECTED_ERROR 0x04
+#define IDE_STATUS_DRQ 0x08
+#define IDE_STATUS_DSC 0x10
+#define IDE_STATUS_WRITE_FAULT 0x20
+#define IDE_STATUS_DRDY 0x40
+#define IDE_STATUS_BUSY 0x80
+
+// IDE error definitions
+#define IDE_ERROR_AMNF 0x01
+#define IDE_ERROR_TKONF 0x02
+#define IDE_ERROR_ABRT 0x04
+#define IDE_ERROR_MCR 0x08
+#define IDE_ERROR_IDFN 0x10
+#define IDE_ERROR_MC 0x20
+#define IDE_ERROR_UNC 0x40
+#define IDE_ERROR_BBK 0x80
+
+// IDE interface structure
+typedef struct _IDE_STRUCT
+ {
+ union
+ {
+ UCHAR ide[9];
+ struct
+ {
+ USHORT data;
+ UCHAR sectors;
+ UCHAR lba[4];
+ UCHAR cmd;
+ UCHAR spigot;
+ } ides;
+ } ide;
+ } IDE_STRUCT;
+
+// SCSI read capacity structure
+typedef struct _READ_CAPACITY_DATA
+ {
+ ULONG blks; /* total blocks (converted to little endian) */
+ ULONG blksiz; /* size of each (converted to little endian) */
+ } READ_CAPACITY_DATA, *PREAD_CAPACITY_DATA;
+
+// SCSI inquiry data
+typedef struct _INQUIRYDATA
+ {
+ UCHAR DeviceType :5;
+ UCHAR DeviceTypeQualifier :3;
+ UCHAR DeviceTypeModifier :7;
+ UCHAR RemovableMedia :1;
+ UCHAR Versions;
+ UCHAR ResponseDataFormat;
+ UCHAR AdditionalLength;
+ UCHAR Reserved[2];
+ UCHAR SoftReset :1;
+ UCHAR CommandQueue :1;
+ UCHAR Reserved2 :1;
+ UCHAR LinkedCommands :1;
+ UCHAR Synchronous :1;
+ UCHAR Wide16Bit :1;
+ UCHAR Wide32Bit :1;
+ UCHAR RelativeAddressing :1;
+ UCHAR VendorId[8];
+ UCHAR ProductId[16];
+ UCHAR ProductRevisionLevel[4];
+ UCHAR VendorSpecific[20];
+ UCHAR Reserved3[40];
+ } INQUIRYDATA, *PINQUIRYDATA;
+
+// IDE IDENTIFY data
+typedef struct _IDENTIFY_DATA
+ {
+ USHORT GeneralConfiguration; // 00
+ USHORT NumberOfCylinders; // 02
+ USHORT Reserved1; // 04
+ USHORT NumberOfHeads; // 06
+ USHORT UnformattedBytesPerTrack; // 08
+ USHORT UnformattedBytesPerSector; // 0A
+ USHORT SectorsPerTrack; // 0C
+ USHORT VendorUnique1[3]; // 0E
+ USHORT SerialNumber[10]; // 14
+ USHORT BufferType; // 28
+ USHORT BufferSectorSize; // 2A
+ USHORT NumberOfEccBytes; // 2C
+ USHORT FirmwareRevision[4]; // 2E
+ USHORT ModelNumber[20]; // 36
+ UCHAR MaximumBlockTransfer; // 5E
+ UCHAR VendorUnique2; // 5F
+ USHORT DoubleWordIo; // 60
+ USHORT Capabilities; // 62
+ USHORT Reserved2; // 64
+ UCHAR VendorUnique3; // 66
+ UCHAR PioCycleTimingMode; // 67
+ UCHAR VendorUnique4; // 68
+ UCHAR DmaCycleTimingMode; // 69
+ USHORT TranslationFieldsValid:1; // 6A
+ USHORT Reserved3:15;
+ USHORT NumberOfCurrentCylinders; // 6C
+ USHORT NumberOfCurrentHeads; // 6E
+ USHORT CurrentSectorsPerTrack; // 70
+ ULONG CurrentSectorCapacity; // 72
+ USHORT Reserved4[197]; // 76
+ } IDENTIFY_DATA, *PIDENTIFY_DATA;
+
+// Identify data without the Reserved4.
+typedef struct _IDENTIFY_DATA2 {
+ USHORT GeneralConfiguration; // 00
+ USHORT NumberOfCylinders; // 02
+ USHORT Reserved1; // 04
+ USHORT NumberOfHeads; // 06
+ USHORT UnformattedBytesPerTrack; // 08
+ USHORT UnformattedBytesPerSector; // 0A
+ USHORT SectorsPerTrack; // 0C
+ USHORT VendorUnique1[3]; // 0E
+ USHORT SerialNumber[10]; // 14
+ USHORT BufferType; // 28
+ USHORT BufferSectorSize; // 2A
+ USHORT NumberOfEccBytes; // 2C
+ USHORT FirmwareRevision[4]; // 2E
+ USHORT ModelNumber[20]; // 36
+ UCHAR MaximumBlockTransfer; // 5E
+ UCHAR VendorUnique2; // 5F
+ USHORT DoubleWordIo; // 60
+ USHORT Capabilities; // 62
+ USHORT Reserved2; // 64
+ UCHAR VendorUnique3; // 66
+ UCHAR PioCycleTimingMode; // 67
+ UCHAR VendorUnique4; // 68
+ UCHAR DmaCycleTimingMode; // 69
+ USHORT TranslationFieldsValid:1; // 6A
+ USHORT Reserved3:15;
+ USHORT NumberOfCurrentCylinders; // 6C
+ USHORT NumberOfCurrentHeads; // 6E
+ USHORT CurrentSectorsPerTrack; // 70
+ ULONG CurrentSectorCapacity; // 72
+ } IDENTIFY_DATA2, *PIDENTIFY_DATA2;
+
+#endif // PSI_EIDE_SCSIOP
+
+// function prototypes
+int Psi240i_Detect (Scsi_Host_Template *tpnt);
+int Psi240i_Command (Scsi_Cmnd *SCpnt);
+int Psi240i_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *));
+int Psi240i_Abort (Scsi_Cmnd *SCpnt);
+int Psi240i_Reset (Scsi_Cmnd *SCpnt, unsigned int flags);
+int Psi240i_BiosParam (Disk *disk, kdev_t dev, int geom[]);
+
+#ifndef NULL
+ #define NULL 0
+#endif
+
+extern struct proc_dir_entry Proc_Scsi_Psi240i;
+
+#define PSI240I { NULL, NULL, \
+ &Proc_Scsi_Psi240i,/* proc_dir_entry */ \
+ NULL, \
+ "PSI-240I EIDE Disk Controller", \
+ Psi240i_Detect, \
+ NULL, \
+ NULL, \
+ Psi240i_Command, \
+ Psi240i_QueueCommand, \
+ Psi240i_Abort, \
+ Psi240i_Reset, \
+ NULL, \
+ Psi240i_BiosParam, \
+ 1, \
+ -1, \
+ SG_NONE, \
+ 1, \
+ 0, \
+ 0, \
+ DISABLE_CLUSTERING }
+
+#endif
diff --git a/drivers/scsi/psi_chip.h b/drivers/scsi/psi_chip.h
new file mode 100644
index 000000000..89bc64e6b
--- /dev/null
+++ b/drivers/scsi/psi_chip.h
@@ -0,0 +1,195 @@
+/*+M*************************************************************************
+ * Perceptive Solutions, Inc. PSI-240I device driver proc support for Linux.
+ *
+ * Copyright (c) 1997 Perceptive Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ * File Name: psi_chip.h
+ *
+ * Description: This file contains the interface defines and
+ * error codes.
+ *
+ *-M*************************************************************************/
+#ifndef PSI_CHIP
+#define PSI_CHIP
+
+/************************************************/
+/* Misc konstants */
+/************************************************/
+#define CHIP_MAXDRIVES 8
+
+/************************************************/
+/* Chip I/O addresses */
+/************************************************/
+#define CHIP_ADRS_0 0x0130
+#define CHIP_ADRS_1 0x0150
+#define CHIP_ADRS_2 0x0190
+#define CHIP_ADRS_3 0x0210
+#define CHIP_ADRS_4 0x0230
+#define CHIP_ADRS_5 0x0250
+
+/************************************************/
+/* EEPROM locations */
+/************************************************/
+#define CHIP_EEPROM_BIOS 0x0000 // BIOS base address
+#define CHIP_EEPROM_DATA 0x2000 // SETUP data base address
+#define CHIP_EEPROM_FACTORY 0x2400 // FACTORY data base address
+#define CHIP_EEPROM_SETUP 0x3000 // SETUP PROGRAM base address
+
+#define CHIP_EEPROM_SIZE 32768U // size of the entire EEPROM
+#define CHIP_EEPROM_BIOS_SIZE 8192 // size of the BIOS in bytes
+#define CHIP_EEPROM_DATA_SIZE 4096 // size of factory, setup, log data block in bytes
+#define CHIP_EEPROM_SETUP_SIZE 20480U // size of the setup program in bytes
+
+/************************************************/
+/* Chip Interrupts */
+/************************************************/
+#define CHIP_IRQ_10 0x72
+#define CHIP_IRQ_11 0x73
+#define CHIP_IRQ_12 0x74
+
+/************************************************/
+/* Chip Setup addresses */
+/************************************************/
+#define CHIP_SETUP_BASE 0x0000C000L
+
+/************************************************/
+/* Chip Register address offsets */
+/************************************************/
+#define REG_DATA 0x00
+#define REG_ERROR 0x01
+#define REG_SECTOR_COUNT 0x02
+#define REG_LBA_0 0x03
+#define REG_LBA_8 0x04
+#define REG_LBA_16 0x05
+#define REG_LBA_24 0x06
+#define REG_STAT_CMD 0x07
+#define REG_SEL_FAIL 0x08
+#define REG_IRQ_STATUS 0x09
+#define REG_ADDRESS 0x0A
+#define REG_FAIL 0x0C
+#define REG_ALT_STAT 0x0E
+#define REG_DRIVE_ADRS 0x0F
+
+/************************************************/
+/* Chip RAM locations */
+/************************************************/
+#define CHIP_DEVICE 0x8000
+#define CHIP_DEVICE_0 0x8000
+#define CHIP_DEVICE_1 0x8008
+#define CHIP_DEVICE_2 0x8010
+#define CHIP_DEVICE_3 0x8018
+#define CHIP_DEVICE_4 0x8020
+#define CHIP_DEVICE_5 0x8028
+#define CHIP_DEVICE_6 0x8030
+#define CHIP_DEVICE_7 0x8038
+typedef struct
+ {
+ UCHAR channel; // channel of this device (0-8).
+ UCHAR spt; // Sectors Per Track.
+ ULONG spc; // Sectors Per Cylinder.
+ } CHIP_DEVICE_N;
+
+#define CHIP_CONFIG 0x8100 // address of boards configuration.
+typedef struct
+ {
+ UCHAR irq; // interrupt request channel number
+ UCHAR numDrives; // Number of accessable drives
+ UCHAR fastFormat; // Boolean for fast format enable
+ } CHIP_CONFIG_N;
+
+#define CHIP_MAP 0x8108 // eight byte device type map.
+
+
+#define CHIP_RAID 0x8120 // array of RAID signature structures and LBA
+#define CHIP_RAID_1 0x8120
+#define CHIP_RAID_2 0x8130
+#define CHIP_RAID_3 0x8140
+#define CHIP_RAID_4 0x8150
+
+/************************************************/
+/* Chip Register Masks */
+/************************************************/
+#define CHIP_ID 0x7B
+#define SEL_RAM 0x8000
+#define MASK_FAIL 0x80
+
+/************************************************/
+/* Chip cable select bits */
+/************************************************/
+#define SECTORSXFER 8
+
+/************************************************/
+/* Chip cable select bits */
+/************************************************/
+#define SEL_NONE 0x00
+#define SEL_1 0x01
+#define SEL_2 0x02
+#define SEL_3 0x04
+#define SEL_4 0x08
+
+/************************************************/
+/* Programmable Interrupt Controller*/
+/************************************************/
+#define PIC1 0x20 // first 8259 base port address
+#define PIC2 0xA0 // second 8259 base port address
+#define INT_OCW1 1 // Operation Control Word 1: IRQ mask
+#define EOI 0x20 // non-specific end-of-interrupt
+
+/************************************************/
+/* Device/Geometry controls */
+/************************************************/
+#define GEOMETRY_NONE 0x0 // No device
+#define GEOMETRY_AUTO 0x1 // Geometry set automatically
+#define GEOMETRY_USER 0x2 // User supplied geometry
+
+#define DEVICE_NONE 0x0 // No device present
+#define DEVICE_INACTIVE 0x1 // device present but not registered active
+#define DEVICE_ATAPI 0x2 // ATAPI device (CD_ROM, Tape, Etc...)
+#define DEVICE_DASD_NONLBA 0x3 // Non LBA incompatible device
+#define DEVICE_DASD_LBA 0x4 // LBA compatible device
+
+/************************************************/
+/* Setup Structure Definitions */
+/************************************************/
+typedef struct // device setup parameters
+ {
+ UCHAR geometryControl; // geometry control flags
+ UCHAR device; // device code
+ USHORT sectors; // number of sectors per track
+ USHORT heads; // number of heads
+ USHORT cylinders; // number of cylinders for this device
+ ULONG blocks; // number of blocks on device
+ USHORT spare1;
+ USHORT spare2;
+ } SETUP_DEVICE, *PSETUP_DEVICE;
+
+typedef struct // master setup structure
+ {
+ USHORT startupDelay;
+ USHORT promptBIOS;
+ USHORT fastFormat;
+ USHORT spare2;
+ USHORT spare3;
+ USHORT spare4;
+ USHORT spare5;
+ USHORT spare6;
+ SETUP_DEVICE setupDevice[8];
+ } SETUP, *PSETUP;
+
+#endif
+
diff --git a/drivers/scsi/psi_dale.h b/drivers/scsi/psi_dale.h
new file mode 100644
index 000000000..d8a407f3d
--- /dev/null
+++ b/drivers/scsi/psi_dale.h
@@ -0,0 +1,187 @@
+/*+M*************************************************************************
+ * Perceptive Solutions, Inc. PCI-2000 device driver proc support for Linux.
+ *
+ * Copyright (c) 1997 Perceptive Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ * File Name: psi_dale.h
+ *
+ * Description: This file contains the interface defines and
+ * error codes.
+ *
+ *-M*************************************************************************/
+
+#ifndef PSI_DALE
+#define PSI_DALE
+
+/************************************************/
+/* Dale PCI setup */
+/************************************************/
+#define VENDOR_PSI 0x1256
+#define DEVICE_DALE_1 0x4401 /* 'D1' */
+
+/************************************************/
+/* Misc konstants */
+/************************************************/
+#define DALE_MAXDRIVES 4
+#define SECTORSXFER 8
+#define BYTES_PER_SECTOR 512
+#define DEFAULT_TIMING_MODE 5
+
+/************************************************/
+/* EEPROM locations */
+/************************************************/
+#define DALE_FLASH_PAGE_SIZE 128 // number of bytes per page
+#define DALE_FLASH_SIZE 65536L
+
+#define DALE_FLASH_BIOS 0x00080000L // BIOS base address
+#define DALE_FLASH_SETUP 0x00088000L // SETUP PROGRAM base address offset from BIOS
+#define DALE_FLASH_RAID 0x00088400L // RAID signature storage
+#define DALE_FLASH_FACTORY 0x00089000L // FACTORY data base address offset from BIOS
+
+#define DALE_FLASH_BIOS_SIZE 32768U // size of FLASH BIOS REGION
+
+/************************************************/
+/* DALE Register address offsets */
+/************************************************/
+#define REG_DATA 0x80
+#define REG_ERROR 0x84
+#define REG_SECTOR_COUNT 0x88
+#define REG_LBA_0 0x8C
+#define REG_LBA_8 0x90
+#define REG_LBA_16 0x94
+#define REG_LBA_24 0x98
+#define REG_STAT_CMD 0x9C
+#define REG_STAT_SEL 0xA0
+#define REG_FAIL 0xB0
+#define REG_ALT_STAT 0xB8
+#define REG_DRIVE_ADRS 0xBC
+
+#define DALE_DATA_SLOW 0x00040000L
+#define DALE_DATA_MODE2 0x00040000L
+#define DALE_DATA_MODE3 0x00050000L
+#define DALE_DATA_MODE4 0x00060000L
+#define DALE_DATA_MODE4P 0x00070000L
+
+#define RTR_LOCAL_RANGE 0x000
+#define RTR_LOCAL_REMAP 0x004
+#define RTR_EXP_RANGE 0x010
+#define RTR_EXP_REMAP 0x014
+#define RTR_REGIONS 0x018
+#define RTR_DM_MASK 0x01C
+#define RTR_DM_LOCAL_BASE 0x020
+#define RTR_DM_IO_BASE 0x024
+#define RTR_DM_PCI_REMAP 0x028
+#define RTR_DM_IO_CONFIG 0x02C
+#define RTR_MAILBOX 0x040
+#define RTR_LOCAL_DOORBELL 0x060
+#define RTR_PCI_DOORBELL 0x064
+#define RTR_INT_CONTROL_STATUS 0x068
+#define RTR_EEPROM_CONTROL_STATUS 0x06C
+
+#define RTL_DMA0_MODE 0x00
+#define RTL_DMA0_PCI_ADDR 0x04
+#define RTL_DMA0_LOCAL_ADDR 0x08
+#define RTL_DMA0_COUNT 0x0C
+#define RTL_DMA0_DESC_PTR 0x10
+#define RTL_DMA1_MODE 0x14
+#define RTL_DMA1_PCI_ADDR 0x18
+#define RTL_DMA1_LOCAL_ADDR 0x1C
+#define RTL_DMA1_COUNT 0x20
+#define RTL_DMA1_DESC_PTR 0x24
+#define RTL_DMA_COMMAND_STATUS 0x28
+#define RTL_DMA_ARB0 0x2C
+#define RTL_DMA_ARB1 0x30
+
+/************************************************/
+/* Dale Scratchpad locations */
+/************************************************/
+#define DALE_CHANNEL_DEVICE_0 0 // device channel locations
+#define DALE_CHANNEL_DEVICE_1 1
+#define DALE_CHANNEL_DEVICE_2 2
+#define DALE_CHANNEL_DEVICE_3 3
+
+#define DALE_SCRATH_DEVICE_0 4 // device type codes
+#define DALE_SCRATH_DEVICE_1 5
+#define DALE_SCRATH_DEVICE_2 6
+#define DALE_SCRATH_DEVICE_3 7
+
+#define DALE_RAID_0_STATUS 8
+#define DALE_RAID_1_STATUS 9
+
+#define DALE_TIMING_MODE 12 // bus master timing mode (2, 3, 4, 5)
+#define DALE_NUM_DRIVES 13 // number of addressable drives on this board
+#define DALE_RAID_ON 14 // RAID status On
+#define DALE_LAST_ERROR 15 // Last error code from BIOS
+
+/************************************************/
+/* Dale cable select bits */
+/************************************************/
+#define SEL_NONE 0x00
+#define SEL_1 0x01
+#define SEL_2 0x02
+
+/************************************************/
+/* Programmable Interrupt Controller */
+/************************************************/
+#define PIC1 0x20 // first 8259 base port address
+#define PIC2 0xA0 // second 8259 base port address
+#define INT_OCW1 1 // Operation Control Word 1: IRQ mask
+#define EOI 0x20 // non-specific end-of-interrupt
+
+/************************************************/
+/* Device/Geometry controls */
+/************************************************/
+#define GEOMETRY_NONE 0x0 // No device
+#define GEOMETRY_SET 0x1 // Geometry set
+#define GEOMETRY_LBA 0x2 // Geometry set in default LBA mode
+#define GEOMETRY_PHOENIX 0x3 // Geometry set in Pheonix BIOS compatibility mode
+
+#define DEVICE_NONE 0x0 // No device present
+#define DEVICE_INACTIVE 0x1 // device present but not registered active
+#define DEVICE_ATAPI 0x2 // ATAPI device (CD_ROM, Tape, Etc...)
+#define DEVICE_DASD_NONLBA 0x3 // Non LBA incompatible device
+#define DEVICE_DASD_LBA 0x4 // LBA compatible device
+
+/************************************************/
+/* Setup Structure Definitions */
+/************************************************/
+typedef struct // device setup parameters
+ {
+ UCHAR geometryControl; // geometry control flags
+ UCHAR device; // device code
+ USHORT sectors; // number of sectors per track
+ USHORT heads; // number of heads
+ USHORT cylinders; // number of cylinders for this device
+ ULONG blocks; // number of blocks on device
+ ULONG realCapacity; // number of real blocks on this device for drive changed testing
+ } SETUP_DEVICE, *PSETUP_DEVICE;
+
+typedef struct // master setup structure
+ {
+ USHORT startupDelay;
+ BOOL promptBIOS;
+ BOOL fastFormat;
+ BOOL shareInterrupt;
+ BOOL rebootRebuil;
+ USHORT timingMode;
+ USHORT spare5;
+ USHORT spare6;
+ SETUP_DEVICE setupDevice[4];
+ } SETUP, *PSETUP;
+
+#endif
diff --git a/drivers/scsi/psi_roy.h b/drivers/scsi/psi_roy.h
new file mode 100644
index 000000000..36a0341a9
--- /dev/null
+++ b/drivers/scsi/psi_roy.h
@@ -0,0 +1,314 @@
+/*+M*************************************************************************
+ * Perceptive Solutions, Inc. PCI-2000 device driver proc support for Linux.
+ *
+ * Copyright (c) 1997 Perceptive Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ * File Name: psi_roy.h
+ *
+ * Description: This file contains the host interface command and
+ * error codes.
+ *
+ *-M*************************************************************************/
+
+#ifndef ROY_HOST
+#define ROY_HOST
+
+/************************************************/
+/* PCI setup */
+/************************************************/
+#define VENDOR_PSI 0x1256
+#define DEVICE_ROY_1 0x5201 /* 'R1' */
+
+/************************************************/
+/* controller constants */
+/************************************************/
+#define MAXADAPTER 4 // Increase this and the sizes of the arrays below, if you need more.
+#define MAX_BUS 2
+#define MAX_UNITS 16
+#define TIMEOUT_COMMAND 30 // number of jiffies for command busy timeout
+
+/************************************************/
+/* I/O address offsets */
+/************************************************/
+#define RTR_MAILBOX 0x040
+#define RTR_LOCAL_DOORBELL 0x060
+#define RTR_PCI_DOORBELL 0x064
+
+/************************************************/
+/* */
+/* Host command codes */
+/* */
+/************************************************/
+#define CMD_READ_CHS 0x01 /* read sectors as specified (CHS mode) */
+#define CMD_READ 0x02 /* read sectors as specified (RBA mode) */
+#define CMD_READ_SG 0x03 /* read sectors using scatter/gather list */
+#define CMD_WRITE_CHS 0x04 /* write sectors as specified (CHS mode) */
+#define CMD_WRITE 0x05 /* write sectors as specified (RBA mode) */
+#define CMD_WRITE_SG 0x06 /* write sectors using scatter/gather list (LBA mode) */
+#define CMD_READ_CHS_SG 0x07 /* read sectors using scatter/gather list (CHS mode) */
+#define CMD_WRITE_CHS_SG 0x08 /* write sectors using scatter/gather list (CHS mode) */
+#define CMD_VERIFY_CHS 0x09 /* verify data on sectors as specified (CHS mode) */
+#define CMD_VERIFY 0x0A /* verify data on sectors as specified (RBA mode) */
+
+#define CMD_READ_ABS 0x10 /* read absolute disk */
+#define CMD_WRITE_ABS 0x11 /* write absolute disk */
+#define CMD_VERIFY_ABS 0x12 /* verify absolute disk */
+#define CMD_TEST_READY 0x13 /* test unit ready and return status code */
+#define CMD_LOCK_DOOR 0x14 /* lock device door */
+#define CMD_UNLOCK_DOOR 0x15 /* unlock device door */
+#define CMD_EJECT_MEDIA 0x16 /* eject the media */
+#define CMD_UPDATE_CAP 0x17 /* update capacity information */
+#define CMD_TEST_PRIV 0x18 /* test and setup private format media */
+
+
+#define CMD_SCSI_THRU 0x30 /* SCSI pass through CDB */
+#define CMD_SCSI_THRU_SG 0x31 /* SCSI pass through CDB with scatter/gather */
+#define CMD_SCSI_REQ_SENSE 0x32 /* SCSI pass through request sense after check condition */
+
+#define CMD_DASD_SCSI_INQ 0x36 /* to DASD inquire for DASD info in SCSI inquiry format */
+#define CMD_DASD_CAP 0x37 /* read DASD capacity */
+#define CMD_DASD_INQ 0x38 /* do DASD inquire for type data */
+#define CMD_SCSI_INQ 0x39 /* do SCSI inquire */
+#define CMD_READ_SETUP 0x3A /* Get setup structures from controller */
+#define CMD_WRITE_SETUP 0x3B /* Put setup structures in controller and burn in flash */
+#define CMD_READ_CONFIG 0x3C /* Get the entire configuration and setup structures */
+#define CMD_WRITE_CONFIG 0x3D /* Put the entire configuration and setup structures in flash */
+
+#define CMD_TEXT_DEVICE 0x3E /* obtain device text */
+#define CMD_TEXT_SIGNON 0x3F /* get sign on banner */
+
+#define CMD_QUEUE 0x40 /* any command below this generates a queue tag interrupt to host*/
+
+#define CMD_PREFETCH 0x40 /* prefetch sectors as specified */
+#define CMD_TEST_WRITE 0x41 /* Test a device for write protect */
+#define CMD_LAST_STATUS 0x42 /* get last command status and error data*/
+#define CMD_ABORT 0x43 /* abort command as specified */
+#define CMD_ERROR 0x44 /* fetch error code from a tagged op */
+#define CMD_DONE 0x45 /* done with operation */
+#define CMD_DIAGNOSTICS 0x46 /* execute controller diagnostics and wait for results */
+#define CMD_FEATURE_MODE 0x47 /* feature mode control word */
+#define CMD_DASD_INQUIRE 0x48 /* inquire as to DASD SCSI device (32 possible) */
+#define CMD_FEATURE_QUERY 0x49 /* query the feature control word */
+#define CMD_DASD_EJECT 0x4A /* Eject removable media for DASD type */
+#define CMD_DASD_LOCK 0x4B /* Lock removable media for DASD type */
+#define CMD_DASD_TYPE 0x4C /* obtain DASD device type */
+#define CMD_NUM_DEV 0x4D /* obtain the number of devices connected to the controller */
+#define CMD_GET_PARMS 0x4E /* obtain device parameters */
+#define CMD_SPECIFY 0x4F /* specify operating system for scatter/gather operations */
+
+#define CMD_RAID_GET_DEV 0x50 /* read RAID device geometry */
+#define CMD_RAID_READ 0x51 /* read RAID 1 parameter block */
+#define CMD_RAID_WRITE 0x52 /* write RAID 1 parameter block */
+#define CMD_RAID_LITEUP 0x53 /* Light up the drive light for identification */
+#define CMD_RAID_REBUILD 0x54 /* issue a RAID 1 pair rebuild */
+#define CMD_RAID_MUTE 0x55 /* mute RAID failure alarm */
+#define CMD_RAID_FAIL 0x56 /* induce a RAID failure */
+#define CMD_RAID_STATUS 0x57 /* get status of RAID pair */
+#define CMD_RAID_STOP 0x58 /* stop any reconstruct in progress */
+#define CMD_RAID_START 0x59 /* start reconstruct */
+
+#define CMD_SCSI_GET 0x60 /* get SCSI pass through devices */
+#define CMD_SCSI_TIMEOUT 0x61 /* set SCSI pass through timeout */
+#define CMD_SCSI_ERROR 0x62 /* get SCSI pass through request sense length and residual data count */
+#define CMD_GET_SPARMS 0x63 /* get SCSI bus and user parms */
+#define CMD_SCSI_ABORT 0x64 /* abort by setting time-out to zero */
+
+#define CMD_CHIRP_CHIRP 0x77 /* make a chirp chirp sound */
+#define CMD_GET_LAST_DONE 0x78 /* get tag of last done in progress */
+#define CMD_GET_FEATURES 0x79 /* get feature code and ESN */
+#define CMD_CLEAR_CACHE 0x7A /* Clear cache on specified device */
+#define CMD_BIOS_TEST 0x7B /* Test whether or not to load BIOS */
+#define CMD_WAIT_FLUSH 0x7C /* wait for cache flushed and invalidate read cache */
+#define CMD_RESET_BUS 0x7D /* reset the SCSI bus */
+#define CMD_STARTUP_QRY 0x7E /* startup in progress query */
+#define CMD_RESET 0x7F /* reset the controller */
+
+#define CMD_RESTART_RESET 0x80 /* reload and restart the controller at any reset issued */
+#define CMD_SOFT_RESET 0x81 /* do a soft reset NOW! */
+
+/************************************************/
+/* */
+/* Host return errors */
+/* */
+/************************************************/
+#define ERR08_TAGGED 0x80 /* doorbell error ored with tag */
+
+#define ERR16_NONE 0x0000 /* no errors */
+#define ERR16_SC_COND_MET 0x0004 /* SCSI status - Condition Met */
+#define ERR16_CMD 0x0101 /* command error */
+#define ERR16_SC_CHECK_COND 0x0002 /* SCSI status - Check Condition */
+#define ERR16_CMD_NOT 0x0201 /* command not supported */
+#define ERR16_NO_DEVICE 0x0301 /* invalid device selection */
+#define ERR16_SECTOR 0x0202 /* bad sector */
+#define ERR16_PROTECT 0x0303 /* write protected */
+#define ERR16_NOSECTOR 0x0404 /* sector not found */
+#define ERR16_MEDIA 0x0C0C /* invalid media */
+#define ERR16_CONTROL 0x2020 /* controller error */
+#define ERR16_CONTROL_DMA 0x2120 /* controller DMA engine error */
+#define ERR16_NO_ALARM 0x2220 /* alarm is not active */
+#define ERR16_OP_BUSY 0x2320 /* operation busy */
+#define ERR16_SEEK 0x4040 /* seek failure */
+#define ERR16_DEVICE_FAIL 0x4140 /* device has failed */
+#define ERR16_TIMEOUT 0x8080 /* timeout error */
+#define ERR16_DEV_NOT_READY 0xAAAA /* drive not ready */
+#define ERR16_UNDEFINED 0xBBBB /* undefined error */
+#define ERR16_WRITE_FAULT 0xCCCC /* write fault */
+#define ERR16_INVALID_DEV 0x4001 /* invalid device access */
+#define ERR16_DEVICE_BUSY 0x4002 /* device is busy */
+#define ERR16_MEMORY 0x4003 /* device pass thru requires too much memory */
+#define ERR16_NO_FEATURE 0x40FA /* feature no implemented */
+#define ERR16_NOTAG 0x40FD /* no tag space available */
+#define ERR16_NOT_READY 0x40FE /* controller not ready error */
+#define ERR16_SETUP_FLASH 0x5050 /* error when writing setup to flash memory */
+#define ERR16_SETUP_SIZE 0x5051 /* setup block size error */
+#define ERR16_SENSE 0xFFFF /* sense opereration failed */
+#define ERR16_SC_BUSY 0x0008 /* SCSI status - Busy */
+#define ERR16_SC_RES_CONFL 0x0018 /* SCSI status - Reservation Conflict */
+#define ERR16_SC_CMD_TERM 0x0022 /* SCSI status - Command Terminated */
+#define ERR16_SC_OTHER 0x00FF /* SCSI status - not recognized (any value masked) */
+#define ERR16_MEDIA_CHANGED 0x8001 /* devices media has been changed */
+
+#define ERR32_NONE 0x00000000 /* no errors */
+#define ERR32_SC_COND_MET 0x00000004 /* SCSI status - Condition Met */
+#define ERR32_CMD 0x00010101 /* command error */
+#define ERR32_SC_CHECK_COND 0x00020002 /* SCSI status - Check Condition */
+#define ERR32_CMD_NOT 0x00030201 /* command not supported */
+#define ERR32_NO_DEVICE 0x00040301 /* invalid device selection */
+#define ERR32_SECTOR 0x00050202 /* bad sector */
+#define ERR32_PROTECT 0x00060303 /* write protected */
+#define ERR32_NOSECTOR 0x00070404 /* sector not found */
+#define ERR32_MEDIA 0x00080C0C /* invalid media */
+#define ERR32_CONTROL 0x00092020 /* controller error */
+#define ERR32_CONTROL_DMA 0x000A2120 /* Controller DMA error */
+#define ERR32_NO_ALARM 0x000B2220 /* alarm is not active */
+#define ERR32_OP_BUSY 0x000C2320 /* operation busy */
+#define ERR32_SEEK 0x000D4040 /* seek failure */
+#define ERR32_DEVICE_FAIL 0x000E4140 /* device has failed */
+#define ERR32_TIMEOUT 0x000F8080 /* timeout error */
+#define ERR32_DEV_NOT_READY 0x0010AAAA /* drive not ready */
+#define ERR32_UNDEFINED 0x0011BBBB /* undefined error */
+#define ERR32_WRITE_FAULT 0x0012CCCC /* write fault */
+#define ERR32_INVALID_DEV 0x00134001 /* invalid device access */
+#define ERR32_DEVICE_BUSY 0x00144002 /* device is busy */
+#define ERR32_MEMORY 0x00154003 /* device pass thru requires too much memory */
+#define ERR32_NO_FEATURE 0x001640FA /* feature no implemented */
+#define ERR32_NOTAG 0x001740FD /* no tag space available */
+#define ERR32_NOT_READY 0x001840FE /* controller not ready error */
+#define ERR32_SETUP_FLASH 0x00195050 /* error when writing setup to flash memory */
+#define ERR32_SETUP_SIZE 0x001A5051 /* setup block size error */
+#define ERR32_SENSE 0x001BFFFF /* sense opereration failed */
+#define ERR32_SC_BUSY 0x001C0008 /* SCSI status - Busy */
+#define ERR32_SC_RES_CONFL 0x001D0018 /* SCSI status - Reservation Conflict */
+#define ERR32_SC_CMD_TERM 0x001E0022 /* SCSI status - Command Terminated */
+#define ERR32_SC_OTHER 0x001F00FF /* SCSI status - not recognized (any value masked) */
+#define ERR32_MEDIA_CHANGED 0x00208001 /* devices media has been changed */
+
+/************************************************/
+/* */
+/* Host Operating System specification codes */
+/* */
+/************************************************/
+
+#define SPEC_INTERRUPT 0x80 /* specification requires host interrupt */
+#define SPEC_BACKWARD_SG 0x40 /* specification requires scatter/gather items reversed */
+#define SPEC_DOS_BLOCK 0x01 /* DOS DASD blocking on pass through */
+#define SPEC_OS2_V3 0x02 /* OS/2 Warp */
+#define SPCE_SCO_3242 0x04 /* SCO 3.4.2.2 */
+
+
+/************************************************/
+/* */
+/* Inquire structures */
+/* */
+/************************************************/
+typedef struct _CNT_SCSI_INQ
+ {
+ UCHAR devt; /* 00: device type */
+ UCHAR devtm; /* 01: device type modifier */
+ UCHAR svers; /* 02: SCSI version */
+ UCHAR rfmt; /* 03: response data format */
+ UCHAR adlen; /* 04: additional length of data */
+ UCHAR res1; /* 05: */
+ UCHAR res2; /* 06: */
+ UCHAR fncs; /* 07: functional capabilities */
+ UCHAR vid[8]; /* 08: vendor ID */
+ UCHAR pid[16]; /* 10: product ID */
+ UCHAR rev[4]; /* 20: product revision */
+ } CNT_SCSI_INQ;
+
+typedef struct _CNT_IDE_INQ
+ {
+ USHORT GeneralConfiguration; /* 00 */
+ USHORT NumberOfCylinders; /* 02 */
+ USHORT Reserved1; /* 04 */
+ USHORT NumberOfHeads; /* 06 */
+ USHORT UnformattedBytesPerTrack; /* 08 */
+ USHORT UnformattedBytesPerSector; /* 0A */
+ USHORT SectorsPerTrack; /* 0C */
+ USHORT VendorUnique1[3]; /* 0E */
+ USHORT SerialNumber[10]; /* 14 */
+ USHORT BufferType; /* 28 */
+ USHORT BufferSectorSize; /* 2A */
+ USHORT NumberOfEccBytes; /* 2C */
+ USHORT FirmwareRevision[4]; /* 2E */
+ USHORT ModelNumber[20]; /* 36 */
+ UCHAR MaximumBlockTransfer; /* 5E */
+ UCHAR VendorUnique2; /* 5F */
+ USHORT DoubleWordIo; /* 60 */
+ USHORT Capabilities; /* 62 */
+ USHORT Reserved2; /* 64 */
+ UCHAR VendorUnique3; /* 66 */
+ UCHAR PioCycleTimingMode; /* 67 */
+ UCHAR VendorUnique4; /* 68 */
+ UCHAR DmaCycleTimingMode; /* 69 */
+ USHORT TranslationFieldsValid; /* 6A */
+ USHORT NumberOfCurrentCylinders; /* 6C */
+ USHORT NumberOfCurrentHeads; /* 6E */
+ USHORT CurrentSectorsPerTrack; /* 70 */
+ ULONG CurrentSectorCapacity; /* 72 */
+ } CNT_IDE_INQ;
+
+typedef struct _DASD_INQUIRE
+ {
+ ULONG type; /* 0 = SCSI, 1 = IDE */
+ union
+ {
+ CNT_SCSI_INQ scsi; /* SCSI inquire data */
+ CNT_IDE_INQ ide; /* IDE inquire data */
+ } inq;
+ } DASD_INQUIRE;
+
+/************************************************/
+/* */
+/* Device Codes */
+/* */
+/************************************************/
+#define DEVC_DASD 0x00 /* Direct-access Storage Device */
+#define DEVC_SEQACESS 0x01 /* Sequential-access device */
+#define DEVC_PRINTER 0x02 /* Printer device */
+#define DEVC_PROCESSOR 0x03 /* Processor device */
+#define DEVC_WRITEONCE 0x04 /* Write-once device */
+#define DEVC_CDROM 0x05 /* CD-ROM device */
+#define DEVC_SCANNER 0x06 /* Scanner device */
+#define DEVC_OPTICAL 0x07 /* Optical memory device */
+#define DEVC_MEDCHGR 0x08 /* Medium changer device */
+#define DEVC_DASD_REMOVABLE 0x80 /* Direct-access storage device, Removable */
+#define DEVC_NONE 0xFF /* no device */
+
+#endif
+
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
index a81df6241..9f20c373c 100644
--- a/drivers/scsi/scsi.c
+++ b/drivers/scsi/scsi.c
@@ -3297,6 +3297,7 @@ static int scsi_register_device_module(struct Scsi_Device_Template * tpnt)
* This does any final handling that is required.
*/
if(tpnt->finish && tpnt->nr_dev) (*tpnt->finish)();
+ resize_dma_pool();
MOD_INC_USE_COUNT;
return 0;
}
diff --git a/drivers/scsi/scsi.h b/drivers/scsi/scsi.h
index 6404809cc..dd0f18fa1 100644
--- a/drivers/scsi/scsi.h
+++ b/drivers/scsi/scsi.h
@@ -498,8 +498,7 @@ static Scsi_Cmnd * end_scsi_request(Scsi_Cmnd * SCpnt, int uptodate, int sectors
req->nr_sectors -= bh->b_size >> 9;
req->sector += bh->b_size >> 9;
bh->b_reqnext = NULL;
- mark_buffer_uptodate(bh, uptodate);
- unlock_buffer(bh);
+ bh->b_end_io(bh, uptodate);
sectors -= bh->b_size >> 9;
if ((bh = req->bh) != NULL) {
req->current_nr_sectors = bh->b_size >> 9;
diff --git a/drivers/scsi/scsi_ioctl.c b/drivers/scsi/scsi_ioctl.c
index 073de6095..0971ce954 100644
--- a/drivers/scsi/scsi_ioctl.c
+++ b/drivers/scsi/scsi_ioctl.c
@@ -341,6 +341,11 @@ int scsi_ioctl (Scsi_Device *dev, int cmd, void *arg)
&((Scsi_Idlun *) arg)->dev_id);
put_user(dev->host->unique_id, &((Scsi_Idlun *) arg)->host_unique_id);
return 0;
+ case SCSI_IOCTL_GET_BUS_NUMBER:
+ result = verify_area(VERIFY_WRITE, (void *) arg, sizeof(int));
+ if (result) return result;
+ put_user( dev->host->host_no, (int *) arg);
+ return 0;
case SCSI_IOCTL_TAGGED_ENABLE:
if(!suser()) return -EACCES;
if(!dev->tagged_supported) return -EINVAL;
diff --git a/drivers/scsi/scsi_module.c b/drivers/scsi/scsi_module.c
index 5316e2e64..8a0de132a 100644
--- a/drivers/scsi/scsi_module.c
+++ b/drivers/scsi/scsi_module.c
@@ -34,7 +34,11 @@
int init_module(void) {
driver_template.module = &__this_module;
scsi_register_module(MODULE_SCSI_HA, &driver_template);
- return (driver_template.present == 0);
+ if (driver_template.present)
+ return 0;
+
+ scsi_unregister_module(MODULE_SCSI_HA, &driver_template);
+ return -1;
}
void cleanup_module( void) {
diff --git a/drivers/scsi/seagate.c b/drivers/scsi/seagate.c
index e9c411e48..d532c13e4 100644
--- a/drivers/scsi/seagate.c
+++ b/drivers/scsi/seagate.c
@@ -72,6 +72,13 @@
* x is some number, It will let you specify a default
* transfer rate if handshaking isn't working correctly.
*
+ * -DOLDCNTDATASCEME There is a new sceme to set the CONTROL
+ * and DATA reigsters which complies more closely
+ * with the SCSI2 standard. This hopefully eliminates
+ * the need to swap the order these registers are
+ * 'messed' with. It makes the following two options
+ * obsolete. To reenable the old sceme define this.
+ *
* The following to options are patches from the SCSI.HOWTO
*
* -DSWAPSTAT This will swap the definitions for STAT_MSG and STAT_CD.
@@ -794,6 +801,10 @@ static int internal_command (unsigned char target, unsigned char lun,
unsigned char message = 0;
register unsigned char status_read;
+#ifndef OLDCNTDATASCEME
+ volatile unsigned char tmp_data;
+ volatile unsigned char tmp_control;
+#endif
unsigned transfersize = 0, underflow = 0;
incommand = 0;
@@ -1029,6 +1040,7 @@ static int internal_command (unsigned char target, unsigned char lun,
* try this with a SCSI protocol or logic analyzer to see what is
* going on.
*/
+#ifdef OLDCNTDATASCEME
#ifdef SWAPCNTDATA
cli();
WRITE_CONTROL (BASE_CMD | CMD_DRVR_ENABLE | CMD_SEL |
@@ -1044,6 +1056,16 @@ static int internal_command (unsigned char target, unsigned char lun,
(reselect ? CMD_ATTN : 0));
sti ();
#endif
+#else
+ tmp_data = (unsigned char) ((1 << target) | (controller_type == SEAGATE
+? 0x80 : 0x40));
+ tmp_control = BASE_CMD | CMD_DRVR_ENABLE | CMD_SEL |
+ (reselect ? CMD_ATTN : 0) | CMD_BSY;
+ WRITE_CONTROL(tmp_data);
+ WRITE_DATA(tmp_control);
+ tmp_control ^= CMD_BSY;
+ WRITE_CONTROL(tmp_control);
+#endif /* OLDCNTDATASCEME */
while (!((status_read = STATUS) & STAT_BSY) && (jiffies < clock)
&& !st0x_aborted)
#if 0 && (DEBUG & PHASE_SELECTION)
diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c
index d83b94e26..a94fb569c 100644
--- a/drivers/scsi/sg.c
+++ b/drivers/scsi/sg.c
@@ -106,7 +106,7 @@ static int sg_open(struct inode * inode, struct file * filp)
if (flags & O_NONBLOCK)
return -EBUSY;
interruptible_sleep_on(&scsi_generics[dev].generic_wait);
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
return -ERESTARTSYS;
}
scsi_generics[dev].exclude=1;
@@ -121,7 +121,7 @@ static int sg_open(struct inode * inode, struct file * filp)
if (flags & O_NONBLOCK)
return -EBUSY;
interruptible_sleep_on(&scsi_generics[dev].generic_wait);
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
return -ERESTARTSYS;
}
@@ -172,7 +172,7 @@ static char *sg_malloc(int size)
while(big_inuse)
{
interruptible_sleep_on(&big_wait);
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
return NULL;
}
big_inuse=1;
@@ -200,12 +200,19 @@ static void sg_free(char *buff,int size)
* complete semaphores to tell us whether the buffer is available for us
* and whether the command is actually done.
*/
-static long sg_read(struct inode *inode,struct file *filp,char *buf,unsigned long count)
+static ssize_t sg_read(struct file *filp, char *buf,
+ size_t count, loff_t *ppos)
{
+ struct inode *inode = filp->f_dentry->d_inode;
int dev=MINOR(inode->i_rdev);
int i;
unsigned long flags;
struct scsi_generic *device=&scsi_generics[dev];
+
+ if (ppos != &filp->f_pos) {
+ /* FIXME: Hmm. Seek to the right place, or fail? */
+ }
+
if ((i=verify_area(VERIFY_WRITE,buf,count)))
return i;
@@ -222,7 +229,7 @@ static long sg_read(struct inode *inode,struct file *filp,char *buf,unsigned lon
return -EAGAIN;
}
interruptible_sleep_on(&device->read_wait);
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
{
restore_flags(flags);
return -ERESTARTSYS;
@@ -322,8 +329,10 @@ static void sg_command_done(Scsi_Cmnd * SCpnt)
wake_up(&scsi_generics[dev].read_wait);
}
-static long sg_write(struct inode *inode,struct file *filp,const char *buf,unsigned long count)
+static ssize_t sg_write(struct file *filp, const char *buf,
+ size_t count, loff_t *ppos)
{
+ struct inode *inode = filp->f_dentry->d_inode;
int bsize,size,amt,i;
unsigned char cmnd[MAX_COMMAND_SIZE];
kdev_t devt = inode->i_rdev;
@@ -333,6 +342,10 @@ static long sg_write(struct inode *inode,struct file *filp,const char *buf,unsig
unsigned char opcode;
Scsi_Cmnd * SCpnt;
+ if (ppos != &filp->f_pos) {
+ /* FIXME: Hmm. Seek to the right place, or fail? */
+ }
+
if ((i=verify_area(VERIFY_READ,buf,count)))
return i;
/*
@@ -355,7 +368,7 @@ static long sg_write(struct inode *inode,struct file *filp,const char *buf,unsig
printk("sg_write: sleeping on pending request\n");
#endif
interruptible_sleep_on(&device->write_wait);
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
return -ERESTARTSYS;
}
diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c
index 517568346..12a8f3723 100644
--- a/drivers/scsi/sr.c
+++ b/drivers/scsi/sr.c
@@ -45,6 +45,8 @@
#include <scsi/scsi_ioctl.h> /* For the door lock/unlock commands */
#include "constants.h"
+MODULE_PARM(xa_test,"i"); /* see sr_ioctl.c */
+
#define MAX_RETRIES 3
#define SR_TIMEOUT (30 * HZ)
@@ -67,12 +69,15 @@ static int * sr_hardsizes = NULL; /* Hardware sector size */
static int sr_open(struct cdrom_device_info*, int);
void get_sectorsize(int);
+void get_capabilities(int);
void requeue_sr_request (Scsi_Cmnd * SCpnt);
static int sr_media_change(struct cdrom_device_info*, int);
static void sr_release(struct cdrom_device_info *cdi)
{
+ if (scsi_CDs[MINOR(cdi->dev)].sector_size > 2048)
+ sr_set_blocklength(MINOR(cdi->dev),2048);
sync_dev(cdi->dev);
scsi_CDs[MINOR(cdi->dev)].device->access_count--;
if (scsi_CDs[MINOR(cdi->dev)].device->host->hostt->module)
@@ -89,14 +94,14 @@ static struct cdrom_device_ops sr_dops = {
sr_media_change, /* media changed */
sr_tray_move, /* tray move */
sr_lock_door, /* lock door */
- NULL, /* select speed */
+ sr_select_speed, /* select speed */
NULL, /* select disc */
sr_get_last_session, /* get last session */
sr_get_mcn, /* get universal product code */
sr_reset, /* hard reset */
sr_audio_ioctl, /* audio ioctl */
sr_dev_ioctl, /* device-specific ioctl */
- CDC_CLOSE_TRAY | CDC_OPEN_TRAY| CDC_LOCK |
+ CDC_CLOSE_TRAY | CDC_OPEN_TRAY| CDC_LOCK | CDC_SELECT_SPEED |
CDC_MULTI_SESSION | CDC_MCN | CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO,
0
};
@@ -139,10 +144,9 @@ int sr_media_change(struct cdrom_device_info *cdi, int slot){
/* If the disk changed, the capacity will now be different,
* so we force a re-read of this information */
if (retval) {
-#ifdef CONFIG_BLK_DEV_SR_VENDOR
+ /* check multisession offset etc */
sr_cd_check(cdi);
-#endif
-
+
/*
* If the disk changed, the capacity will now be different,
* so we force a re-read of this information
@@ -311,7 +315,8 @@ static void rw_intr (Scsi_Cmnd * SCpnt)
}
if (SCpnt->sense_buffer[2] == ILLEGAL_REQUEST) {
- printk("CD-ROM error: ");
+ printk("sr%d: CD-ROM error: ",
+ DEVICE_NR(SCpnt->request.rq_dev));
print_sense("sr", SCpnt);
printk("command was: ");
print_command(SCpnt->cmnd);
@@ -329,7 +334,8 @@ static void rw_intr (Scsi_Cmnd * SCpnt)
}
if (SCpnt->sense_buffer[2] == NOT_READY) {
- printk(KERN_INFO "CD-ROM not ready. Make sure you have a disc in the drive.\n");
+ printk(KERN_INFO "sr%d: CD-ROM not ready. Make sure you have a disc in the drive.\n",
+ DEVICE_NR(SCpnt->request.rq_dev));
SCpnt = end_scsi_request(SCpnt, 0, this_count);
requeue_sr_request(SCpnt); /* Do next request */
return;
@@ -358,7 +364,7 @@ static void rw_intr (Scsi_Cmnd * SCpnt)
requeue_sr_request(SCpnt);
return;
}
- }
+ }
/* We only get this far if we have an error we have not recognized */
if(result) {
@@ -443,6 +449,17 @@ static void do_sr_request (void)
SDev->was_reset = 0;
}
+ /* we do lazy blocksize switching (when reading XA sectors,
+ * see CDROMREADMODE2 ioctl) */
+ if (scsi_CDs[DEVICE_NR(CURRENT->rq_dev)].sector_size > 2048) {
+ if (!in_interrupt())
+ sr_set_blocklength(DEVICE_NR(CURRENT->rq_dev),2048);
+#if 1
+ else
+ printk("sr: can't switch blocksize: in interrupt\n");
+#endif
+ }
+
if (flag++ == 0)
SCpnt = allocate_device(&CURRENT,
scsi_CDs[DEVICE_NR(CURRENT->rq_dev)].device, 0);
@@ -649,7 +666,7 @@ void requeue_sr_request (Scsi_Cmnd * SCpnt)
* 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");
+ printk("Warning: Running low on SCSI DMA buffers\n");
/* Try switching back to a non scatter-gather operation. */
while(--count >= 0){
if(sgpnt[count].alt_address)
@@ -805,17 +822,7 @@ static int sr_attach(Scsi_Device * SDp){
SDp->scsi_request_fn = do_sr_request;
scsi_CDs[i].device = SDp;
- scsi_CDs[i].cdi.ops = &sr_dops;
- scsi_CDs[i].cdi.handle = &scsi_CDs[i];
- scsi_CDs[i].cdi.dev = MKDEV(MAJOR_NR,i);
- scsi_CDs[i].cdi.mask = 0;
- scsi_CDs[i].cdi.speed = 1;
- scsi_CDs[i].cdi.capacity = 1;
- register_cdrom(&scsi_CDs[i].cdi, "sr");
-
-#ifdef CONFIG_BLK_DEV_SR_VENDOR
sr_vendor_init(i);
-#endif
sr_template.nr_dev++;
if(sr_template.nr_dev > sr_template.dev_max)
@@ -902,7 +909,7 @@ void get_sectorsize(int i){
case 512:
break;
default:
- printk ("scd%d : unsupported sector size %d.\n",
+ printk ("sr%d: unsupported sector size %d.\n",
i, scsi_CDs[i].sector_size);
scsi_CDs[i].capacity = 0;
scsi_CDs[i].needs_sector_size = 1;
@@ -919,6 +926,58 @@ void get_sectorsize(int i){
scsi_free(buffer, 512);
}
+void get_capabilities(int i){
+ unsigned char cmd[6];
+ unsigned char *buffer;
+ int rc,n;
+
+ static char *loadmech[] = {
+ "caddy",
+ "tray",
+ "pop-up",
+ "",
+ "changer",
+ "changer",
+ "",
+ ""
+ };
+
+ buffer = (unsigned char *) scsi_malloc(512);
+ cmd[0] = MODE_SENSE;
+ cmd[1] = (scsi_CDs[i].device->lun << 5) & 0xe0;
+ cmd[2] = 0x2a;
+ cmd[4] = 128;
+ cmd[3] = cmd[5] = 0;
+ rc = sr_do_ioctl(i, cmd, buffer, 128, 1);
+
+ if (-EINVAL == rc) {
+ /* failed, drive has'nt this mode page */
+ scsi_CDs[i].cdi.speed = 1;
+ scsi_CDs[i].cdi.capacity = 1;
+ /* disable speed select, drive probably can't do this either */
+ scsi_CDs[i].cdi.mask |= CDC_SELECT_SPEED;
+ } else {
+ n = buffer[3]+4;
+ scsi_CDs[i].cdi.speed = ((buffer[n+8] << 8) + buffer[n+9])/176;
+ scsi_CDs[i].cdi.capacity = 1;
+ scsi_CDs[i].readcd_known = 1;
+ scsi_CDs[i].readcd_cdda = buffer[n+5] & 0x01;
+ /* print some capability bits */
+ printk("sr%i: scsi3-mmc drive: %dx/%dx %s%s%s%s%s\n",i,
+ ((buffer[n+14] << 8) + buffer[n+15])/176,
+ scsi_CDs[i].cdi.speed,
+ buffer[n+3]&0x01 ? "writer " : "", /* CD Writer */
+ buffer[n+2]&0x02 ? "cd/rw " : "", /* can read rewriteable */
+ buffer[n+4]&0x20 ? "xa/form2 " : "", /* can read xa/from2 */
+ buffer[n+5]&0x01 ? "cdda " : "", /* can read audio data */
+ loadmech[buffer[n+6]>>5]);
+ if ((buffer[n+6] >> 5) == 0)
+ /* caddy drives can't close tray... */
+ scsi_CDs[i].cdi.mask |= CDC_CLOSE_TRAY;
+ }
+ scsi_free(buffer, 512);
+}
+
static int sr_registered = 0;
static int sr_init()
@@ -984,7 +1043,16 @@ void sr_finish()
scsi_CDs[i].use = 1;
scsi_CDs[i].ten = 1;
scsi_CDs[i].remap = 1;
+ scsi_CDs[i].readcd_known = 0;
+ scsi_CDs[i].readcd_cdda = 0;
sr_sizes[i] = scsi_CDs[i].capacity >> (BLOCK_SIZE_BITS - 9);
+
+ scsi_CDs[i].cdi.ops = &sr_dops;
+ scsi_CDs[i].cdi.handle = &scsi_CDs[i];
+ scsi_CDs[i].cdi.dev = MKDEV(MAJOR_NR,i);
+ scsi_CDs[i].cdi.mask = 0;
+ get_capabilities(i);
+ register_cdrom(&scsi_CDs[i].cdi, "sr");
}
diff --git a/drivers/scsi/sr.h b/drivers/scsi/sr.h
index 1c3482f3d..983ff18df 100644
--- a/drivers/scsi/sr.h
+++ b/drivers/scsi/sr.h
@@ -17,8 +17,6 @@
#ifndef _SR_H
#define _SR_H
-#include <linux/config.h>
-
#include "scsi.h"
typedef struct
@@ -34,13 +32,15 @@ typedef struct
unsigned ten:1; /* support ten byte commands */
unsigned remap:1; /* support remapping */
unsigned use:1; /* is this device still supportable */
- unsigned xa_flag:1; /* CD has XA sectors */
+ unsigned xa_flag:1; /* CD has XA sectors ? */
+ unsigned readcd_known:1; /* drive supports READ_CD (0xbe) */
+ unsigned readcd_cdda:1; /* reading audio data using READ_CD */
struct cdrom_device_info cdi;
} Scsi_CD;
extern Scsi_CD * scsi_CDs;
-int sr_do_ioctl(int, unsigned char*, void*, unsigned);
+int sr_do_ioctl(int, unsigned char*, void*, unsigned, int);
int sr_lock_door(struct cdrom_device_info*, int);
int sr_tray_move(struct cdrom_device_info*, int);
@@ -49,15 +49,16 @@ int sr_disk_status(struct cdrom_device_info*);
int sr_get_last_session(struct cdrom_device_info*, struct cdrom_multisession*);
int sr_get_mcn(struct cdrom_device_info*, struct cdrom_mcn*);
int sr_reset(struct cdrom_device_info*);
+int sr_select_speed(struct cdrom_device_info *cdi, int speed);
int sr_audio_ioctl(struct cdrom_device_info*, unsigned int, void*);
int sr_dev_ioctl(struct cdrom_device_info*, unsigned int, unsigned long);
-/* vendor-specific */
-#ifdef CONFIG_BLK_DEV_SR_VENDOR
-void sr_vendor_init(int minor);
-int sr_cd_check(struct cdrom_device_info*);
int sr_read_sector(int minor, int lba, int blksize, unsigned char *dest);
+int sr_is_xa(int minor);
-#endif
+/* sr_vendor.c */
+void sr_vendor_init(int minor);
+int sr_cd_check(struct cdrom_device_info*);
+int sr_set_blocklength(int minor, int blocklength);
#endif
diff --git a/drivers/scsi/sr_ioctl.c b/drivers/scsi/sr_ioctl.c
index bc870dfea..628d39c3e 100644
--- a/drivers/scsi/sr_ioctl.c
+++ b/drivers/scsi/sr_ioctl.c
@@ -1,4 +1,3 @@
-#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/mm.h>
@@ -16,6 +15,17 @@
#include <linux/ucdrom.h>
#include "sr.h"
+#if 0
+# define DEBUG
+#endif
+
+/* for now we borrow the "operation not supported" from the network folks */
+#define EDRIVE_CANT_DO_THIS EOPNOTSUPP
+
+/* The sr_is_xa() seems to trigger firmware bugs with some drives :-(
+ * It is off by default and can be turned on with this module parameter */
+static int xa_test = 0;
+
extern void get_sectorsize(int);
#define IOCTL_RETRIES 3
@@ -39,12 +49,14 @@ static void sr_ioctl_done(Scsi_Cmnd * SCpnt)
error code is. Normally the UNIT_ATTENTION code will automatically
clear after one error */
-int sr_do_ioctl(int target, unsigned char * sr_cmd, void * buffer, unsigned buflength)
+int sr_do_ioctl(int target, unsigned char * sr_cmd, void * buffer, unsigned buflength, int quiet)
{
Scsi_Cmnd * SCpnt;
- int result;
+ int result, err = 0, retries = 0;
SCpnt = allocate_device(NULL, scsi_CDs[target].device, 1);
+
+retry:
{
struct semaphore sem = MUTEX_LOCKED;
SCpnt->request.sem = &sem;
@@ -61,28 +73,79 @@ int sr_do_ioctl(int target, unsigned char * sr_cmd, void * buffer, unsigned bufl
switch(SCpnt->sense_buffer[2] & 0xf) {
case UNIT_ATTENTION:
scsi_CDs[target].device->changed = 1;
- printk("Disc change detected.\n");
+ printk(KERN_INFO "sr%d: disc change detected.\n", target);
+ if (retries++ < 10)
+ goto retry;
+ err = -ENOMEDIUM;
break;
case NOT_READY: /* This happens if there is no disc in drive */
- printk(KERN_INFO "CDROM not ready. Make sure there is a disc in the drive.\n");
+ if (SCpnt->sense_buffer[12] == 0x04 &&
+ SCpnt->sense_buffer[13] == 0x01) {
+ /* sense: Logical unit is in process of becoming ready */
+ if (!quiet)
+ printk(KERN_INFO "sr%d: CDROM not ready yet.\n", target);
+ if (retries++ < 10) {
+ /* sleep 2 sec and try again */
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + 200;
+ schedule ();
+ goto retry;
+ } else {
+ /* 20 secs are enouth? */
+ err = -ENOMEDIUM;
+ break;
+ }
+ }
+ printk(KERN_INFO "sr%d: CDROM not ready. Make sure there is a disc in the drive.\n",target);
+#ifdef DEBUG
+ print_sense("sr", SCpnt);
+#endif
+ err = -ENOMEDIUM;
break;
case ILLEGAL_REQUEST:
- printk("CDROM (ioctl) reports ILLEGAL REQUEST.\n");
+ if (!quiet)
+ printk("sr%d: CDROM (ioctl) reports ILLEGAL REQUEST.\n",
+ target);
+ if (SCpnt->sense_buffer[12] == 0x20 &&
+ SCpnt->sense_buffer[13] == 0x00) {
+ /* sense: Invalid command operation code */
+ err = -EDRIVE_CANT_DO_THIS;
+ } else {
+ err = -EINVAL;
+ }
+#ifdef DEBUG
+ print_command(sr_cmd);
+ print_sense("sr", SCpnt);
+#endif
break;
default:
+ printk("sr%d: CDROM (ioctl) error, command: ", target);
+ print_command(sr_cmd);
print_sense("sr", SCpnt);
+ err = -EIO;
};
result = SCpnt->result;
SCpnt->request.rq_status = RQ_INACTIVE; /* Deallocate */
+ /* Wake up a process waiting for device */
wake_up(&SCpnt->device->device_wait);
- /* Wake up a process waiting for device*/
- return result;
+
+ return err;
}
/* ---------------------------------------------------------------------- */
/* interface to cdrom.c */
+static int test_unit_ready(int minor)
+{
+ u_char sr_cmd[10];
+
+ sr_cmd[0] = TEST_UNIT_READY;
+ sr_cmd[1] = ((scsi_CDs[minor].device -> lun) << 5);
+ sr_cmd[2] = sr_cmd[3] = sr_cmd[4] = sr_cmd[5] = 0;
+ return sr_do_ioctl(minor, sr_cmd, NULL, 255, 1);
+}
+
int sr_tray_move(struct cdrom_device_info *cdi, int pos)
{
u_char sr_cmd[10];
@@ -92,7 +155,7 @@ int sr_tray_move(struct cdrom_device_info *cdi, int pos)
sr_cmd[2] = sr_cmd[3] = sr_cmd[5] = 0;
sr_cmd[4] = (pos == 0) ? 0x03 /* close */ : 0x02 /* eject */;
- return sr_do_ioctl(MINOR(cdi->dev), sr_cmd, NULL, 255);
+ return sr_do_ioctl(MINOR(cdi->dev), sr_cmd, NULL, 255, 0);
}
int sr_lock_door(struct cdrom_device_info *cdi, int lock)
@@ -109,51 +172,39 @@ int sr_drive_status(struct cdrom_device_info *cdi, int slot)
return -EINVAL;
}
- if (!scsi_ioctl(scsi_CDs[MINOR(cdi->dev)].device,
- SCSI_IOCTL_TEST_UNIT_READY,0))
- return CDS_DISC_OK;
+ if (0 == test_unit_ready(MINOR(cdi->dev)))
+ return CDS_DISC_OK;
-#if 1
- /* Tell tray is open if the drive is not ready. Seems there is
- * no way to check whenever the tray is really open, but this way
- * we get auto-close-on-open work. And it seems to have no ill
- * effects with caddy drives... */
return CDS_TRAY_OPEN;
-#else
- return CDS_NO_DISC;
-#endif
}
int sr_disk_status(struct cdrom_device_info *cdi)
{
struct cdrom_tochdr toc_h;
struct cdrom_tocentry toc_e;
- int i;
+ int i,rc,have_datatracks = 0;
- if (scsi_ioctl(scsi_CDs[MINOR(cdi->dev)].device,SCSI_IOCTL_TEST_UNIT_READY,0))
- return CDS_NO_DISC;
-
- /* if the xa-bit is on, we tell it is XA... */
- if (scsi_CDs[MINOR(cdi->dev)].xa_flag)
- return CDS_XA_2_1;
+ /* look for data tracks */
+ if (0 != (rc = sr_audio_ioctl(cdi, CDROMREADTOCHDR, &toc_h)))
+ return (rc == -ENOMEDIUM) ? CDS_NO_DISC : CDS_NO_INFO;
- /* ...else we look for data tracks */
- if (sr_audio_ioctl(cdi, CDROMREADTOCHDR, &toc_h))
- return CDS_NO_INFO;
for (i = toc_h.cdth_trk0; i <= toc_h.cdth_trk1; i++) {
toc_e.cdte_track = i;
toc_e.cdte_format = CDROM_LBA;
if (sr_audio_ioctl(cdi, CDROMREADTOCENTRY, &toc_e))
return CDS_NO_INFO;
- if (toc_e.cdte_ctrl & CDROM_DATA_TRACK)
- return CDS_DATA_1;
-#if 0
- if (i == toc_h.cdth_trk0 && toc_e.cdte_addr.lba > 100)
- /* guess: looks like a "hidden track" CD */
- return CDS_DATA_1;
-#endif
+ if (toc_e.cdte_ctrl & CDROM_DATA_TRACK) {
+ have_datatracks = 1;
+ break;
+ }
}
- return CDS_AUDIO;
+ if (!have_datatracks)
+ return CDS_AUDIO;
+
+ if (scsi_CDs[MINOR(cdi->dev)].xa_flag)
+ return CDS_XA_2_1;
+ else
+ return CDS_DATA_1;
}
int sr_get_last_session(struct cdrom_device_info *cdi,
@@ -161,7 +212,7 @@ int sr_get_last_session(struct cdrom_device_info *cdi,
{
ms_info->addr.lba=scsi_CDs[MINOR(cdi->dev)].ms_offset;
ms_info->xa_flag=scsi_CDs[MINOR(cdi->dev)].xa_flag ||
- scsi_CDs[MINOR(cdi->dev)].ms_offset > 0;
+ (scsi_CDs[MINOR(cdi->dev)].ms_offset > 0);
return 0;
}
@@ -185,7 +236,7 @@ int sr_get_mcn(struct cdrom_device_info *cdi,struct cdrom_mcn *mcn)
buffer = (unsigned char*) scsi_malloc(512);
if(!buffer) return -ENOMEM;
- result = sr_do_ioctl(MINOR(cdi->dev), sr_cmd, buffer, 24);
+ result = sr_do_ioctl(MINOR(cdi->dev), sr_cmd, buffer, 24, 0);
memcpy (mcn->medium_catalog_number, buffer + 9, 13);
mcn->medium_catalog_number[13] = 0;
@@ -201,6 +252,26 @@ int sr_reset(struct cdrom_device_info *cdi)
return 0;
}
+int sr_select_speed(struct cdrom_device_info *cdi, int speed)
+{
+ u_char sr_cmd[12];
+
+ if (speed == 0)
+ speed = 0xffff; /* set to max */
+ else
+ speed *= 177; /* Nx to kbyte/s */
+
+ memset(sr_cmd,0,12);
+ sr_cmd[0] = 0xbb; /* SET CD SPEED */
+ sr_cmd[1] = (scsi_CDs[MINOR(cdi->dev)].device->lun) << 5;
+ sr_cmd[2] = (speed >> 8) & 0xff; /* MSB for speed (in kbytes/sec) */
+ sr_cmd[3] = speed & 0xff; /* LSB */
+
+ if (sr_do_ioctl(MINOR(cdi->dev), sr_cmd, NULL, 0, 0))
+ return -EIO;
+ return 0;
+}
+
/* ----------------------------------------------------------------------- */
/* this is called by the generic cdrom driver. arg is a _kernel_ pointer, */
/* becauce the generic cdrom driver does the user access stuff for us. */
@@ -224,7 +295,7 @@ int sr_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void* arg)
sr_cmd[8] = 0;
sr_cmd[9] = 0;
- result = sr_do_ioctl(target, sr_cmd, NULL, 255);
+ result = sr_do_ioctl(target, sr_cmd, NULL, 255, 0);
break;
case CDROMRESUME:
@@ -236,7 +307,7 @@ int sr_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void* arg)
sr_cmd[8] = 1;
sr_cmd[9] = 0;
- result = sr_do_ioctl(target, sr_cmd, NULL, 255);
+ result = sr_do_ioctl(target, sr_cmd, NULL, 255, 0);
break;
case CDROMPLAYMSF:
@@ -254,7 +325,7 @@ int sr_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void* arg)
sr_cmd[8] = msf->cdmsf_frame1;
sr_cmd[9] = 0;
- result = sr_do_ioctl(target, sr_cmd, NULL, 255);
+ result = sr_do_ioctl(target, sr_cmd, NULL, 255, 0);
break;
}
@@ -273,7 +344,7 @@ int sr_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void* arg)
sr_cmd[8] = blk->len;
sr_cmd[9] = 0;
- result = sr_do_ioctl(target, sr_cmd, NULL, 255);
+ result = sr_do_ioctl(target, sr_cmd, NULL, 255, 0);
break;
}
@@ -292,7 +363,7 @@ int sr_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void* arg)
sr_cmd[8] = ti->cdti_ind1;
sr_cmd[9] = 0;
- result = sr_do_ioctl(target, sr_cmd, NULL, 255);
+ result = sr_do_ioctl(target, sr_cmd, NULL, 255, 0);
break;
}
@@ -312,7 +383,7 @@ int sr_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void* arg)
buffer = (unsigned char *) scsi_malloc(512);
if(!buffer) return -ENOMEM;
- result = sr_do_ioctl(target, sr_cmd, buffer, 12);
+ result = sr_do_ioctl(target, sr_cmd, buffer, 12, 0);
tochdr->cdth_trk0 = buffer[2];
tochdr->cdth_trk1 = buffer[3];
@@ -338,7 +409,7 @@ int sr_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void* arg)
buffer = (unsigned char *) scsi_malloc(512);
if(!buffer) return -ENOMEM;
- result = sr_do_ioctl (target, sr_cmd, buffer, 12);
+ result = sr_do_ioctl (target, sr_cmd, buffer, 12, 0);
tocentry->cdte_ctrl = buffer[5] & 0xf;
tocentry->cdte_adr = buffer[5] >> 4;
@@ -361,7 +432,7 @@ int sr_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void* arg)
sr_cmd[2] = sr_cmd[3] = sr_cmd[5] = 0;
sr_cmd[4] = 0;
- result = sr_do_ioctl(target, sr_cmd, NULL, 255);
+ result = sr_do_ioctl(target, sr_cmd, NULL, 255, 0);
break;
case CDROMSTART:
@@ -370,7 +441,7 @@ int sr_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void* arg)
sr_cmd[2] = sr_cmd[3] = sr_cmd[5] = 0;
sr_cmd[4] = 1;
- result = sr_do_ioctl(target, sr_cmd, NULL, 255);
+ result = sr_do_ioctl(target, sr_cmd, NULL, 255, 0);
break;
case CDROMVOLCTRL:
@@ -390,7 +461,7 @@ int sr_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void* arg)
buffer = (unsigned char *) scsi_malloc(512);
if(!buffer) return -ENOMEM;
- if ((result = sr_do_ioctl (target, sr_cmd, buffer, 28))) {
+ if ((result = sr_do_ioctl (target, sr_cmd, buffer, 28, 0))) {
printk ("Hosed while obtaining audio mode page\n");
scsi_free(buffer, 512);
break;
@@ -410,7 +481,7 @@ int sr_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void* arg)
break;
};
- if ((result = sr_do_ioctl (target, sr_cmd, mask, 28))) {
+ if ((result = sr_do_ioctl (target, sr_cmd, mask, 28, 0))) {
printk ("Hosed while obtaining mask for audio mode page\n");
scsi_free(buffer, 512);
scsi_free(mask, 512);
@@ -431,7 +502,7 @@ int sr_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void* arg)
sr_cmd[4] = 28;
sr_cmd[5] = 0;
- result = sr_do_ioctl (target, sr_cmd, buffer, 28);
+ result = sr_do_ioctl (target, sr_cmd, buffer, 28, 0);
scsi_free(buffer, 512);
scsi_free(mask, 512);
break;
@@ -454,7 +525,7 @@ int sr_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void* arg)
buffer = (unsigned char *) scsi_malloc(512);
if(!buffer) return -ENOMEM;
- if ((result = sr_do_ioctl (target, sr_cmd, buffer, 28))) {
+ if ((result = sr_do_ioctl (target, sr_cmd, buffer, 28, 0))) {
printk ("(CDROMVOLREAD) Hosed while obtaining audio mode page\n");
scsi_free(buffer, 512);
break;
@@ -487,7 +558,7 @@ int sr_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void* arg)
buffer = (unsigned char*) scsi_malloc(512);
if(!buffer) return -ENOMEM;
- result = sr_do_ioctl(target, sr_cmd, buffer, 16);
+ result = sr_do_ioctl(target, sr_cmd, buffer, 16, 0);
subchnl->cdsc_audiostatus = buffer[1];
subchnl->cdsc_format = CDROM_MSF;
@@ -516,7 +587,120 @@ int sr_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void* arg)
return result;
}
-
+
+/* -----------------------------------------------------------------------
+ * a function to read all sorts of funny cdrom sectors using the READ_CD
+ * scsi-3 mmc command
+ *
+ * lba: linear block address
+ * format: 0 = data (anything)
+ * 1 = audio
+ * 2 = data (mode 1)
+ * 3 = data (mode 2)
+ * 4 = data (mode 2 form1)
+ * 5 = data (mode 2 form2)
+ * blksize: 2048 | 2336 | 2340 | 2352
+ */
+
+int
+sr_read_cd(int minor, unsigned char *dest, int lba, int format, int blksize)
+{
+ unsigned char cmd[12];
+
+#ifdef DEBUG
+ printk("sr%d: sr_read_cd lba=%d format=%d blksize=%d\n",
+ minor,lba,format,blksize);
+#endif
+
+ memset(cmd,0,12);
+ cmd[0] = 0xbe /* READ_CD */;
+ cmd[1] = (scsi_CDs[minor].device->lun << 5) | ((format & 7) << 2);
+ cmd[2] = (unsigned char)(lba >> 24) & 0xff;
+ cmd[3] = (unsigned char)(lba >> 16) & 0xff;
+ cmd[4] = (unsigned char)(lba >> 8) & 0xff;
+ cmd[5] = (unsigned char) lba & 0xff;
+ cmd[8] = 1;
+ switch (blksize) {
+ case 2336: cmd[9] = 0x58; break;
+ case 2340: cmd[9] = 0x78; break;
+ case 2352: cmd[9] = 0xf8; break;
+ default: cmd[9] = 0x10; break;
+ }
+ return sr_do_ioctl(minor, cmd, dest, blksize, 0);
+}
+
+/*
+ * read sectors with blocksizes other than 2048
+ */
+
+int
+sr_read_sector(int minor, int lba, int blksize, unsigned char *dest)
+{
+ unsigned char cmd[12]; /* the scsi-command */
+ int rc;
+
+ /* we try the READ CD command first... */
+ if (scsi_CDs[minor].readcd_known) {
+ rc = sr_read_cd(minor, dest, lba, 0, blksize);
+ if (-EDRIVE_CANT_DO_THIS != rc)
+ return rc;
+ scsi_CDs[minor].readcd_known = 0;
+ printk("CDROM does'nt support READ CD (0xbe) command\n");
+ /* fall & retry the other way */
+ }
+
+ /* ... if this fails, we switch the blocksize using MODE SELECT */
+ if (blksize != scsi_CDs[minor].sector_size)
+ if (0 != (rc = sr_set_blocklength(minor, blksize)))
+ return rc;
+
+#ifdef DEBUG
+ printk("sr%d: sr_read_sector lba=%d blksize=%d\n",minor,lba,blksize);
+#endif
+
+ memset(cmd,0,12);
+ cmd[0] = READ_10;
+ cmd[1] = (scsi_CDs[minor].device->lun << 5);
+ cmd[2] = (unsigned char)(lba >> 24) & 0xff;
+ cmd[3] = (unsigned char)(lba >> 16) & 0xff;
+ cmd[4] = (unsigned char)(lba >> 8) & 0xff;
+ cmd[5] = (unsigned char) lba & 0xff;
+ cmd[8] = 1;
+ rc = sr_do_ioctl(minor, cmd, dest, blksize, 0);
+
+ return rc;
+}
+
+/*
+ * read a sector in raw mode to check the sector format
+ * ret: 1 == mode2 (XA), 0 == mode1, <0 == error
+ */
+
+int
+sr_is_xa(int minor)
+{
+ unsigned char *raw_sector;
+ int is_xa;
+
+ if (!xa_test)
+ return 0;
+
+ raw_sector = (unsigned char *) scsi_malloc(2048+512);
+ if (!raw_sector) return -ENOMEM;
+ if (0 == sr_read_sector(minor,scsi_CDs[minor].ms_offset+16,
+ CD_FRAMESIZE_RAW1,raw_sector)) {
+ is_xa = (raw_sector[3] == 0x02) ? 1 : 0;
+ } else {
+ /* read a raw sector failed for some reason. */
+ is_xa = -1;
+ }
+ scsi_free(raw_sector, 2048+512);
+#ifdef DEBUG
+ printk("sr%d: sr_is_xa: %d\n",minor,is_xa);
+#endif
+ return is_xa;
+}
+
int sr_dev_ioctl(struct cdrom_device_info *cdi,
unsigned int cmd, unsigned long arg)
{
@@ -525,33 +709,31 @@ int sr_dev_ioctl(struct cdrom_device_info *cdi,
target = MINOR(cdi->dev);
switch (cmd) {
- /* these are compatible with the ide-cd driver */
- case CDROMREADRAW:
case CDROMREADMODE1:
case CDROMREADMODE2:
-
-#if CONFIG_BLK_DEV_SR_VENDOR
+ case CDROMREADRAW:
{
unsigned char *raw;
struct cdrom_msf msf;
- int blocksize, lba, rc;
+ int lba, rc;
+ int blocksize = 2048;
- if (cmd == CDROMREADMODE1)
- blocksize = CD_FRAMESIZE; /* 2048 */
- else if (cmd == CDROMREADMODE2)
- blocksize = CD_FRAMESIZE_RAW0; /* 2336 */
- else
- /* some SCSI drives do not allow this one */
- blocksize = CD_FRAMESIZE_RAW; /* 2352 */
+ switch (cmd) {
+ case CDROMREADMODE2: blocksize = CD_FRAMESIZE_RAW0; break; /* 2336 */
+ case CDROMREADRAW: blocksize = CD_FRAMESIZE_RAW; break; /* 2352 */
+ }
if (copy_from_user(&msf,(void*)arg,sizeof(msf)))
return -EFAULT;
if (!(raw = scsi_malloc(2048+512)))
- return -ENOMEM;
+ return -ENOMEM;
lba = (((msf.cdmsf_min0 * CD_SECS) + msf.cdmsf_sec0)
* CD_FRAMES + msf.cdmsf_frame0) - CD_BLOCK_OFFSET;
- rc = sr_read_sector(target, lba, blocksize, raw);
+ if (lba < 0 || lba >= scsi_CDs[target].capacity)
+ return -EINVAL;
+
+ rc = sr_read_sector(target, lba, blocksize, raw);
if (!rc)
if (copy_to_user((void*)arg, raw, blocksize))
rc = -EFAULT;
@@ -559,11 +741,44 @@ int sr_dev_ioctl(struct cdrom_device_info *cdi,
scsi_free(raw,2048+512);
return rc;
}
-#else
- return -EINVAL;
-#endif
+ case CDROMREADAUDIO:
+ {
+ unsigned char *raw;
+ int lba, rc=0;
+ struct cdrom_read_audio ra;
-
+ if (!scsi_CDs[target].readcd_known || !scsi_CDs[target].readcd_cdda)
+ return -EINVAL; /* -EDRIVE_DOES_NOT_SUPPORT_THIS ? */
+
+ if (copy_from_user(&ra,(void*)arg,sizeof(ra)))
+ return -EFAULT;
+
+ if (ra.addr_format == CDROM_LBA)
+ lba = ra.addr.lba;
+ else
+ lba = (((ra.addr.msf.minute * CD_SECS) + ra.addr.msf.second)
+ * CD_FRAMES + ra.addr.msf.frame) - CD_BLOCK_OFFSET;
+
+ if (lba < 0 || lba >= scsi_CDs[target].capacity)
+ return -EINVAL;
+ if (!(raw = scsi_malloc(2048+512)))
+ return -ENOMEM;
+
+ while (ra.nframes > 0) {
+ rc = sr_read_cd(target, raw, lba, 1, CD_FRAMESIZE_RAW);
+ if (!rc)
+ if (copy_to_user(ra.buf, raw, CD_FRAMESIZE_RAW))
+ rc = -EFAULT;
+ if (rc)
+ break;
+
+ ra.buf += CD_FRAMESIZE_RAW;
+ ra.nframes -= 1;
+ lba++;
+ }
+ scsi_free(raw,2048+512);
+ return rc;
+ }
case BLKRAGET:
if (!arg)
return -EINVAL;
diff --git a/drivers/scsi/sr_vendor.c b/drivers/scsi/sr_vendor.c
index e8f73f1b2..82d5ba7d8 100644
--- a/drivers/scsi/sr_vendor.c
+++ b/drivers/scsi/sr_vendor.c
@@ -6,6 +6,12 @@
* the like) are to new to be included into the SCSI-II standard (to
* be exact: there is'nt anything in my draft copy).
*
+ * Aug 1997: Ha! Got a SCSI-3 cdrom spec across my fingers. SCSI-3 does
+ * multisession using the READ TOC command (like SONY).
+ *
+ * Rearranged stuff here: SCSI-3 is included allways, support
+ * for NEC/TOSHIBA/HP commands is optional.
+ *
* Gerd Knorr <kraxel@cs.tu-berlin.de>
*
* --------------------------------------------------------------------------
@@ -20,14 +26,15 @@
* - SONY: Detection and support of multisession CD's.
* added by Thomas Quinot <thomas@cuivre.freenix.fr>
*
- * - PIONEER, HITACHI, PLEXTOR, MATSHITA, TEAC, PHILIPS:
- * Known to work with SONY code.
+ * - PIONEER, HITACHI, PLEXTOR, MATSHITA, TEAC, PHILIPS: known to
+ * work with SONY (SCSI3 now) code.
*
* - HP: Much like SONY, but a little different... (Thomas)
* HP-Writers only ??? Maybe other CD-Writers work with this too ?
* HP 6020 writers now supported.
*/
+#include <linux/config.h>
#include <linux/errno.h>
#include <linux/string.h>
@@ -40,27 +47,33 @@
#include <linux/ucdrom.h>
#include "sr.h"
+#if 0
+# define DEBUG
+#endif
+
/* here are some constants to sort the vendors into groups */
-#define VENDOR_CAN_NOT_HANDLE 1 /* don't know how to handle */
+#define VENDOR_SCSI3 1 /* default: scsi-3 mmc */
+
#define VENDOR_NEC 2
#define VENDOR_TOSHIBA 3
-#define VENDOR_SONY_LIKE 4 /* much drives are Sony compatible */
-#define VENDOR_HP_4020 5 /* HP 4xxx writers, others too ?? */
-#define VENDOR_HP_6020 6 /* HP 6020 writers */
+#define VENDOR_HP_4020 4 /* HP 4xxx writers, others too ?? */
+#define VENDOR_HP_6020 5 /* HP 6020 writers */
#define VENDOR_ID (scsi_CDs[minor].vendor)
-#if 0
-#define DEBUG
-#endif
-
void
sr_vendor_init(int minor)
{
+#ifndef CONFIG_BLK_DEV_SR_VENDOR
+ VENDOR_ID = VENDOR_SCSI3;
+#else
char *vendor = scsi_CDs[minor].device->vendor;
char *model = scsi_CDs[minor].device->model;
-
+
+ /* default */
+ VENDOR_ID = VENDOR_SCSI3;
+
if ((!strncmp(vendor,"HP",2) || !strncmp(vendor,"PHILIPS",7)) &&
scsi_CDs[minor].device->type == TYPE_WORM) {
if (!strncmp(model,"CD-Writer 6020",14))
@@ -80,29 +93,33 @@ sr_vendor_init(int minor)
} else if (!strncmp (vendor, "TOSHIBA", 7)) {
VENDOR_ID = VENDOR_TOSHIBA;
- } else {
- /* most drives can handled like Sony ones, so we take
- * it as default */
- VENDOR_ID = VENDOR_SONY_LIKE;
-#ifdef DEBUG
- printk(KERN_DEBUG
- "sr: using \"Sony group\" multisession code\n");
-#endif
}
+#endif
}
/* small handy function for switching block length using MODE SELECT,
* used by sr_read_sector() */
-static int
-set_density_and_blocklength(int minor, unsigned char *buffer,
- int density, int blocklength)
+int
+sr_set_blocklength(int minor, int blocklength)
{
+ unsigned char *buffer; /* the buffer for the ioctl */
unsigned char cmd[12]; /* the scsi-command */
struct ccs_modesel_head *modesel;
- int rc;
+ int rc,density = 0;
+
+#ifdef CONFIG_BLK_DEV_SR_VENDOR
+ if (VENDOR_ID == VENDOR_TOSHIBA)
+ density = (blocklength > 2048) ? 0x81 : 0x83;
+#endif
+
+ buffer = (unsigned char *) scsi_malloc(512);
+ if (!buffer) return -ENOMEM;
+#ifdef DEBUG
+ printk("sr%d: MODE SELECT 0x%x/%d\n",minor,density,blocklength);
+#endif
memset(cmd,0,12);
cmd[0] = MODE_SELECT;
cmd[1] = (scsi_CDs[minor].device->lun << 5) | (1 << 4);
@@ -113,53 +130,17 @@ set_density_and_blocklength(int minor, unsigned char *buffer,
modesel->density = density;
modesel->block_length_med = (blocklength >> 8 ) & 0xff;
modesel->block_length_lo = blocklength & 0xff;
- rc = sr_do_ioctl(minor, cmd, buffer, sizeof(*modesel));
+ if (0 == (rc = sr_do_ioctl(minor, cmd, buffer, sizeof(*modesel), 0)))
+ scsi_CDs[minor].sector_size = blocklength;
#ifdef DEBUG
- if (rc)
- printk("sr: switching blocklength to %d bytes failed\n",
- blocklength);
+ else
+ printk("sr%d: switching blocklength to %d bytes failed\n",
+ minor,blocklength);
#endif
- return rc;
-}
-
-
-/* read a sector with other than 2048 bytes length
- * dest is assumed to be allocated with scsi_malloc
- *
- * XXX maybe we have to do some locking here.
- */
-
-int
-sr_read_sector(int minor, int lba, int blksize, unsigned char *dest)
-{
- unsigned char *buffer; /* the buffer for the ioctl */
- unsigned char cmd[12]; /* the scsi-command */
- int rc, density;
-
- density = (VENDOR_ID == VENDOR_TOSHIBA) ? 0x83 : 0;
-
- buffer = (unsigned char *) scsi_malloc(512);
- if (!buffer) return -ENOMEM;
-
- rc = set_density_and_blocklength(minor, buffer, density, blksize);
- if (!rc) {
- memset(cmd,0,12);
- cmd[0] = READ_10;
- cmd[1] = (scsi_CDs[minor].device->lun << 5);
- cmd[2] = (unsigned char)(lba >> 24) & 0xff;
- cmd[3] = (unsigned char)(lba >> 16) & 0xff;
- cmd[4] = (unsigned char)(lba >> 8) & 0xff;
- cmd[5] = (unsigned char) lba & 0xff;
- cmd[8] = 1;
- rc = sr_do_ioctl(minor, cmd, dest, blksize);
- set_density_and_blocklength(minor, buffer, density, 2048);
- }
-
scsi_free(buffer, 512);
return rc;
}
-
/* This function gets called after a media change. Checks if the CD is
multisession, asks for offset etc. */
@@ -169,7 +150,6 @@ int sr_cd_check(struct cdrom_device_info *cdi)
{
unsigned long sector,min,sec,frame;
unsigned char *buffer; /* the buffer for the ioctl */
- unsigned char *raw_sector;
unsigned char cmd[12]; /* the scsi-command */
int rc,is_xa,no_multi,minor;
@@ -187,16 +167,41 @@ int sr_cd_check(struct cdrom_device_info *cdi)
switch(VENDOR_ID) {
+ case VENDOR_SCSI3:
+ memset(cmd,0,12);
+ cmd[0] = READ_TOC;
+ cmd[1] = (scsi_CDs[minor].device->lun << 5);
+ cmd[8] = 12;
+ cmd[9] = 0x40;
+ rc = sr_do_ioctl(minor, cmd, buffer, 12, 0);
+ if (rc != 0)
+ break;
+ if ((buffer[0] << 8) + buffer[1] < 0x0a) {
+ printk(KERN_INFO "sr%d: Hmm, seems the drive "
+ "doesn't support multisession CD's\n",minor);
+ no_multi = 1;
+ break;
+ }
+ sector = buffer[11] + (buffer[10] << 8) +
+ (buffer[9] << 16) + (buffer[8] << 24);
+ if (buffer[6] <= 1) {
+ /* ignore sector offsets from first track */
+ sector = 0;
+ }
+ break;
+
+#ifdef CONFIG_BLK_DEV_SR_VENDOR
case VENDOR_NEC:
memset(cmd,0,12);
cmd[0] = 0xde;
cmd[1] = (scsi_CDs[minor].device->lun << 5) | 0x03;
cmd[2] = 0xb0;
- rc = sr_do_ioctl(minor, cmd, buffer, 0x16);
+ rc = sr_do_ioctl(minor, cmd, buffer, 0x16, 0);
if (rc != 0)
break;
if (buffer[14] != 0 && buffer[14] != 0xb0) {
- printk(KERN_INFO "sr (nec): Hmm, seems the cdrom doesn't support multisession CD's\n");
+ printk(KERN_INFO "sr%d: Hmm, seems the cdrom "
+ "doesn't support multisession CD's\n",minor);
no_multi = 1;
break;
}
@@ -205,20 +210,19 @@ int sr_cd_check(struct cdrom_device_info *cdi)
frame = BCD_TO_BIN(buffer[17]);
sector = min*CD_SECS*CD_FRAMES + sec*CD_FRAMES + frame;
break;
-
+
case VENDOR_TOSHIBA:
/* we request some disc information (is it a XA-CD ?,
* where starts the last session ?) */
memset(cmd,0,12);
cmd[0] = 0xc7;
cmd[1] = (scsi_CDs[minor].device->lun << 5) | 3;
- rc = sr_do_ioctl(minor, cmd, buffer, 4);
- if (rc == 0x28000002 &&
- !scsi_ioctl(scsi_CDs[minor].device,
- SCSI_IOCTL_TEST_UNIT_READY, NULL)) {
- printk(KERN_INFO "sr (toshiba): Hmm, seems the drive doesn't support multisession CD's\n");
- no_multi = 1;
- break;
+ rc = sr_do_ioctl(minor, cmd, buffer, 4, 0);
+ if (rc == -EINVAL) {
+ printk(KERN_INFO "sr%d: Hmm, seems the drive "
+ "doesn't support multisession CD's\n",minor);
+ no_multi = 1;
+ break;
}
if (rc != 0)
break;
@@ -239,102 +243,57 @@ int sr_cd_check(struct cdrom_device_info *cdi)
0x04 : 0x0c;
cmd[9] = 0x40;
rc = sr_do_ioctl(minor, cmd, buffer,
- (VENDOR_ID == VENDOR_HP_4020) ? 0x04 : 0x0c);
+ (VENDOR_ID == VENDOR_HP_4020) ? 0x04 : 0x0c, 0);
if (rc != 0) {
break;
}
if ((rc = buffer[2]) == 0) {
printk (KERN_WARNING
- "sr (hp): No finished session\n");
+ "sr%d: No finished session\n",minor);
break;
}
- if (VENDOR_ID == VENDOR_HP_4020) {
- cmd[0] = READ_TOC; /* Read TOC */
- cmd[1] = (scsi_CDs[minor].device->lun << 5);
- cmd[6] = rc & 0x7f; /* number of last session */
- cmd[8] = 0x0c;
- cmd[9] = 0x40;
- rc = sr_do_ioctl(minor, cmd, buffer, 12);
- if (rc != 0) {
- break;
- }
+ if (VENDOR_ID == VENDOR_HP_4020) {
+ cmd[0] = READ_TOC; /* Read TOC */
+ cmd[1] = (scsi_CDs[minor].device->lun << 5);
+ cmd[6] = rc & 0x7f; /* number of last session */
+ cmd[8] = 0x0c;
+ cmd[9] = 0x40;
+ rc = sr_do_ioctl(minor, cmd, buffer, 12, 0);
+ if (rc != 0) {
+ break;
+ }
}
sector = buffer[11] + (buffer[10] << 8) +
(buffer[9] << 16) + (buffer[8] << 24);
break;
-
- case VENDOR_SONY_LIKE:
- memset(cmd,0,12);
- cmd[0] = READ_TOC;
- cmd[1] = (scsi_CDs[minor].device->lun << 5);
- cmd[8] = 12;
- cmd[9] = 0x40;
- rc = sr_do_ioctl(minor, cmd, buffer, 12);
- if (rc != 0) {
- break;
- }
- if ((buffer[0] << 8) + buffer[1] < 0x0a) {
- printk(KERN_INFO "sr (sony): Hmm, seems the drive doesn't support multisession CD's\n");
- no_multi = 1;
- break;
- }
- sector = buffer[11] + (buffer[10] << 8) +
- (buffer[9] << 16) + (buffer[8] << 24);
- if (buffer[6] <= 1) {
- /* ignore sector offsets from first track */
- sector = 0;
- }
- break;
-
- case VENDOR_CAN_NOT_HANDLE:
- sector = 0;
- no_multi = 1;
- break;
+#endif /* CONFIG_BLK_DEV_SR_VENDOR */
default:
/* should not happen */
printk(KERN_WARNING
- "sr: unknown vendor code (%i), not initialized ?\n",
- VENDOR_ID);
+ "sr%d: unknown vendor code (%i), not initialized ?\n",
+ minor,VENDOR_ID);
sector = 0;
no_multi = 1;
break;
}
-
- scsi_CDs[minor].xa_flag = 0;
- if (CDS_AUDIO != sr_disk_status(cdi)) {
- /* read a sector in raw mode to check the sector format */
- raw_sector = (unsigned char *) scsi_malloc(2048+512);
- if (!buffer) return -ENOMEM;
- if (0 == sr_read_sector(minor,sector+16,CD_FRAMESIZE_RAW1,
- raw_sector)){
- is_xa = (raw_sector[3] == 0x02);
- if (sector > 0 && !is_xa)
- printk(KERN_INFO "sr: broken CD found: It is "
- "multisession, but has'nt XA sectors\n");
- } else {
- /* read a raw sector failed for some reason. */
- is_xa = (sector > 0);
- }
- scsi_free(raw_sector, 2048+512);
- }
-#ifdef DEBUG
- else printk("sr: audio CD found\n");
-#endif
-
scsi_CDs[minor].ms_offset = sector;
- scsi_CDs[minor].xa_flag = is_xa;
+ scsi_CDs[minor].xa_flag = 0;
+ if (CDS_AUDIO != sr_disk_status(cdi) && 1 == sr_is_xa(minor))
+ scsi_CDs[minor].xa_flag = 1;
+
+ if (2048 != scsi_CDs[minor].sector_size)
+ sr_set_blocklength(minor,2048);
if (no_multi)
cdi->mask |= CDC_MULTI_SESSION;
#ifdef DEBUG
- printk(KERN_DEBUG
- "sr: multisession offset=%lu, XA=%s\n",
- sector,is_xa ? "yes" : "no");
+ if (sector)
+ printk(KERN_DEBUG "sr%d: multisession offset=%lu\n",
+ minor,sector);
#endif
-
scsi_free(buffer, 512);
return rc;
}
diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c
index 07fa2bf58..b70522280 100644
--- a/drivers/scsi/st.c
+++ b/drivers/scsi/st.c
@@ -11,7 +11,7 @@
Copyright 1992 - 1997 Kai Makisara
email Kai.Makisara@metla.fi
- Last modified: Tue May 27 22:29:00 1997 by makisara@home
+ Last modified: Wed Nov 5 23:39:52 1997 by makisara@home
Some small formal changes - aeb, 950809
*/
@@ -230,18 +230,9 @@ st_sleep_done (Scsi_Cmnd * SCpnt)
}
else
(STp->buffer)->last_result = SCpnt->result;
-#if 0
- if ((STp->buffer)->writing) {
- /* Process errors before releasing request */
- (STp->buffer)->last_result_fatal = st_chk_result(SCpnt);
- SCpnt->request.rq_status = RQ_INACTIVE;
- }
- else
- SCpnt->request.rq_status = RQ_SCSI_DONE;
-#else
+
SCpnt->request.rq_status = RQ_SCSI_DONE;
(STp->buffer)->last_SCpnt = SCpnt;
-#endif
#if DEBUG
STp->write_pending = 0;
@@ -645,8 +636,10 @@ scsi_tape_open(struct inode * inode, struct file * filp)
}
if ((STp->buffer)->last_result_fatal != 0) {
- if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
- (SCpnt->sense_buffer[2] & 0x0f) == NO_TAPE) {
+ if ((STp->device)->scsi_level >= SCSI_2 &&
+ (SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
+ (SCpnt->sense_buffer[2] & 0x0f) == NOT_READY &&
+ SCpnt->sense_buffer[12] == 0x3a) { /* Check ASC */
STp->ready = ST_NO_TAPE;
} else
STp->ready = ST_NOT_READY;
@@ -872,8 +865,6 @@ scsi_tape_close(struct inode * inode, struct file * filp)
if (!SCpnt)
goto out;
- SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */
-
if ((STp->buffer)->last_result_fatal != 0 &&
((SCpnt->sense_buffer[0] & 0x70) != 0x70 ||
(SCpnt->sense_buffer[2] & 0x4f) != 0x40 ||
@@ -882,11 +873,13 @@ scsi_tape_close(struct inode * inode, struct file * filp)
SCpnt->sense_buffer[5] |
SCpnt->sense_buffer[6]) == 0))) {
/* Filter out successful write at EOM */
+ SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */
printk(KERN_ERR "st%d: Error on write filemark.\n", dev);
if (result == 0)
result = (-EIO);
}
else {
+ SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */
if (STps->drv_file >= 0)
STps->drv_file++ ;
STps->drv_block = 0;
@@ -955,12 +948,12 @@ out:
/* Write command */
- static long
-st_write(struct inode * inode, struct file * filp, const char * buf,
- unsigned long count)
+static ssize_t
+st_write(struct file * filp, const char * buf, size_t count, loff_t *ppos)
{
- long total;
- int i, do_count, blks, retval, transfer;
+ struct inode *inode = filp->f_dentry->d_inode;
+ ssize_t total;
+ ssize_t i, do_count, blks, retval, transfer;
int write_threshold;
int doing_write = 0;
static unsigned char cmd[10];
@@ -971,9 +964,18 @@ st_write(struct inode * inode, struct file * filp, const char * buf,
ST_partstat * STps;
int dev = TAPE_NR(inode->i_rdev);
+ if (ppos != &filp->f_pos) {
+ /* "A request was outside the capabilities of the device." */
+ return -ENXIO;
+ }
+
STp = &(scsi_tapes[dev]);
- if (STp->ready != ST_READY)
- return (-EIO);
+ if (STp->ready != ST_READY) {
+ if (STp->ready == ST_NO_TAPE)
+ return (-ENOMEDIUM);
+ else
+ return (-EIO);
+ }
STm = &(STp->modes[STp->current_mode]);
if (!STm->defined)
return (-ENXIO);
@@ -1254,7 +1256,7 @@ st_write(struct inode * inode, struct file * filp, const char * buf,
/* Read data from the tape. Returns zero in the normal case, one if the
eof status has changed, and the negative error code in case of a
fatal error. Otherwise updates the buffer and the eof state. */
- static long
+static long
read_tape(struct inode *inode, long count, Scsi_Cmnd **aSCpnt)
{
int transfer, blks, bytes;
@@ -1344,7 +1346,7 @@ read_tape(struct inode *inode, long count, Scsi_Cmnd **aSCpnt)
}
else {
SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */
- SCpnt = NULL;
+ SCpnt = *aSCpnt = NULL;
if (transfer == blks) { /* We did not get anything, error */
printk(KERN_NOTICE "st%d: Incorrect block size.\n", dev);
if (STps->drv_block >= 0)
@@ -1441,11 +1443,12 @@ read_tape(struct inode *inode, long count, Scsi_Cmnd **aSCpnt)
/* Read command */
- static long
-st_read(struct inode * inode, struct file * filp, char * buf, unsigned long count)
+static ssize_t
+st_read(struct file * filp, char * buf, size_t count, loff_t *ppos)
{
- long total;
- int i, transfer;
+ struct inode * inode = filp->f_dentry->d_inode;
+ ssize_t total;
+ ssize_t i, transfer;
int special;
Scsi_Cmnd * SCpnt = NULL;
Scsi_Tape * STp;
@@ -1453,9 +1456,18 @@ st_read(struct inode * inode, struct file * filp, char * buf, unsigned long coun
ST_partstat * STps;
int dev = TAPE_NR(inode->i_rdev);
+ if (ppos != &filp->f_pos) {
+ /* "A request was outside the capabilities of the device." */
+ return -ENXIO;
+ }
+
STp = &(scsi_tapes[dev]);
- if (STp->ready != ST_READY)
- return (-EIO);
+ if (STp->ready != ST_READY) {
+ if (STp->ready == ST_NO_TAPE)
+ return (-ENOMEDIUM);
+ else
+ return (-EIO);
+ }
STm = &(STp->modes[STp->current_mode]);
if (!STm->defined)
return (-ENXIO);
@@ -1533,7 +1545,7 @@ st_read(struct inode * inode, struct file * filp, char * buf, unsigned long coun
if ((STp->buffer)->buffer_bytes > 0) {
#if DEBUG
if (debugging && STps->eof != ST_NOEOF)
- printk(ST_DEB_MSG "st%d: EOF up (%d). Left %d, needed %ld.\n", dev,
+ printk(ST_DEB_MSG "st%d: EOF up (%d). Left %d, needed %d.\n", dev,
STps->eof, (STp->buffer)->buffer_bytes, count - total);
#endif
transfer = (STp->buffer)->buffer_bytes < count - total ?
@@ -1875,8 +1887,12 @@ st_int_ioctl(struct inode * inode,
int dev = TAPE_NR(inode->i_rdev);
STp = &(scsi_tapes[dev]);
- if (STp->ready != ST_READY && cmd_in != MTLOAD)
- return (-EIO);
+ if (STp->ready != ST_READY && cmd_in != MTLOAD) {
+ if (STp->ready == ST_NO_TAPE)
+ return (-ENOMEDIUM);
+ else
+ return (-EIO);
+ }
timeout = STp->long_timeout;
STps = &(STp->ps[STp->partition]);
fileno = STps->drv_file;
@@ -2512,6 +2528,7 @@ set_location(struct inode * inode, unsigned int block, int partition,
if (!SCpnt)
return (-EBUSY);
+ SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */
STps->drv_block = STps->drv_file = (-1);
STps->eof = ST_NOEOF;
if ((STp->buffer)->last_result_fatal != 0) {
@@ -2537,7 +2554,6 @@ set_location(struct inode * inode, unsigned int block, int partition,
STps->drv_block = STps->drv_file = 0;
result = 0;
}
- SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */
return result;
}
diff --git a/drivers/scsi/st.h b/drivers/scsi/st.h
index c25c80cc7..c3e71f293 100644
--- a/drivers/scsi/st.h
+++ b/drivers/scsi/st.h
@@ -64,7 +64,6 @@ typedef struct {
unsigned capacity;
struct wait_queue * waiting;
Scsi_Device* device;
- Scsi_Cmnd SCpnt;
struct semaphore sem;
ST_buffer * buffer;