diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1997-12-06 23:51:34 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1997-12-06 23:51:34 +0000 |
commit | 230e5ab6a084ed50470f101934782dbf54b0d06b (patch) | |
tree | 5dd821c8d33f450470588e7a543f74bf74306e9e /drivers/scsi | |
parent | c9b1c8a64c6444d189856f1e26bdcb8b4cd0113a (diff) |
Merge with Linux 2.1.67.
Diffstat (limited to 'drivers/scsi')
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; |