diff -urN linux-2.2.10-ref/drivers/scsi/Config.in linux-2.2.10/drivers/scsi/Config.in --- linux-2.2.10-ref/drivers/scsi/Config.in Wed Jun 23 14:14:16 1999 +++ linux-2.2.10/drivers/scsi/Config.in Wed Jun 23 14:34:53 1999 @@ -75,6 +75,17 @@ bool ' ppa/imm option - Assume slow parport control register' CONFIG_SCSI_IZIP_SLOW_CTR fi fi + +dep_tristate 'Parallel Port SCSI adapters' CONFIG_PPSCSI $CONFIG_SCSI m +if [ "$CONFIG_PPSCSI" != "n" ]; then + dep_tristate ' Adaptec APA-348 adapter' CONFIG_PPSCSI_T348 $CONFIG_PPSCSI + dep_tristate ' Adaptec APA-358 adapter' CONFIG_PPSCSI_T358 $CONFIG_PPSCSI + dep_tristate ' OnSpec 90c26 adapter' CONFIG_PPSCSI_ONSCSI $CONFIG_PPSCSI + dep_tristate ' Shining SparSCI adapter' CONFIG_PPSCSI_SPARCSI $CONFIG_PPSCSI + dep_tristate ' Shuttle EPSA-2 adapter' CONFIG_PPSCSI_EPSA2 $CONFIG_PPSCSI + dep_tristate ' Shuttle EPST adapter' CONFIG_PPSCSI_EPST $CONFIG_PPSCSI +fi + dep_tristate 'NCR53c406a SCSI support' CONFIG_SCSI_NCR53C406A $CONFIG_SCSI dep_tristate 'symbios 53c416 SCSI support' CONFIG_SCSI_SYM53C416 $CONFIG_SCSI if [ "$CONFIG_PCI" = "y" ]; then diff -urN linux-2.2.10-ref/drivers/scsi/Makefile linux-2.2.10/drivers/scsi/Makefile --- linux-2.2.10-ref/drivers/scsi/Makefile Thu Apr 29 14:53:41 1999 +++ linux-2.2.10/drivers/scsi/Makefile Wed Jun 23 14:33:24 1999 @@ -265,6 +265,70 @@ endif endif +# +# new ppSCSI subsystem +# + +ifeq ($(CONFIG_PPSCSI),y) +L_OBJS += ppscsi.o +else + ifeq ($(CONFIG_PPSCSI),m) + M_OBJS += ppscsi.o + endif +endif + +ifeq ($(CONFIG_PPSCSI_T348),y) +L_OBJS += t348.o +else + ifeq ($(CONFIG_PPSCSI_T348),m) + M_OBJS += t348.o + endif +endif + +ifeq ($(CONFIG_PPSCSI_T358),y) +L_OBJS += t358.o +else + ifeq ($(CONFIG_PPSCSI_T358),m) + M_OBJS += t358.o + endif +endif + +ifeq ($(CONFIG_PPSCSI_ONSCSI),y) +L_OBJS += onscsi.o +else + ifeq ($(CONFIG_PPSCSI_ONSCSI),m) + M_OBJS += onscsi.o + endif +endif + +ifeq ($(CONFIG_PPSCSI_EPSA2),y) +L_OBJS += epsa2.o +else + ifeq ($(CONFIG_PPSCSI_EPSA2),m) + M_OBJS += epsa2.o + endif +endif + +ifeq ($(CONFIG_PPSCSI_EPST),y) +L_OBJS += epst.o +else + ifeq ($(CONFIG_PPSCSI_EPST),m) + M_OBJS += epst.o + endif +endif + +ifeq ($(CONFIG_PPSCSI_SPARCSI),y) +L_OBJS += sparcsi.o +else + ifeq ($(CONFIG_PPSCSI_SPARCSI),m) + M_OBJS += sparcsi.o + endif +endif + +# +# end of ppSCSI stuff +# + ifeq ($(CONFIG_SCSI_QLOGIC_FAS),y) L_OBJS += qlogicfas.o else diff -urN linux-2.2.10-ref/drivers/scsi/epsa2.c linux-2.2.10/drivers/scsi/epsa2.c --- linux-2.2.10-ref/drivers/scsi/epsa2.c Wed Dec 31 19:00:00 1969 +++ linux-2.2.10/drivers/scsi/epsa2.c Wed Jun 23 14:31:38 1999 @@ -0,0 +1,509 @@ +/* + epsa2.c (c) 1996-1999 Grant Guenther + + This is the ppSCSI protocol module for the Shuttle + Technologies EPSA2 parallel port SCSI adapter. EPSA2 is + a predecessor to the EPST. It uses slightly different + command encoding and has a less elaborate internal register + model. + +*/ + +#define EPSA2_VERSION "0.90" + +#define PPSC_BASE +#define PPSC_HA_MODULE + +#include "ppscsi.h" + +#define EPSA2_VER_CODE 0xb1 + +#define j44(a,b) (((a>>4)&0x0f)+(b&0xf0)) +#define j53(a,b) (((a>>3)&0x1f)+((b<<4)&0xe0)) + +static char epsa2_map[256]; /* status bits permutation */ + +static void epsa2_init( PHA *pha) + +/* { REQ, BSY, MSG, CD, IO} */ + +{ char key[5] = {0x20,0x40,0x04,0x02,0x01}; + + ppsc_make_map(epsa2_map,key,0); + sprintf(pha->ident,"epsa2 %s (%s), Shuttle EPSA2", + EPSA2_VERSION,PPSC_H_VERSION); +} + +static void epsa2_write_regr( PHA *pha, int regr, int value) + +{ switch (pha->mode) { + + case 0: + case 1: + case 2: w0(0x70+regr); w2(1); w0(value); w2(4); + break; + + case 3: + case 4: + case 5: w3(0x40+regr); w4(value); w2(4); + break; + + } +} + +static int epsa2_read_regr( PHA *pha, int regr ) + +{ int a, b; + + switch (pha->mode) { + + case 0: w0(0x40+regr); w2(1); w2(3); + a = r1(); w2(4); b = r1(); + return j44(a,b); + + case 1: w0(0x60+regr); w2(1); w2(4); + a = r1(); b = r2(); w0(0xff); + return j53(a,b); + + case 2: w0(0x50+regr); w2(1); w2(0x25); + a = r0(); w2(4); + return a; + + case 3: + case 4: + case 5: w3(regr); w2(0x24); a = r4(); w2(4); + return a; + + } + + return -1; +} + +/* for performance reasons, these block transfer functions make + some assumptions about the behaviour of the SCSI devices. In + particular, DMA transfers are assumed not to stall within the + last few bytes of a block ... +*/ + +static int epsa2_read_block( PHA *pha, char *buf, int len) + +{ int t, k, p, a, b; + + k = 0; + + switch (pha->mode) { + + case 0: w0(7); w2(1); w2(3); w0(0xff); + p = 1; + while (k < len) { + w2(6+p); a = r1(); + if (a & 8) b = a; else { w2(4+p); b = r1(); } + buf[k++] = j44(a,b); + p = 1 - p; + if (!(k % 16)) { + w0(0xfe); t = r1(); w0(0xff); + if (t & 8) break; + } + } + w0(0); w2(4); + break; + + case 1: w0(0x27); w2(1); w2(5); w0(0xff); + p = 0; + while (k < len) { + a = r1(); b = r2(); + buf[k++] = j53(a,b); + w2(4+p); + p = 1 - p; + if (!(k % 16)) { + w0(0xfe); t = r1(); w0(0xff); + if (t & 8) break; + } + } + w0(0); w2(4); + break; + + case 2: w0(0x17); w2(1); + p = 1; + while (k < len) { + w2(0x24+p); + buf[k++] = r0(); + p = 1 - p; + if ((!(k % 16)) && (r1() & 8)) break; + } + w2(6); w2(4); + break; + + case 3: w3(6); w2(0x24); + while (k < len) { + buf[k++] = r4(); + if ((!(k % 16)) && (r1() & 8)) break; + } + w2(4); + break; + + case 4: w3(6); w2(0x24); + while (k < len) { + if ((len - k) > 1) { + *((u16 *)(&buf[k])) = r4w(); + k += 2; + } else { + buf[k++] = r4(); + } + if ((!(k % 16)) && (r1() & 8)) break; + } + w2(4); + break; + + case 5: w3(6); w2(0x24); + while (k < len) { + if ((len - k) > 3) { + *((u32 *)(&buf[k])) = r4l(); + k += 4; + } else { + buf[k++] = r4(); + } + if ((!(k % 16)) && (r1() & 8)) break; + } + w2(4); + break; + } + + return k; +} + +static int epsa2_write_block( PHA *pha, char *buf, int len) + +{ int p, k; + + k = 0; + + switch (pha->mode) { + + case 0: + case 1: + case 2: w0(0x37); w2(1); + p = 1; + while (k < len) { + w2(4+p); + w0(buf[k++]); + p = 1 - p; + if ((!(k % 16)) && (r1() & 8)) break; + } + w2(5); w2(7); w2(4); + break; + + case 3: w3(0x46); + while (k < len) { + w4(buf[k++]); + if ((!(k % 16)) && (r1() & 8)) break; + } + w2(4); + break; + + case 4: w3(0x46); + while (k < len) { + if ((len - k) > 1) { + w4w(*((u16 *)(&buf[k]))); + k += 2; + } else { + w4(buf[k++]); + } + if ((!(k % 16)) && (r1() & 8)) break; + } + w2(4); + break; + + case 5: w3(0x46); + while (k < len) { + if ((len - k) > 3) { + w4l(*((u32 *)(&buf[k]))); + k += 4; + } else { + w4(buf[k++]); + } + if ((!(k % 16)) && (r1() & 8)) break; + } + w2(4); + break; + } + + return k; +} + +#define WR(r,v) epsa2_write_regr(pha,r,v) +#define RR(r) (epsa2_read_regr(pha,r)) + +#define CPP(x) w2(4);w0(0x22);w0(0xaa);w0(0x55);w0(0);w0(0xff);\ + w0(0x87);w0(0x78);w0(x);w2(5);w2(4);w0(0xff); + +static void epsa2_connect( PHA *pha) + +{ CPP(0x40); CPP(0xe0); + + w0(0x73); w2(1); w0(0); w2(4); + w0(0x72); w2(1); w0(0x40); w2(4); + + w0(0); w2(1); w2(4); + + CPP(0x50); CPP(0x48); + + switch (pha->mode) { + + case 0: WR(7,0x82); + break; + + case 1: + case 2: WR(7,0xa2); + break; + + case 3: + case 4: + case 5: CPP(0x30); CPP(0x20); + WR(7,0xa3); + break; + } + + w2(4); +} + +static void epsa2_disconnect( PHA *pha) + +{ switch (pha->mode) { + + case 0: WR(7,2); WR(2,0); + break; + + case 1: + case 2: WR(7,0x22); WR(2,0); + break; + + case 3: + case 4: + case 5: WR(7,0x23); w2(4); + w0(0x72); w2(1); w0(0); w2(4); + break; + } + + CPP(0x30); CPP(0x40); +} + +static int epsa2_test_proto( PHA *pha) + +{ int i, j, e; + char wb[16], rb[16]; + + e = 0; + + epsa2_connect(pha); + i = RR(7); + if (V_PROBE) printk("%s: version code reads: 0x%x\n",pha->device,i); + epsa2_disconnect(pha); + + if (i != EPSA2_VER_CODE) return 1; + + epsa2_connect(pha); + + for (j=0;j<200;j++) { + for (i=0;i<16;i++) { wb[i] = i+j; rb[i] = i+j+6; } + WR(5,1); + epsa2_write_block(pha,wb,16); + udelay(100); + WR(5,0x11); + epsa2_read_block(pha,rb,16); + for (i=0;i<16;i++) if (wb[i] != rb[i]) e++; + } + + epsa2_disconnect(pha); + + if (V_FULL) + printk("%s: test port 0x%x mode %d errors %d\n", + pha->device,pha->port,pha->mode,e); + + return e; +} + +/* The EPSA2 contains a core SCSI controller that is very + similar to the NCR 5380. Some bits have been shuffled + around, but the basic structure is the same. +*/ + +static int epsa2_select( PHA *pha, int initiator, int target) + +{ WR(4,(1< + + This is the ppSCSI protocol module for the Shuttle + Technologies EPST parallel port SCSI adapter. + +*/ + +#define EPST_VERSION "0.91" + +#define PPSC_BASE +#define PPSC_HA_MODULE + +#include "ppscsi.h" + +#define EPST_VER_CODE 0xb2 + +#define j44(a,b) (((a>>4)&0x0f)+(b&0xf0)) +#define j53(a,b) (((a>>3)&0x1f)+((b<<4)&0xe0)) + +static char epst_map[256]; /* status bits permutation */ + +static void epst_init( PHA *pha) + +/* { REQ, BSY, MSG, CD, IO} */ + +{ char key[5] = {0x20,0x40,0x04,0x02,0x01}; + + ppsc_make_map(epst_map,key,0); + sprintf(pha->ident,"epst %s (%s), Shuttle EPST", + EPST_VERSION,PPSC_H_VERSION); +} + +static void epst_write_regr( PHA *pha, int regr, int value) + +{ switch (pha->mode) { + + case 0: + case 1: + case 2: w0(0x60+regr); w2(1); w0(value); w2(4); + break; + + case 3: + case 4: + case 5: w3(0x40+regr); w4(value); + break; + + } +} + +static int epst_read_regr( PHA *pha, int regr ) + +{ int a, b; + + switch (pha->mode) { + + case 0: w0(regr); w2(1); w2(3); + a = r1(); w2(4); b = r1(); + return j44(a,b); + + case 1: w0(0x40+regr); w2(1); w2(4); + a = r1(); b = r2(); w0(0xff); + return j53(a,b); + + case 2: w0(0x20+regr); w2(1); w2(0x25); + a = r0(); w2(4); + return a; + + case 3: + case 4: + case 5: w3(regr); w2(0x24); a = r4(); + return a; + + } + + return -1; +} + +/* for performance reasons, these block transfer functions make + some assumptions about the behaviour of the SCSI devices. In + particular, DMA transfers are assumed not to stall within the + last few bytes of a block ... +*/ + +static int epst_read_block( PHA *pha, char *buf, int len) + +{ int t, k, p, a, b; + + k = 0; + + switch (pha->mode) { + + case 0: w0(7); w2(1); w2(3); w0(0xff); + p = 1; + while (k < len) { + w2(6+p); a = r1(); + if (a & 8) b = a; else { w2(4+p); b = r1(); } + buf[k++] = j44(a,b); + p = 1 - p; + if (!(k % 16)) { + w0(0xfe); t = r1(); w0(0xff); + if (t & 8) break; + } + } + w0(0); w2(4); + break; + + case 1: w0(0x47); w2(1); w2(5); w0(0xff); + p = 0; + while (k < len) { + a = r1(); b = r2(); + buf[k++] = j53(a,b); + w2(4+p); + p = 1 - p; + if (!(k % 16)) { + w0(0xfe); t = r1(); w0(0xff); + if (t & 8) break; + } + } + w0(0); w2(4); + break; + + case 2: w0(0x27); w2(1); + p = 1; + while (k < len) { + w2(0x24+p); + buf[k++] = r0(); + p = 1 - p; + if ((!(k % 16)) && (r1() & 8)) break; + } + w2(6); w2(4); + break; + + case 3: w3(0x80); w2(0x24); + while (k < len) { + buf[k++] = r4(); + if ((!(k % 16)) && (r1() & 8)) break; + } + w2(4); + break; + + case 4: w3(0x80); w2(0x24); + while (k < len) { + if ((len - k) > 1) { + *((u16 *)(&buf[k])) = r4w(); + k += 2; + } else { + buf[k++] = r4(); + } + if ((!(k % 16)) && (r1() & 8)) break; + } + w2(4); + break; + + case 5: w3(0x80); w2(0x24); + while (k < len) { + if ((len - k) > 3) { + *((u32 *)(&buf[k])) = r4l(); + k += 4; + } else { + buf[k++] = r4(); + } + if ((!(k % 16)) && (r1() & 8)) break; + } + w2(4); + break; + } + + return k; +} + +static int epst_write_block( PHA *pha, char *buf, int len) + +{ int p, k; + + k = 0; + + switch (pha->mode) { + + case 0: + case 1: + case 2: w0(0x67); w2(1); + p = 1; + while (k < len) { + w2(4+p); + w0(buf[k++]); + p = 1 - p; + if ((!(k % 16)) && (r1() & 8)) break; + } + w2(5); w2(7); w2(4); + break; + + case 3: w3(0xc0); + while (k < len) { + w4(buf[k++]); + if ((!(k % 16)) && (r1() & 8)) break; + } + w2(4); + break; + + case 4: w3(0xc0); + while (k < len) { + if ((len - k) > 1) { + w4w(*((u16 *)(&buf[k]))); + k += 2; + } else { + w4(buf[k++]); + } + if ((!(k % 16)) && (r1() & 8)) break; + } + w2(4); + break; + + case 5: w3(0xc0); + while (k < len) { + if ((len - k) > 3) { + w4l(*((u32 *)(&buf[k]))); + k += 4; + } else { + w4(buf[k++]); + } + if ((!(k % 16)) && (r1() & 8)) break; + } + w2(4); + break; + } + + return k; +} + +#define WR(r,v) epst_write_regr(pha,r,v) +#define RR(r) (epst_read_regr(pha,r)) + +#define CPP(x) w2(4);w0(0x22);w0(0xaa);w0(0x55);w0(0);w0(0xff);\ + w0(0x87);w0(0x78);w0(x);w2(5);w2(4);w0(0xff); + +static void epst_connect( PHA *pha) + +{ w2(4); + CPP(0x40); CPP(0xe0); + w0(0); w2(1); w2(3); w2(4); + + if (pha->mode >= 3) { + w0(0); w2(1); w2(3); w2(4); w2(0xc); + w0(0x40); w2(6); w2(7); w2(4); + } + + WR(0x1d,0x20); WR(0x1d,0); /* clear the ring buffer */ + WR(0xa,0x1e); /* set up PDMA */ + WR(0xc,4); /* enable status bits */ + WR(8,2); /* deglitch timing */ +} + +static void epst_disconnect( PHA *pha) + +{ CPP(0x30); w2(4); + CPP(0x40); w2(4); +} + +#define Wsr(r,v) WR(0x18+r,v) +#define Rsr(r) (RR(0x18+r)) + +static int epst_test_proto( PHA *pha) + +{ int i, j, e; + char wb[16], rb[16]; + + e = 0; + + epst_connect(pha); + i = RR(0xb); + if (V_PROBE) printk("%s: version code reads: 0x%x\n",pha->device,i); + epst_disconnect(pha); + + if (i != EPST_VER_CODE) return 1; + + epst_connect(pha); + + for (j=0;j<200;j++) { + for (i=0;i<16;i++) { wb[i] = i+j; rb[i] = i+j+6; } + Wsr(5,1); + epst_write_block(pha,wb,16); + Wsr(5,0x11); + epst_read_block(pha,rb,16); + for (i=0;i<16;i++) if (wb[i] != rb[i]) e++; + } + + epst_disconnect(pha); + + if (V_FULL) + printk("%s: test port 0x%x mode %d errors %d\n", + pha->device,pha->port,pha->mode,e); + + return e; +} + +/* The EPST contains a core SCSI controller that is very + similar to the NCR 5380. Some bits have been shuffled + around, but the basic structure is the same. +*/ + +static int epst_select( PHA *pha, int initiator, int target) + +{ Wsr(4,(1< + + This is the ppSCSI protocol module for the OnSpec 90c26 + in its SCSI adapter mode. +*/ + +#define ONSCSI_VERSION "0.90" + +#define PPSC_BASE +#define PPSC_HA_MODULE + +#include "ppscsi.h" + +#define ONSCSI_REP_COUNT 256 + +#define TOGL pha->private[0] + +static char onscsi_map[256]; /* status bits permutation */ + +static void onscsi_init( PHA *pha) + +/* { REQ, BSY, MSG, CD, IO} */ + +{ char key[5] = {0x10,0x01,0x20,0x40,0x80}; + + ppsc_make_map(onscsi_map,key,0); + sprintf(pha->ident,"onscsi %s (%s), OnSpec 90c26", + ONSCSI_VERSION,PPSC_H_VERSION); +} + +#define j44(a,b) ((b&0xf0)|((a>>4)&0x0f)) + +#define CMD(x) w0(x);w2(5);w2(0xd);w2(5);w2(0xd);w2(5);w2(4); +#define VAL(v) w0(v);w2(5);w2(7);w2(5);w2(4); + +static inline void onscsi_opcode( PHA *pha, int x ) + +{ if (pha->mode < 2) { + CMD(x); + } else { + w3(x); + } +} + +#define OP(x) onscsi_opcode(pha,x) +#define FULLBYTE (pha->mode > 0) + +static void onscsi_write_regr( PHA *pha, int r, int v) + +{ onscsi_opcode(pha,r); + + if (pha->mode < 2) { + VAL(v); + } else { + w2(5); w4(v); w2(4); + } +} + +static inline int onscsi_read_nybble( PHA *pha) + +{ int a, b; + + w2(6); a = r1(); w2(4); + w2(6); b = r1(); w2(4); + + return j44(a,b); +} + +static int onscsi_read_regr( PHA *pha, int r ) + +{ int v = -1; + + onscsi_opcode(pha,r); + + switch (pha->mode) { + + case 0: v = onscsi_read_nybble(pha); + break; + + case 1: w2(0x26); v = r0(); w2(4); + break; + + case 2: + case 3: + case 4: w2(0x24); v = r4(); w2(4); + break; + + } + + return v; +} + +#define RR(r) onscsi_read_regr(pha,r) +#define WR(r,v) onscsi_write_regr(pha,r,v) + +static void onscsi_write_block( PHA *pha, char *buf, int n) + +{ int i; + + w2(5+TOGL); + + switch (pha->mode) { + + case 0: + case 1: for (i=0;imode) { + + case 0: w2(4); + for (i=0;isaved_r0 = r0(); + pha->saved_r2 = r2(); + + CPP(0x20,4); + + CMD(2); VAL(0); + CMD(2); VAL(FULLBYTE); + + WR(2,FULLBYTE); +} + +static void onscsi_disconnect( PHA *pha) + +{ WR(3,0); WR(7,0x48); + OP(4); + CPP(0x30,pha->saved_r2); + + w0(pha->saved_r0); + w2(pha->saved_r2); +} + +static int onscsi_test_proto( PHA *pha) + +{ int i, k, j; + char wbuf[16], rbuf[16]; + int e = 0; + + pha->saved_r0 = r0(); + pha->saved_r2 = r2(); + + CPP(0x30,pha->saved_r2); + CPP(0x0,pha->saved_r2); + + w0(0xfe);w0(0xaa);w0(0x55);w0(0);w0(0xff); + i = ((r1() & 0xf0) << 4); w0(0x87); + i |= (r1() & 0xf0); w0(0x78); + w0(0x20);w2(5); + i |= ((r1() & 0xf0) >> 4); + w2(4);w0(0xff); + + if (V_PROBE) printk("%s: signature 0x%x\n",pha->device,i); + + if (i == 0xb5f) { + + CMD(2); VAL(FULLBYTE); + + w2(4); w2(0xc); udelay(100); w2(4); udelay(100); + + CMD(2); VAL(0); + CMD(2); VAL(FULLBYTE); + + WR(2,FULLBYTE); + + k = RR(4); + + if (V_PROBE) + printk("%s: OnSpec 90c26 version %x\n",pha->device,k); + + } + + CPP(0x30,pha->saved_r2); + + w0(pha->saved_r0); + w2(pha->saved_r2); + + if (i != 0xb5f) return 1; + + onscsi_connect(pha); + + for (k=0;kmode == 0) WR(2,0x30); + if (pha->mode == 1) WR(2,0x10); + + WR(3,0); WR(7,0x48); + + WR(3,1); WR(7,0x48); OP(5); + TOGL = 0; + onscsi_write_block(pha,wbuf,16); + w2(4); + + if (pha->mode == 0) WR(2,0); + if (pha->mode == 1) WR(2,0x11); + + WR(3,5); WR(7,0x48); OP(5); + TOGL = 0; + onscsi_read_block(pha,rbuf,16); + w2(4); + + for (j=0;j<16;j++) + if (rbuf[j] != wbuf[j]) e++; + + } + + onscsi_disconnect(pha); + +#ifdef EXPERIMENT + + /* enable this to see how the buffer status bits work */ + + if (pha->mode == 2) { + + onscsi_connect(pha); + + WR(3,0); WR(7,0x48); + WR(3,1); WR(7,0x48); OP(5); + w2(5); + + for (k=0;k<16;k++) { + j = r1(); w4(k); + printk("%2x:%d ",j,k); + } + printk("\n"); + + w2(4); WR(3,5); WR(7,0x48); OP(5); + w2(0x24); + + for (k=0;k<16;k++) { + j = r1(); + printk("%2x:%d ",j,r4()); + } + printk("\n"); + + w2(4); + + onscsi_disconnect(pha); + } + + if (pha->mode == 1) { + + onscsi_connect(pha); + + WR(2,0x11); + WR(3,0); WR(7,0x48); + WR(3,1); WR(7,0x48); OP(5); + w2(5); i = 0; + + for (k=0;k<16;k++) { + j = r1(); w0(k); i = 2 - i; w2(5+i); + printk("%2x:%d ",j,k); + } + printk("%2x.\n",r1()); + + w2(4); + + WR(2,0x11); + WR(3,0); WR(7,0x48); + WR(3,5); WR(7,0x48); OP(5); + w2(0x24); i = 0; + + printk("%2x ",r1()); + for (k=0;k<16;k++) { + i = 2 - i; w2(0x24+i); j = r1(); + printk("%2x:%d ",j,r0()); + } + printk("\n"); + + w2(4); + + onscsi_disconnect(pha); + } + +#endif + + if (V_FULL) + printk("%s: test port 0x%x mode %d errors %d\n", + pha->device,pha->port,pha->mode,e); + + return e; +} + +static int onscsi_select( PHA *pha, int initiator, int target) + +{ WR(1,0); + WR(2,0x80+FULLBYTE); + if (RR(1) != 0) return -1; + WR(0,((1 << initiator) | (1 << target))); + WR(1,2); + return 0; +} + +static int onscsi_test_select( PHA *pha) + +{ return ((RR(1) & 3) == 3); +} + +static void onscsi_select_finish( PHA *pha) + +{ WR(1,0); +} + +static void onscsi_deselect( PHA *pha) + +{ WR(1,0); + /* WR(2,0x20+FULLBYTE); */ + WR(2,FULLBYTE); + WR(3,0); WR(7,0x48); +} + +static int onscsi_get_bus_status( PHA *pha) + +{ WR(2,0x20+FULLBYTE); + return onscsi_map[RR(1)]; +} + +static void onscsi_slow_start( PHA *pha, char *val) + +{ pha->priv_flag = (RR(1) & 0x80); + pha->priv_ptr = val; + + if (pha->priv_flag) WR(2,0x20); else WR(2,0x21); + + OP(0); + + if (pha->priv_flag) { + w2(6); + } else { + w0(*val); w2(5); w2(7); + } +} + +static int onscsi_slow_done( PHA *pha) + +{ return (!(r1() & 8)); +} + +static void onscsi_slow_end( PHA *pha) + +{ if (pha->priv_flag) { + *pha->priv_ptr = onscsi_read_nybble(pha); + } else { + w2(5); w2(4); + } +} + +static void onscsi_start_block( PHA *pha, int rd) + +{ pha->priv_flag = rd; + + if (rd) { + WR(3,5); WR(7,0x48); + if (pha->mode == 1) WR(2,0x31); + OP(5); + w2(5); w0(0xff); w2(4); + } else { + WR(3,1); WR(7,0x48); + if (pha->mode == 1) WR(2,0x31); + OP(5); + } + TOGL = 0; +} + +static int onscsi_transfer_done( PHA *pha) + +{ int x; + + if (pha->priv_flag) return 1; + + if (pha->mode == 0) { WR(2,0x20); OP(5); } + x = r1(); x = r1(); + if (pha->mode == 0) { WR(2,0x30); OP(5); } + + if ((x & 0xf0) == 0x80) return 16; + return 0; +} + +static int onscsi_transfer_ready( PHA *pha) + +{ int x; + + if (pha->priv_flag) { + x = r1(); x = r1(); + if ((x & 0xf0) == 0xf0) return 16; + if ((x & 0xf0) == 0xb0) return 8; + if ((x & 0xf0) == 0x90) return 1; + if ((x & 0xf8) == 0x88) return -1; + if ((x & 0xf8) == 0x08) return -1; + if ((x & 0xf8) == 0x0) return 1; + + if ((x & 0xf8) != 0x80) printk("DEBUG: %x\n",x); + + return 0; + } + + return onscsi_transfer_done(pha); +} + + +static int onscsi_transfer_block( PHA *pha, char * buf, int buflen, int rd) + +{ int k, b; + + k = 0; + while ( k < buflen) { + + if ((b=onscsi_transfer_ready(pha)) <= 0) break; + if (b > (buflen-k)) b = buflen-k; + + if (rd) onscsi_read_block(pha,buf,b); + else onscsi_write_block(pha,buf,b); + + k += b; buf += b; + } + + return k; +} + +static void onscsi_end_block( PHA *pha, int rd) + +{ w2(4); WR(3,0); WR(7,0x48); +} + +static void onscsi_reset_bus( PHA *pha) + +{ WR(2,2); + udelay(500); + WR(2,0); + WR(2,FULLBYTE); +} + +char *(mode_strings[5]) = {"Nybble","PS/2","EPP","EPP-16","EPP-32"}; + +static struct ppsc_protocol onscsi_psp = { + + {&host0,&host1,&host2,&host3}, /* params */ + &host_structs, /* hosts */ + 5, /* num_modes */ + 2, /* epp_first */ + 1, /* default_delay */ + 1, /* can_message */ + 16, /* sg_tablesize */ + mode_strings, + onscsi_init, + NULL, /* release */ + onscsi_connect, + onscsi_disconnect, + onscsi_test_proto, + onscsi_select, + onscsi_test_select, + onscsi_select_finish, + onscsi_deselect, + onscsi_get_bus_status, + onscsi_slow_start, + onscsi_slow_done, + onscsi_slow_end, + onscsi_start_block, + onscsi_transfer_block, + onscsi_transfer_ready, + onscsi_transfer_done, + onscsi_end_block, + onscsi_reset_bus + }; + +int onscsi_detect( Scsi_Host_Template *tpnt ) + +{ return ppsc_detect( &onscsi_psp, tpnt, verbose); +} + +struct proc_dir_entry proc_scsi_onscsi = + {PROC_SCSI_ONSCSI,6,"onscsi",S_IFDIR|S_IRUGO|S_IXUGO,2}; + +#ifdef MODULE + +Scsi_Host_Template driver_template = PPSC_TEMPLATE(onscsi); + +#include "scsi_module.c" + +#else + +void onscsi_setup( char *str, int *ints) + +{ ppsc_gen_setup(stt,4,str); +} + +#endif + +/* end of onscsi.c */ diff -urN linux-2.2.10-ref/drivers/scsi/ppscsi.c linux-2.2.10/drivers/scsi/ppscsi.c --- linux-2.2.10-ref/drivers/scsi/ppscsi.c Wed Dec 31 19:00:00 1969 +++ linux-2.2.10/drivers/scsi/ppscsi.c Wed Jun 23 14:31:38 1999 @@ -0,0 +1,1246 @@ +/* + ppscsi.c (c) 1999 Grant Guenther + Under the terms of the GNU public license. + + This is the common code shared by the PPSCSI family of + low-level drivers for parallel port SCSI host adapters. + + + To use one of the ppSCSI drivers, you must first have this module + built-in to your kernel, or loaded. Then, you can load the + appropriate protocol module. All protocol modules accept the + same parameters: + + verbose=N determines the logging level where N= + 0 only serious errors are logged + 1 report progress messages while probing adapters + 2 log the scsi commands sent to adapters + 3 basic debugging information + 4 full debugging (generates lots of output) + + hostN=,,,,, + + sets per-host-adapter parameters where + + N is between 0 and 3, each protocol can + support up to four separate adapters. + + The base port for this adapter, eg: 0x378. + Default depends on the host number, and is + one of the standard parallel ports. For + instance, host2 is probed on 0x278 by + default. + + Protocol dependent mode number. Usually + probed to determine the fastest available + mode. + + microseconds of delay per port access. + Default is protocol dependent. + + Determines this host's ability to load + the system. Default 0. Set to 1 or 2 + to reduce load at the expense of device + performance. + + scatter-gather table size. + + bit mask of targets on which to force + all commands to use explicit REQ/ACK + handshaking, rather than adapter buffers. + +*/ + +#define PPSC_VERSION "0.91" + +#define PPSC_BASE +#include "ppscsi.h" +#include +#include +#include +#include + +#ifdef CONFIG_PARPORT +#include +#endif + +#define PPSC_GEN_TMO 40*HZ +#define PPSC_SELECT_TMO HZ/10 +#define PPSC_PROBE_TMO HZ/2 +#define PPSC_RESET_TMO 4*HZ +#define PPSC_SLOW_LOOPS 30 +#define PPSC_BUSY_SNOOZE HZ; + +#define PPSC_DEF_NICE 0 +#define PPSC_INITIATOR 7 + +spinlock_t ppsc_spinlock = SPIN_LOCK_UNLOCKED; + +static char ppsc_bulk_map[256]; + +void ppsc_make_map( char map[256], char key[5], int inv) + +{ int i, j; + + for (i=0;i<256;i++) { + map[i] = 0; + for (j=0;j<5;j++) map[i] = + (map[i] << 1) | ((i & key[j]) != inv*key[j]); + } +} + +void ppsc_gen_setup( STT t[], int n, char *ss ) + +{ int j,k, sgn; + + k = 0; + for (j=0;jcontinuation = continuation; + pha->ready = ready; + if (timeout) + pha->timeout = jiffies + timeout; + else pha->timeout = pha->then + pha->tmo; + + if (!pha->nice && !pha->tq_active) { +#ifdef HAVE_DISABLE_HLT + disable_hlt(); +#endif + pha->tq_active = 1; + queue_task(&pha->tq,&tq_scheduler); + } + + if (!pha->timer_active) { + pha->timer_active = 1; + pha->timer.expires = jiffies + ((pha->nice>0)?(pha->nice-1):0); + add_timer(&pha->timer); + } + + spin_unlock_irqrestore(&ppsc_spinlock,flags); +} + +static void ppsc_tq_int( void *data ) + +{ void (*con)(PHA *); + long flags; + PHA *pha = (PHA *)data; + + spin_lock_irqsave(&ppsc_spinlock,flags); + + con = pha->continuation; + +#ifdef HAVE_DISABLE_HLT + enable_hlt(); +#endif + + pha->tq_active = 0; + + if (!con) { + spin_unlock_irqrestore(&ppsc_spinlock,flags); + return; + } + pha->timedout = (jiffies >= pha->timeout); + if (!pha->ready || pha->ready(pha) || pha->timedout) { + pha->continuation = NULL; + spin_unlock_irqrestore(&ppsc_spinlock,flags); + con(pha); + return; + } + +#ifdef HAVE_DISABLE_HLT + disable_hlt(); +#endif + + pha->tq_active = 1; + queue_task(&pha->tq,&tq_scheduler); + spin_unlock_irqrestore(&ppsc_spinlock,flags); +} + +static void ppsc_timer_int( unsigned long data) + +{ void (*con)(PHA *); + long flags; + PHA *pha = (PHA *)data; + + spin_lock_irqsave(&ppsc_spinlock,flags); + + con = pha->continuation; + pha->timer_active = 0; + if (!con) { + spin_unlock_irqrestore(&ppsc_spinlock,flags); + return; + } + pha->timedout = (jiffies >= pha->timeout); + if (!pha->ready || pha->ready(pha) || pha->timedout) { + pha->continuation = NULL; + spin_unlock_irqrestore(&ppsc_spinlock,flags); + con(pha); + return; + } + pha->timer_active = 1; + pha->timer.expires = jiffies + ((pha->nice>0)?(pha->nice-1):0); + add_timer(&pha->timer); + spin_unlock_irqrestore(&ppsc_spinlock,flags); +} + +#ifdef CONFIG_PARPORT + +static void ppsc_wake_up( void *p) + +{ PHA *pha = (PHA *) p; + long flags; + void (*cont)(PHA *) = NULL; + + spin_lock_irqsave(&ppsc_spinlock,flags); + + if (pha->claim_cont && + !parport_claim((struct pardevice *)(pha->pardev))) { + cont = pha->claim_cont; + pha->claim_cont = NULL; + pha->claimed = 1; + } + + spin_unlock_irqrestore(&ppsc_spinlock,flags); + + wake_up(&(pha->parq)); + + if (cont) cont(pha); +} + +#endif + +void ppsc_do_claimed( PHA *pha, void(*cont)(PHA *)) + +#ifdef CONFIG_PARPORT + +{ long flags; + + spin_lock_irqsave(&ppsc_spinlock,flags); + + if (!pha->pardev || + !parport_claim((struct pardevice *)(pha->pardev))) { + pha->claimed = 1; + spin_unlock_irqrestore(&ppsc_spinlock,flags); + cont(pha); + } else { + pha->claim_cont = cont; + spin_unlock_irqrestore(&ppsc_spinlock,flags); + } +} + +#else + +{ cont(pha); +} + +#endif + +static void ppsc_claim( PHA *pha) + +{ if (pha->claimed) return; + pha->claimed = 1; +#ifdef CONFIG_PARPORT + if (pha->pardev) + while (parport_claim((struct pardevice *)(pha->pardev))) + sleep_on(&(pha->parq)); +#endif +} + +static void ppsc_unclaim( PHA *pha) + +{ pha->claimed = 0; +#ifdef CONFIG_PARPORT + if (pha->pardev) parport_release((struct pardevice *)(pha->pardev)); +#endif +} + +static void ppsc_unregister_parport( PHA *pha) + +{ +#ifdef CONFIG_PARPORT + if (pha->pardev) { + parport_unregister_device((struct pardevice *)(pha->pardev)); + pha->pardev = NULL; + } +#endif +} + +static void ppsc_register_parport( PHA *pha, int verbose) + +{ +#ifdef CONFIG_PARPORT + struct parport *pp; + + pp = parport_enumerate(); + + while((pp)&&(pp->base != pha->port)) pp = pp->next; + if (!pp) return; + pha->pardev = (void *) parport_register_device( + pp,pha->device,NULL,ppsc_wake_up,NULL,0,(void *)pha); + pha->parq = NULL; + if (verbose) + printk("%s: 0x%x is %s\n",pha->device,pha->port,pp->name); + pha->parname = (char *)pp->name; +#endif +} + +/* Here's the actual core SCSI stuff ... */ + +#define PPSC_FAIL(err,msg) { ppsc_fail_command(pha,err,msg); return; } + +static void ppsc_start(PHA *pha); +static void ppsc_select_intr(PHA *pha); +static void ppsc_engine(PHA *pha); +static void ppsc_transfer(PHA *pha); +static void ppsc_transfer_done(PHA *pha); +static int ppsc_slow(PHA *pha, char *val); +static void ppsc_slow_done(PHA *pha); +static void ppsc_cleanup(PHA *pha); +static void ppsc_fail_command(PHA *pha, int err_code, char *msg); +static int ppsc_ready( PHA *pha); + +/* synchronous interface is deprecated, but we maintain it for + internal use. It just starts an asynchronous command and waits + for it to complete. +*/ + +int ppsc_command(Scsi_Cmnd *cmd) + +{ PHA *pha = (PHA *) cmd->host->unique_id; + + pha->cur_cmd = cmd; + pha->done = NULL; + pha->then = jiffies; + + ppsc_do_claimed(pha,ppsc_start); + + while (pha->cur_cmd) scsi_sleep(1); + + return cmd->result; +} + +int ppsc_queuecommand(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)) + +{ PHA *pha = (PHA *) cmd->host->unique_id; + + if (pha->cur_cmd) { + printk("%s: Driver is busy\n",pha->device); + return 0; + } + + pha->cur_cmd = cmd; + pha->done = done; + pha->then = jiffies; + + ppsc_do_claimed(pha,ppsc_start); + + return 0; + +} + +static void ppsc_arb_fail(PHA *pha) + +{ PPSC_FAIL(DID_BUS_BUSY,"Arbitration failure"); +} + + +static void ppsc_start(PHA *pha) + +{ int k, r, b, bf; + struct scatterlist *p; + + pha->last_phase = PPSC_PH_NONE; + pha->return_code = (DID_OK << 16); + pha->overflow = 0; + pha->protocol_error = 0; + pha->cmd_count = 0; + + k = pha->cur_cmd->cmnd[0]; + bf = ppsc_bulk_map[k]; + + bf &= (!((1<cur_cmd->target) & pha->slow_targets)); + + r = pha->cur_cmd->use_sg; + if (r) { + b = 0; + p = (struct scatterlist *)pha->cur_cmd->request_buffer; + for (k=0;klength; + p++; + } + } else { + b = pha->cur_cmd->request_bufflen; + } + + bf &= (b > 127); + + if (V_DEBUG) + printk("%s: Target %d, bl=%d us=%d bf=%d cm=%x\n", + pha->device,pha->cur_cmd->target,b,r,bf,k); + + pha->bulk = bf; + pha->tlen = b; + + pha->proto->connect(pha); + + r = 0; + while (r++ < 5) { + k = pha->proto->select(pha,PPSC_INITIATOR,pha->cur_cmd->target); + if (k != -1) break; + udelay(200); + } + + if (k == -1) { + ppsc_set_intr(pha,ppsc_arb_fail,NULL,1); + return; + } + + ppsc_set_intr(pha,ppsc_select_intr,pha->proto->test_select, + PPSC_SELECT_TMO); + +} + +static void ppsc_select_intr(PHA *pha) + +{ if (!pha->proto->test_select(pha)) { + pha->return_code = DID_NO_CONNECT << 16; + ppsc_cleanup(pha); + return; + } + if (pha->proto->select_finish) + pha->proto->select_finish(pha); + + if (V_FULL) + printk("%s: selected target\n",pha->device); + + pha->timedout = 0; + ppsc_engine(pha); +} + +static void ppsc_update_sg(PHA *pha) + +{ if ((!pha->cur_len) && pha->sg_count) { + pha->sg_count--; + pha->sg_list++; + pha->cur_buf = pha->sg_list->address; + pha->cur_len = pha->sg_list->length; + } +} + +static void ppsc_engine(PHA *pha) + +{ int phase, i; + char *sb; + + while (1) { + + if ((pha->last_phase == PPSC_PH_MSGIN) || + ((pha->last_phase == PPSC_PH_STAT) + && (!pha->proto->can_message))) { + pha->return_code |= (pha->status_byte & STATUS_MASK) + | (pha->message_byte << 8); + ppsc_cleanup(pha); + return; + } + + phase = pha->proto->get_bus_status(pha); + + if (pha->abort_flag) + PPSC_FAIL(DID_ABORT,"Command aborted"); + + if (pha->protocol_error) + PPSC_FAIL(DID_ERROR,"Adapter protocol failure"); + + if (!(phase & PPSC_BSY)) { + if (pha->last_phase == PPSC_PH_STAT) { + if (V_DEBUG) printk("%s: No msg phase ?\n", pha->device); + pha->return_code |= (pha->status_byte & STATUS_MASK); + ppsc_cleanup(pha); + return; + } + PPSC_FAIL(DID_ERROR,"Unexpected bus free"); + } + + if (!(phase & PPSC_REQ)) { + if (pha->timedout) + PPSC_FAIL(DID_TIME_OUT,"Pseudo-interrupt timeout"); + ppsc_set_intr(pha,ppsc_engine,ppsc_ready,0); + return; + } + + switch (phase) { + + case PPSC_PH_CMD: + + if (phase != pha->last_phase) { + if (pha->last_phase != PPSC_PH_NONE) + PPSC_FAIL(DID_ERROR,"Phase sequence error 1"); + pha->cmd_count = 0; + if (V_TRACE) { + printk("%s: Command to %d (%d): ", + pha->device, pha->cur_cmd->target, + pha->cur_cmd->cmd_len); + for (i=0;icur_cmd->cmd_len;i++) + printk("%2x ",pha->cur_cmd->cmnd[i]); + printk("\n"); + } + } + + pha->last_phase = phase; + + if (pha->cmd_count >= pha->cur_cmd->cmd_len) + PPSC_FAIL(DID_ERROR,"Command buffer overrun"); + + if (!ppsc_slow(pha,&(pha->cur_cmd->cmnd[pha->cmd_count++]))) + return; + + break; + + case PPSC_PH_READ: + case PPSC_PH_WRITE: + + if (phase != pha->last_phase) { + if (pha->last_phase != PPSC_PH_CMD) + PPSC_FAIL(DID_ERROR,"Phase sequence error 2"); + pha->data_dir = phase & PPSC_IO; + pha->data_count = 0; + + pha->sg_count = pha->cur_cmd->use_sg; + if (pha->sg_count) { + pha->sg_count--; + pha->sg_list = + (struct scatterlist *)pha->cur_cmd->request_buffer; + pha->cur_buf = pha->sg_list->address; + pha->cur_len = pha->sg_list->length; + } else { + pha->cur_buf = pha->cur_cmd->request_buffer; + pha->cur_len = pha->cur_cmd->request_bufflen; + } + + pha->last_phase = phase; + + } + + if ((pha->bulk) && (pha->cur_len > 0 )) { + pha->proto->start_block(pha,pha->data_dir); + ppsc_transfer(pha); + return; + } + + ppsc_update_sg(pha); + + if (!pha->cur_len) { + pha->cur_len = 1; + pha->cur_buf = (char *)&i; + i = 0x5a; + pha->overflow++; + } + + pha->cur_len--; + pha->data_count++; + + if (!ppsc_slow(pha,pha->cur_buf++)) return; + + break; + + case PPSC_PH_STAT: + + if ((pha->last_phase != PPSC_PH_CMD) && + (pha->last_phase != PPSC_PH_READ) && + (pha->last_phase != PPSC_PH_WRITE)) + PPSC_FAIL(DID_ERROR,"Phase sequence error 3"); + + if ((pha->last_phase != PPSC_PH_CMD) && + (V_DEBUG)) { + printk("%s: %s%s %d bytes\n", + pha->device, + pha->bulk?"":"slow ", + pha->data_dir?"read":"write", + pha->data_count); + + if (pha->cur_cmd->cmnd[0] == REQUEST_SENSE) { + + sb = (char *)pha->cur_cmd->request_buffer; + printk("%s: Sense key: %x ASC: %x ASCQ: %x\n", + pha->device, sb[2] & 0xff, + sb[12] & 0xff, sb[13] & 0xff); + } + } + + if (pha->overflow) + printk("%s: WARNING: data %s overran by %d/%d bytes\n", + pha->device,pha->data_dir?"read":"write", + pha->overflow,pha->data_count); + + pha->last_phase = phase; + + if (!ppsc_slow(pha,&pha->status_byte)) return; + + break; + + case PPSC_PH_MSGIN: + + if (pha->last_phase != PPSC_PH_STAT) + PPSC_FAIL(DID_ERROR,"Phase sequence error 4"); + + pha->last_phase = phase; + + if (V_FULL) + printk("%s: status = %x\n",pha->device,pha->status_byte); + + if (!ppsc_slow(pha,&pha->message_byte)) return; + + break; + + default: + + PPSC_FAIL(DID_ERROR,"Unexpected bus phase"); + + } + } +} + +static void ppsc_transfer(PHA *pha) + +{ int i, j; + + + if (pha->timedout) PPSC_FAIL(DID_TIME_OUT,"PDMA timeout"); + + while(1) { + + if (!(j=pha->proto->transfer_ready(pha))) { + ppsc_set_intr(pha,ppsc_transfer, + pha->proto->transfer_ready,0); + return; + } + + if (j < 0) { + if (V_DEBUG) + printk("%s: short transfer\n",pha->device); + ppsc_set_intr(pha,ppsc_transfer_done, + pha->proto->transfer_done,0); + return; + } + + i = pha->proto->transfer_block(pha,pha->cur_buf, + pha->cur_len,pha->data_dir); + + if (V_FULL) printk("%s: Fragment %d\n",pha->device,i); + + if ((i < 0) || (i > pha->cur_len)) + PPSC_FAIL(DID_ERROR,"Block transfer error"); + + pha->cur_len -= i; + pha->cur_buf += i; + pha->data_count += i; + + ppsc_update_sg(pha); + + if (pha->cur_len == 0 ) { + ppsc_set_intr(pha,ppsc_transfer_done, + pha->proto->transfer_done,0); + return; + } + } +} + +static void ppsc_transfer_done(PHA *pha) + +{ if (pha->timedout) PPSC_FAIL(DID_TIME_OUT,"PDMA done timeout"); + + pha->proto->end_block(pha,pha->data_dir); + ppsc_engine(pha); +} + +static int ppsc_slow( PHA *pha, char *val) + +{ int k; + + pha->proto->slow_start(pha,val); + + k = 0; + while (k++ < PPSC_SLOW_LOOPS) + if (pha->proto->slow_done(pha)) { + pha->proto->slow_end(pha); + return 1; + } + + ppsc_set_intr(pha,ppsc_slow_done,pha->proto->slow_done,0); + return 0; +} + +static void ppsc_slow_done( PHA *pha) + +{ int k; + + if (pha->timedout) PPSC_FAIL(DID_TIME_OUT,"PIO timeout"); + + pha->proto->slow_end(pha); + + k = 0; + while (k++ < PPSC_SLOW_LOOPS) + if (ppsc_ready(pha)) break; + + ppsc_engine(pha); +} + +static void ppsc_try_again( unsigned long data ) + +{ PHA *pha = (PHA *)data; + + ppsc_do_claimed(pha,ppsc_start); +} + +static void ppsc_cleanup(PHA *pha) + +{ Scsi_Cmnd *cmd; + void (*done)(Scsi_Cmnd *); + long saved_flags; + + pha->tot_bytes += pha->data_count; + + cmd = pha->cur_cmd; + done = pha->done; + cmd->result = pha->return_code; + pha->cur_cmd = 0; + + pha->proto->deselect(pha); + pha->proto->disconnect(pha); + + if (V_FULL) printk("%s: releasing parport\n",pha->device); + + ppsc_unclaim(pha); + + if (pha->abort_flag) { + + if (V_DEBUG) printk("%s: command aborted !\n",pha->device); + + return; /* kill the thread */ + } + + if (V_DEBUG) + printk("%s: Command status %08x last phase %o\n", + pha->device,cmd->result,pha->last_phase); + + if (status_byte(pha->return_code) == BUSY) { + + pha->cur_cmd = cmd; + + if (V_FULL) + printk("%s: BUSY, sleeping before retry ...\n", + pha->device); + + pha->sleeper.next = NULL; + pha->sleeper.prev = NULL; + pha->sleeper.data = (unsigned long) pha; + pha->sleeper.function = ppsc_try_again; + pha->sleeper.expires = jiffies + PPSC_BUSY_SNOOZE; + add_timer(&pha->sleeper); + + return; + + } + + pha->tot_cmds++; + + if ((cmd->cmnd[0] != REQUEST_SENSE) && + (status_byte(pha->return_code) == CHECK_CONDITION)) { + + if (V_FULL) + printk("%s: Requesting sense data\n",pha->device); + + cmd->cmnd[0] = REQUEST_SENSE; + cmd->cmnd[1] &= 0xe0; + cmd->cmnd[2] = 0; + cmd->cmnd[3] = 0; + cmd->cmnd[4] = sizeof(cmd->sense_buffer); + cmd->cmnd[5] = 0; + cmd->cmd_len = 6; + cmd->use_sg = 0; + cmd->request_buffer = (char *) cmd->sense_buffer; + cmd->request_bufflen = sizeof(cmd->sense_buffer); + + pha->cur_cmd = cmd; + ppsc_do_claimed(pha,ppsc_start); + + return; + } + + if (done) { + + spin_lock_irqsave(&io_request_lock,saved_flags); + done(cmd); + spin_unlock_irqrestore(&io_request_lock,saved_flags); + + } + +} + +static void ppsc_fail_command(PHA *pha, int err_code, char *msg) + +{ int bs; + + pha->tot_errs++; + + bs = pha->proto->get_bus_status(pha); + + pha->return_code = err_code << 16; + if (!pha->quiet) + printk("%s: %s, bs=%o cb=%d db=%d bu=%d sg=%d " + "rd=%d lp=%o pe=%d cc=%d\n", + pha->device, msg, bs, + pha->cmd_count, pha->data_count, + pha->bulk, pha->sg_count, pha->data_dir, + pha->last_phase, pha->protocol_error, pha->tot_cmds); + + ppsc_cleanup(pha); +} + +static int ppsc_ready( PHA *pha) + +{ int bs; + + if (pha->abort_flag || pha->protocol_error) return 1; + bs = pha->proto->get_bus_status(pha); + + if ( (bs & (PPSC_REQ|PPSC_BSY)) != PPSC_BSY) return 1; + + return 0; +} + +int ppsc_abort( Scsi_Cmnd * cmd) + +{ PHA *pha = (PHA *)cmd->host->unique_id; + + printk("%s: Command abort not supported\n",pha->device); + return FAILED; +} + +static void ppsc_reset_pha(PHA *pha) + +{ if (!pha->proto->reset_bus) { + printk("%s: No reset method available\n",pha->device); + return; + } + + ppsc_claim(pha); + pha->proto->connect(pha); + pha->proto->reset_bus(pha); + scsi_sleep(4*HZ); + pha->proto->disconnect(pha); + ppsc_unclaim(pha); + + if (!pha->quiet) printk("%s: Bus reset\n",pha->device); +} + +int ppsc_reset( Scsi_Cmnd * cmd) + +{ PHA *pha = (PHA *)cmd->host->unique_id; + int k = 0; + + if (!pha->proto->reset_bus) + return FAILED; + + if (pha->cur_cmd) + pha->abort_flag = PPSC_DO_RESET; + + while (pha->cur_cmd && (k < PPSC_RESET_TMO)) { + scsi_sleep(HZ/10); + k += HZ/10; + } + + if (pha->cur_cmd) { + printk("%s: Driver won't give up for reset\n",pha->device); + return FAILED; + } + + ppsc_reset_pha(pha); + + return SUCCESS; +} + +#define PROCIN(n,var)\ + if ((length>n+1)&&(strncmp(buffer,#var"=",n+1)==0)) { \ + pha->var = simple_strtoul(buffer+n+1,NULL,0);\ + return length;\ + } + +#define PROCOUT(fmt,val) len+=sprintf(buffer+len,fmt"\n",val); + +int ppsc_proc_info( char *buffer, char **start, off_t offset, + int length, int hostno, int inout) + +{ int len = 0; + struct Scsi_Host *p = scsi_hostlist; + PHA *pha; + + /* first, lets find the host struct */ + + while (p && (p->host_no != hostno)) p = p->next; + if (!p) return 0; /* should never happen */ + pha = (PHA *)p->unique_id; + + if (inout) { + + PROCIN(4,mode); + PROCIN(5,delay); + PROCIN(7,verbose); + PROCIN(10,abort_flag); + PROCIN(4,nice); + + return (-EINVAL); + } + + PROCOUT("ident: %s",pha->ident); + PROCOUT("base port: 0x%03x",pha->port); + PROCOUT("mode: %d",pha->mode); + if (pha->proto->mode_names) + PROCOUT("mode name: %s",pha->proto->mode_names[pha->mode]); + PROCOUT("delay: %d",pha->delay); + PROCOUT("nice: %d",pha->nice); + PROCOUT("verbose: %d",pha->verbose); + PROCOUT("quiet: %d",pha->quiet); + PROCOUT("tot_cmds: %d",pha->tot_cmds); + PROCOUT("tot_bytes: %ld",pha->tot_bytes); + PROCOUT("tot_errs: %d",pha->tot_errs); + + if (pha->pardev) { + PROCOUT("parport device: %s",pha->parname); + PROCOUT("claimed: %d",pha->claimed); + } + if (V_DEBUG) { + PROCOUT("then: %ld",pha->then); + PROCOUT("timeout: %ld",pha->timeout); + PROCOUT("now: %ld",jiffies); + PROCOUT("timer active: %d",pha->timer_active); + PROCOUT("tq_active: %d",pha->tq_active); + PROCOUT("abort_flag: %d",pha->abort_flag); + PROCOUT("return_code: %08x",pha->return_code); + PROCOUT("last_phase: %o",pha->last_phase); + PROCOUT("cmd_count: %d",pha->cmd_count); + PROCOUT("data_count: %d",pha->data_count); + PROCOUT("data_dir: %d",pha->data_dir); + PROCOUT("bulk: %d",pha->bulk); + PROCOUT("tlen: %d",pha->tlen); + PROCOUT("overflow: %d",pha->overflow); + } + + if (offset > len) return 0; + + *start = buffer+offset; len -= offset; + if (len > length) len = length; + return len; +} + +int ppsc_biosparam( Disk * disk, kdev_t dev, int ip[]) + +{ 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] > 1023) + ip[2] = 1023; + } + return 0; +} + +static int ppsc_inquire(PHA *pha, int target, char *buf) + +{ char inq[6] = {0x12,0,0,0,36,0}; + int i; + Scsi_Cmnd cmd; + + cmd.host = pha->host_ptr; + cmd.target = target; + cmd.cmd_len = 6; + for (i=0;i<6;i++) cmd.cmnd[i] = inq[i]; + cmd.use_sg = 0; + cmd.request_buffer = buf; + cmd.request_bufflen = 36; + + return ppsc_command(&cmd); + +} + +static void ppsc_test_mode( PHA *pha, int mode) + +{ int i, t, s, e, f, g, ok, old_mode; + char ibuf[38]; + + if ((mode >= pha->proto->epp_first) && (pha->reserved < 8)) return; + + old_mode = pha->mode; + pha->mode = mode; + + e = -1; f = -1; g = 0; + + if (pha->proto->test_proto) { + ppsc_claim(pha); + e = pha->proto->test_proto(pha); + ppsc_unclaim(pha); + } + + if (e <= 0) { + f = 0; + for (t=0;t<8;t++) { + s = ppsc_inquire(pha,t,ibuf); + if (s == DID_NO_CONNECT << 16) continue; + if (s) { f++; + break; + } + if (V_FULL) { + for (i=0;i<36;i++) + if ((ibuf[i] < ' ') || (ibuf[i] >= '~')) ibuf[i] = '.'; + ibuf[36] = 0; + printk("%s: port 0x%x mode %d targ %d: %s\n", + pha->device,pha->port,mode,t,ibuf); + } + g++; + } + if (f) ppsc_reset_pha(pha); + } + + ok = (e<=0) && (f == 0); + + if (!ok) pha->mode = old_mode; + + if (V_PROBE) printk("%s: port 0x%3x mode %d test %s (%d,%d,%d)\n", + pha->device,pha->port,mode,ok?"passed":"failed",e,f,g); +} + + +int ppsc_release_pha( PHA *pha) + + +{ if (pha->proto->release) pha->proto->release(pha); + + if ((!pha->pardev) && (pha->reserved)) + release_region(pha->port,pha->reserved); + + ppsc_unregister_parport(pha); + + MOD_DEC_USE_COUNT; + + return 0; +} + + +int ppsc_detect( PSP *proto, Scsi_Host_Template *tpnt, int verbose) + +{ int i, m, p, d, n, s, z; + PHA *pha; + int host_count = 0; + struct Scsi_Host *hreg; + + m = 0; + for (i=0;i<4;i++) if ((*proto->params[i])[PPSC_PARM_PORT] != -1) m++; + + if (!m) { + (*proto->params[1])[PPSC_PARM_PORT] = 0x378; + (*proto->params[2])[PPSC_PARM_PORT] = 0x278; + (*proto->params[3])[PPSC_PARM_PORT] = 0x3bc; + } + + tpnt->this_id = PPSC_INITIATOR; + + for (i=0;i<4;i++) { + + p = (*proto->params[i])[PPSC_PARM_PORT]; + if (p <= 0) continue; + m = (*proto->params[i])[PPSC_PARM_MODE]; + n = (*proto->params[i])[PPSC_PARM_NICE]; + if (n == -1) n = PPSC_DEF_NICE; + d = (*proto->params[i])[PPSC_PARM_DLY]; + if (d == -1) d = proto->default_delay; + s = (*proto->params[i])[PPSC_PARM_SGTS]; + if (s == -1) s = proto->default_sg_tablesize; + z = (*proto->params[i])[PPSC_PARM_SLOW]; + if (z == -1) z = 0; + + MOD_INC_USE_COUNT; + + pha = &(((*proto->hosts)[i])); + + pha->proto = proto; + + pha->port = p; + pha->delay = d; + pha->nice = n; + + d = sizeof(pha->device)-3; + p = strlen(tpnt->name); + if (p > d) p = d; + for (n=0;ndevice[n] = tpnt->name[n]; + pha->device[p] = '.'; + pha->device[p+1] = '0' + i; + pha->device[p+2] = 0; + + pha->tq.next = NULL; + pha->tq.sync = 0; + pha->tq.routine = ppsc_tq_int; + pha->tq.data = (void *) pha; + + pha->timer.next = NULL; + pha->timer.prev = NULL; + pha->timer.data = (unsigned long) pha; + pha->timer.function = ppsc_timer_int; + + pha->parq = NULL; + pha->pardev = NULL; + pha->claimed = 0; + pha->claim_cont = NULL; + pha->timer_active = 0; + pha->tq_active = 0; + pha->timedout = 0; + + pha->cur_cmd = NULL; + pha->done = NULL; + pha->abort_flag = 0; + pha->protocol_error = 0; + pha->tot_errs = 0; + pha->tot_cmds = 0; + pha->tot_bytes = 0; + + for (n=0;n<8;n++) pha->private[n] = 0; + + pha->slow_targets = z; + + ppsc_register_parport(pha,verbose); + + pha->proto->init(pha); + + pha->verbose = verbose; + pha->quiet = 1; /* no errors until probe over */ + if (V_FULL) pha->quiet = 0; /* unless we want them ... */ + + pha->tmo = PPSC_PROBE_TMO; + + hreg = scsi_register(tpnt,0); + hreg->dma_channel = -1; + hreg->n_io_port = 0; + hreg->unique_id = (int) pha; + hreg->sg_tablesize = s; + + pha->host_ptr = hreg; + + pha->reserved = (pha->port % 8) ? 3 : 8; + if (!pha->pardev) { + if (!check_region(pha->port,pha->reserved)) + request_region(pha->port,pha->reserved,pha->device); + else pha->reserved = 0; + } + + pha->mode = -1; + + if (pha->reserved) { + if (m == -1) for (m=0;mnum_modes;m++) + ppsc_test_mode(pha,m); + else ppsc_test_mode(pha,m); + } + + if (pha->mode != -1) { + + pha->quiet = 0; /* enable PPSC_FAIL msgs */ + pha->tmo = PPSC_GEN_TMO; + host_count++; + + printk("%s: %s at 0x%3x mode %d (%s) dly %d nice %d sg %d\n", + pha->device, + pha->ident, + pha->port, + pha->mode, + (pha->proto->mode_names)? + pha->proto->mode_names[pha->mode]:"", + pha->delay, + pha->nice, + hreg->sg_tablesize); + + } else { + + scsi_unregister(hreg); + ppsc_release_pha(pha); + + } + } + return host_count; +} + +int ppsc_release(struct Scsi_Host *host) + +{ PHA *pha = (PHA *) host->unique_id; + + return ppsc_release_pha(pha); +} + +int ppsc_initialise(void) + +{ int i; + + for (i=0;i<256;i++) ppsc_bulk_map[i] = 0; + +/* commands marked in this map will use pseudo-DMA transfers, while + the rest will use the slow handshaking. +*/ + + ppsc_bulk_map[READ_6] = 1; + ppsc_bulk_map[READ_10] = 1; + ppsc_bulk_map[READ_BUFFER] = 1; + ppsc_bulk_map[WRITE_6] = 1; + ppsc_bulk_map[WRITE_10] = 1; + ppsc_bulk_map[WRITE_BUFFER] = 1; + + printk("ppSCSI %s (%s) installed\n",PPSC_VERSION,PPSC_H_VERSION); + return 0; +} + +#ifdef MODULE + +int init_module(void) + +{ return ppsc_initialise(); +} + +void cleanup_module(void) + +{ +} + +#endif + +/* end of ppscsi.c */ diff -urN linux-2.2.10-ref/drivers/scsi/ppscsi.h linux-2.2.10/drivers/scsi/ppscsi.h --- linux-2.2.10-ref/drivers/scsi/ppscsi.h Wed Dec 31 19:00:00 1969 +++ linux-2.2.10/drivers/scsi/ppscsi.h Wed Jun 23 14:31:38 1999 @@ -0,0 +1,372 @@ +#ifndef _PPSC_H +#define _PPSC_H + +/* + ppscsi.h (c) 1999 Grant Guenther + Under the terms of the GNU public license. + + This header file defines a common interface for constructing + low-level SCSI drivers for parallel port SCSI adapters. + +*/ + +#define PPSC_H_VERSION "0.91" + +#include +#include +#include +#include +#include +#include +#include "sd.h" +#include "hosts.h" + +/* ppscsi global functions */ + +extern void ppsc_make_map( char map[256], char key[5], int inv); + +extern int ppsc_proc_info(char *,char **,off_t,int,int,int); +extern int ppsc_command(Scsi_Cmnd *); +extern int ppsc_queuecommand(Scsi_Cmnd *, void (* done)(Scsi_Cmnd *)); +extern int ppsc_abort(Scsi_Cmnd *); +extern int ppsc_reset(Scsi_Cmnd *); +extern int ppsc_biosparam(Disk *, kdev_t, int[]); +extern int ppsc_release(struct Scsi_Host *); + +#ifndef PPSC_BASE + +/* imports for hosts.c */ + +#ifdef CONFIG_PPSCSI_T348 +extern int t348_detect( Scsi_Host_Template *); +extern struct proc_dir_entry proc_scsi_t348; +#endif + +#ifdef CONFIG_PPSCSI_T358 +extern int t358_detect( Scsi_Host_Template *); +extern struct proc_dir_entry proc_scsi_t358; +#endif + +#ifdef CONFIG_PPSCSI_ONSCSI +extern int onscsi_detect( Scsi_Host_Template *); +extern struct proc_dir_entry proc_scsi_onscsi; +#endif + +#ifdef CONFIG_PPSCSI_EPST +extern int epst_detect( Scsi_Host_Template *); +extern struct proc_dir_entry proc_scsi_epst; +#endif + +#ifdef CONFIG_PPSCSI_EPSA2 +extern int epsa2_detect( Scsi_Host_Template *); +extern struct proc_dir_entry proc_scsi_epsa2; +#endif + +#ifdef CONFIG_PPSCSI_BELKIN +extern int belkin_detect( Scsi_Host_Template *); +extern struct proc_dir_entry proc_scsi_belkin; +#endif + +#ifdef CONFIG_PPSCSI_VPI0 +extern int vpi0_detect( Scsi_Host_Template *); +extern struct proc_dir_entry proc_scsi_vpi0; +#endif + +#ifdef CONFIG_PPSCSI_VPI2; +extern int vpi2_detect( Scsi_Host_Template *); +extern struct proc_dir_entry proc_scsi_vpi2; +#endif + +#ifdef CONFIG_PPSCSI_SPARCSI +extern int sparcsi_detect( Scsi_Host_Template *); +extern struct proc_dir_entry proc_scsi_sparcsi; +#endif + +#endif + +#define PPSC_TEMPLATE(proto) { \ + name: #proto, \ + detect: proto##_detect, \ + release: ppsc_release, \ + proc_dir: &proc_scsi_##proto,\ + proc_info: ppsc_proc_info, \ + queuecommand: ppsc_queuecommand, \ + eh_abort_handler: ppsc_abort, \ + eh_bus_reset_handler: ppsc_reset, \ + eh_host_reset_handler: ppsc_reset, \ + bios_param: ppsc_biosparam, \ + can_queue: 1, \ + sg_tablesize: 0, \ + cmd_per_lun: 1, \ + use_clustering: DISABLE_CLUSTERING,\ + use_new_eh_code: 1 \ +} + +/* types used by the actual driver modules */ + +#ifdef PPSC_BASE + +#include +#include +#include +#include +#include +#include + + +struct setup_tab_t { + + char *tag; /* variable name */ + int size; /* number of elements in array */ + int *iv; /* pointer to variable */ +}; + +typedef struct setup_tab_t STT; + +extern void ppsc_gen_setup( STT t[], int n, char *ss ); + +typedef struct ppsc_host_adapter PHA; + +struct ppsc_host_adapter { + + char ident[80]; /* Adapter name and version info */ + + char device[12]; /* device name for messages */ + + struct Scsi_Host *host_ptr; /* SCSI host structure */ + struct ppsc_protocol *proto; /* adapter protocol */ + + int port; /* parallel port base address */ + int mode; /* transfer mode in use */ + int delay; /* parallel port settling delay */ + int saved_r0; /* saved port state */ + int saved_r2; /* saved port state */ + + int reserved; /* number of ports reserved */ + int tmo; /* default command timeout */ + int verbose; /* logging level */ + int quiet; /* do not log PPSC_FAIL msgs */ + + int slow_targets; /* bit mask for disabling block mode */ + + struct wait_queue *parq; /* semaphore for parport sharing */ + void *pardev; /* pointer to pardevice */ + char *parname; /* parport name */ + int claimed; /* parport has been claimed */ + void (*claim_cont)(PHA *); /* continuation for parport wait */ + + void (*continuation)(PHA *); /* next "interrupt" handler */ + int (*ready)(PHA *); /* current ready test */ + long then; /* jiffies at start of last wait */ + long timeout; /* when to timeout this wait */ + int timedout; /* timeout was seen */ + int timer_active; /* we're using a timer */ + int tq_active; /* we have a task queued */ + int nice; /* tune the CPU load */ + struct timer_list timer; /* timer queue element */ + struct tq_struct tq; /* task queue element */ + + int private[8]; /* for the protocol layer, if needed */ + char *priv_ptr; + int priv_flag; + + Scsi_Cmnd *cur_cmd; /* current command on this host */ + void (*done)(Scsi_Cmnd *); /* current "done" function */ + + int overflow; /* excess bytes transferred */ + int bulk; /* should we use block mode ? */ + int tlen; /* total transfer length */ + int abort_flag; /* abort=1 reset=2 requested */ + int return_code; /* build return value here */ + + struct scatterlist *sg_list; /* current fragment, if any */ + int sg_count; /* remaining fragments */ + char *cur_buf; /* current buffer pointer */ + int cur_len; /* remaining bytes in buffer */ + + struct timer_list sleeper; /* for BUSY handling */ + + int last_phase; /* to detect phase changes */ + char message_byte; + char status_byte; + + int cmd_count; /* bytes of command transfered */ + int data_count; /* bytes of data transferred */ + int data_dir; /* direction of transfer */ + + int tot_cmds; /* number of commands processed */ + long tot_bytes; /* total bytes transferred */ + int tot_errs; /* number of failed commands */ + + int protocol_error; /* Some protocols can set this + != zero to signal a fatal error + we report it and expect to die + */ +}; + +/* constants for 'verbose' */ + +#define PPSC_VERB_NORMAL 0 +#define PPSC_VERB_PROBE 1 +#define PPSC_VERB_TRACE 2 +#define PPSC_VERB_DEBUG 3 +#define PPSC_VERB_FULL 4 + +#define V_PROBE (pha->verbose >= PPSC_VERB_PROBE) +#define V_TRACE (pha->verbose >= PPSC_VERB_TRACE) +#define V_DEBUG (pha->verbose >= PPSC_VERB_DEBUG) +#define V_FULL (pha->verbose >= PPSC_VERB_FULL) + +/* constants for abort_flag */ + +#define PPSC_DO_ABORT 1 +#define PPSC_DO_RESET 2 + + +struct ppsc_protocol { + + int (*params[4])[8]; /* hostN tuning parameters */ + + PHA (*hosts)[4]; /* actual PHA structs */ + + int num_modes; /* number of modes*/ + int epp_first; /* modes >= this use 8 ports */ + int default_delay; /* delay parameter if not specified */ + + int can_message; /* adapter can send/rcv SCSI msgs */ + int default_sg_tablesize; /* sg_tablesize if not specified */ + + char **mode_names; /* printable names of comm. modes */ + +/* first two functions are NOT called with the port claimed. */ + + void (*init)(PHA *); /* (pha) + protocol initialisation + should fill in pha->ident */ + void (*release)(PHA *); /* (pha) optional + protocol no longer in use */ + void (*connect)(PHA *); /* (pha) + connect to adapter */ + void (*disconnect)(PHA *); /* (pha) + release adapter */ + int (*test_proto)(PHA *); /* (pha) optional + test protocol in current settings, + returns error count */ + int (*select)(PHA *,int,int); /* (pha,initiator,target) + start artibration and selection + 0 = OK, -1 = arb. failed */ + int (*test_select)(PHA *); /* (pha) + test for selection to complete + 1 = OK, 0 try again */ + void (*select_finish)(PHA *); /* (pha) optional + called after successful select */ + void (*deselect)(PHA *); /* (pha) + release SCSI bus */ + int (*get_bus_status)(PHA *); /* (pha) + return (REQ,BSY,MSG,C/D,I/O) */ + void (*slow_start)(PHA *,char *); /* (pha,byte) + start transfer of one byte using + explicit handshaking */ + int (*slow_done)(PHA *); /* (pha) + has the device acked the byte ? */ + void (*slow_end)(PHA *); /* (pha) + shut down the slow transfer */ + void (*start_block)(PHA *,int); /* (pha,read) + start data transfer */ + int (*transfer_block)(PHA *,char *,int,int); + /* (pha,buf,len,read) + transfer as much as possible and + return count of bytes + can return -1 if error detected */ + int (*transfer_ready)(PHA *pha);/* (pha) + can we go again yet ? + >0 = yes, 0 = try again, -1 = done */ + int (*transfer_done)(PHA *pha); /* (pha) + has all data been flushed ? + 1 = yes, 0 = try again */ + void (*end_block)(PHA *,int); /* (pha,read) + shut down block transfer */ + void (*reset_bus)(PHA *); /* (pha) optional + reset SCSI bus if possible */ + +}; + +/* constants for the params array */ + +#define PPSC_PARM_PORT 0 +#define PPSC_PARM_MODE 1 +#define PPSC_PARM_DLY 2 +#define PPSC_PARM_NICE 3 +#define PPSC_PARM_SGTS 4 +#define PPSC_PARM_SLOW 5 + +/* constants for get_bus_status */ + +#define PPSC_REQ 16 +#define PPSC_BSY 8 +#define PPSC_MSG 4 +#define PPSC_CD 2 +#define PPSC_IO 1 + +/* phases */ + +#define PPSC_PH_NONE 0 +#define PPSC_PH_WRITE (PPSC_REQ|PPSC_BSY) +#define PPSC_PH_READ (PPSC_PH_WRITE|PPSC_IO) +#define PPSC_PH_CMD (PPSC_PH_WRITE|PPSC_CD) +#define PPSC_PH_STAT (PPSC_PH_READ|PPSC_CD) +#define PPSC_PH_MSGIN (PPSC_PH_STAT|PPSC_MSG) + +typedef struct ppsc_protocol PSP; + +extern int ppsc_detect( PSP *, Scsi_Host_Template *, int); + +#ifdef PPSC_HA_MODULE + +static int verbose = PPSC_VERB_NORMAL; + +static int host0[8] = {-1,-1,-1,-1,-1,-1,-1,-1}; +static int host1[8] = {-1,-1,-1,-1,-1,-1,-1,-1}; +static int host2[8] = {-1,-1,-1,-1,-1,-1,-1,-1}; +static int host3[8] = {-1,-1,-1,-1,-1,-1,-1,-1}; + +#ifndef MODULE + +static STT stt[4] = { {"host0",8,host0}, + {"host1",8,host1}, + {"host2",8,host2}, + {"host3",8,host3} }; +#endif + +MODULE_PARM(host0,"1-8i"); +MODULE_PARM(host1,"1-8i"); +MODULE_PARM(host2,"1-8i"); +MODULE_PARM(host3,"1-8i"); +MODULE_PARM(verbose,"i"); + +static struct ppsc_host_adapter host_structs[4]; + +#define delay_p (pha->delay?udelay(pha->delay):0) +#define out_p(offs,byte) outb(byte,pha->port+offs); delay_p; +#define in_p(offs) (delay_p,inb(pha->port+offs)) + +#define w0(byte) {out_p(0,byte);} +#define r0() (in_p(0) & 0xff) +#define w1(byte) {out_p(1,byte);} +#define r1() (in_p(1) & 0xff) +#define w2(byte) {out_p(2,byte);} +#define r2() (in_p(2) & 0xff) +#define w3(byte) {out_p(3,byte);} +#define w4(byte) {out_p(4,byte);} +#define r4() (in_p(4) & 0xff) +#define w4w(data) {outw(data,pha->port+4); delay_p;} +#define w4l(data) {outl(data,pha->port+4); delay_p;} +#define r4w() (delay_p,inw(pha->port+4)&0xffff) +#define r4l() (delay_p,inl(pha->port+4)&0xffffffff) + +#endif /* PPSC_HA_MODULE */ +#endif /* PPSC_BASE */ +#endif /* _PPSC_H */ + +/* end of ppscsi.h */ + diff -urN linux-2.2.10-ref/drivers/scsi/sparcsi.c linux-2.2.10/drivers/scsi/sparcsi.c --- linux-2.2.10-ref/drivers/scsi/sparcsi.c Wed Dec 31 19:00:00 1969 +++ linux-2.2.10/drivers/scsi/sparcsi.c Wed Jun 23 14:31:38 1999 @@ -0,0 +1,389 @@ +/* + sparcsi.c (c) 1997-1999 Grant Guenther + + This is the low-level protocol module for the WBS-11A parallel + port SCSI adapter. This adapter has been marketed by LinkSys + as the "ParaSCSI+" and by Shining Technologies as the "SparCSI". + The device is constructed from the KBIC-951A ISA replicator + chip from KingByte and the NCR 5380. + +*/ + +#define SPARCSI_VERSION "0.90" + +#define PPSC_BASE +#define PPSC_HA_MODULE + +#include "ppscsi.h" + +#define r12w() (delay_p,inw(pha->port+1)&0xffff) + +#define j44(a,b) ((((a>>4)&0x0f)|(b&0xf0))^0x88) +#define j53(w) (((w>>3)&0x1f)|((w>>4)&0xe0)) + +static char sparcsi_map[256]; /* status bits permutation */ + +static void sparcsi_init( PHA *pha) + +/* { REQ, BSY, MSG, CD, IO} */ + +{ char key[5] = {0x20,0x40,0x10,0x08,0x04}; + + ppsc_make_map(sparcsi_map,key,0); + sprintf(pha->ident,"sparcsi %s (%s), WBS-11A", + SPARCSI_VERSION,PPSC_H_VERSION); +} + +static void sparcsi_write_regr( PHA *pha, int regr, int value) + +{ switch (pha->mode) { + + case 0: + case 1: + case 2: w0(regr|0x10); w2(4); w2(6); w2(4); + w0(value); w2(5); w2(4); + break; + + case 3: w0(0x20); w2(4); w2(6); w2(4); w3(regr); + w4(value); w2(4); w2(0); w2(4); + break; + + } +} + +static int sparcsi_read_regr( PHA *pha, int regr) + +{ int a, b; + + switch (pha->mode) { + + case 0: w0(regr|0x18); w2(4); w2(6); w2(4); w2(5); + a = r1(); w0(0x58); b = r1(); w2(4); + return j44(a,b); + + case 1: w0(regr|0x58); w2(4); w2(6); w2(4); w2(5); + a = r12w(); w2(4); + return j53(a); + + case 2: w0(regr|0x98); w2(4); w2(6); w2(4); w2(0xa5); + a = r0(); w2(4); + return a; + + case 3: w0(0x20); w2(4); w2(6); w2(4); w3(regr); + w2(0xe4); a = r4(); w2(4); w2(0); w2(4); + return a; + + } + return -1; +} + +static void sparcsi_read_block( PHA *pha, char *buf, int len) + +{ int k, a, b; + + switch (pha->mode) { + + case 0: w0(8); w2(4); w2(6); w2(4); + for (k=0;kmode) { + + case 0: + case 1: + case 2: w0(0); w2(4); w2(6); w2(4); + for(k=0;ksaved_r0 = r0(); + pha->saved_r2 = r2(); + w2(4); +} + +static void sparcsi_disconnect( PHA *pha) + +{ w0(pha->saved_r0); + w2(pha->saved_r2); +} + +#define WR(r,v) sparcsi_write_regr(pha,r,v) +#define RR(r) (sparcsi_read_regr(pha,r)) + +static int sparcsi_test_proto( PHA *pha) + +{ int k, e; + + e = 0; + + sparcsi_connect(pha); + + if (!pha->private[0]) { /* reset the SCSI bus on first sight */ + + if (V_FULL) printk("%s: SCSI reset ...\n",pha->device); + + WR(1,0x80); udelay(60); + WR(1,0); + scsi_sleep(5*HZ); + pha->private[0] = 1; + } + + WR(1,0); + WR(1,1); + + if (V_PROBE) + printk("%s: 5380 regrs [4]=%x [5]=%x\n",pha->device,RR(4),RR(5)); + + for (k=0;k<256;k++) { + WR(0,k); + if (RR(0) != k) e++; + WR(0,255-k); + if (RR(0) != (255-k)) e++; + } + + WR(1,0); + + sparcsi_disconnect(pha); + + return e; +} + +static int sparcsi_select( PHA *pha, int initiator, int target) + +{ WR(3,0); WR(1,1); + WR(0,(1 << initiator)); WR(2,1); udelay(100); + if (RR(1) != 0x41) { + WR(1,0); + return -1; + } + + WR(1,5); WR(0,(1 << initiator)|(1 << target)); + WR(2,0); WR(2,0); WR(2,0); + return 0; +} + +static int sparcsi_test_select( PHA *pha) + +{ return ((RR(4) & 0x42) == 0x42); +} + +static void sparcsi_select_finish( PHA *pha) + +{ WR(3,2); WR(1,5); WR(1,1); +} + +static void sparcsi_deselect( PHA *pha) + +{ WR(1,0); +} + +static int sparcsi_get_bus_status( PHA *pha) + +{ int s; + + s = RR(4); + return sparcsi_map[s]; +} + +static void sparcsi_slow_start( PHA *pha, char *val) + +{ int ph, io; + + ph = ((RR(4)>>2)&7); + io = (ph & 1); + + WR(3,ph); + WR(1,1-io); + if (io) *val = RR(0); else WR(0,*val); + WR(1,0x10+(1-io)); +} + +static int sparcsi_slow_done( PHA *pha) + +{ return ((RR(4) & 0x20) == 0); +} + +static void sparcsi_slow_end( PHA *pha) + +{ int io; + + io = ((RR(4)>>2)&1); + + WR(1,1-io); +} + +static void sparcsi_start_block( PHA *pha, int rd) + +{ if (rd) { + + WR(3,1); WR(1,0); + WR(2,2); WR(7,3); + WR(3,1); WR(1,0); + + } else { + + WR(3,0); WR(1,1); + WR(2,2); WR(5,0); + WR(3,0); WR(1,1); + + } + pha->priv_flag = rd; +} + +static int sparcsi_transfer_ready( PHA *pha) + +{ int chunk; + + chunk = 512; + if ((pha->data_count == 0) && (!pha->priv_flag)) chunk++; + + if (r1() & 0x40) return chunk; + if (!(RR(5) & 8)) return -1; + return 0; +} + +static int sparcsi_transfer_block( PHA *pha, char * buf, int buflen, int rd) + +{ int k, n; + + k = 0; + while (k < buflen) { + + n = sparcsi_transfer_ready(pha); + + if (n <= 0) break; + + if (n > (buflen - k)) n = buflen - k; + + if (rd) sparcsi_read_block(pha,buf,n); + else sparcsi_write_block(pha,buf,n); + + k += n; buf += n; + } + + return k; +} + +static int sparcsi_transfer_done( PHA *pha) + +{ return 1; +} + +static void sparcsi_end_block( PHA *pha, int rd) + +{ char buf[2] = {0,0}; + + if (!rd) sparcsi_write_block(pha,buf,1); + + WR(2,0); +} + +static void sparcsi_reset_bus( PHA *pha) + +{ WR(1,1); WR(3,0); + WR(2,0); + WR(1,0x80); udelay(60); + WR(1,0); + WR(2,0); + WR(1,1); WR(3,0); + WR(2,0); +} + +char *(mode_strings[4]) = {"Nybble","KBIC 5/3","PS/2","EPP"}; + +static struct ppsc_protocol sparcsi_psp = { + + {&host0,&host1,&host2,&host3}, /* params */ + &host_structs, /* hosts */ + 4, /* num_modes */ + 3, /* epp_first */ + 1, /* default_delay */ + 1, /* can_message */ + 16, /* sg_tablesize */ + mode_strings, + sparcsi_init, + NULL, + sparcsi_connect, + sparcsi_disconnect, + sparcsi_test_proto, + sparcsi_select, + sparcsi_test_select, + sparcsi_select_finish, + sparcsi_deselect, + sparcsi_get_bus_status, + sparcsi_slow_start, + sparcsi_slow_done, + sparcsi_slow_end, + sparcsi_start_block, + sparcsi_transfer_block, + sparcsi_transfer_ready, + sparcsi_transfer_done, + sparcsi_end_block, + sparcsi_reset_bus + }; + +int sparcsi_detect( Scsi_Host_Template *tpnt ) + +{ return ppsc_detect( &sparcsi_psp, tpnt, verbose); +} + +struct proc_dir_entry proc_scsi_sparcsi = + {PROC_SCSI_SPARCSI,7,"sparcsi",S_IFDIR|S_IRUGO|S_IXUGO,2}; + +#ifdef MODULE + +Scsi_Host_Template driver_template = PPSC_TEMPLATE(sparcsi); + +#include "scsi_module.c" + +#else + +void sparcsi_setup( char *str, int *ints) + +{ ppsc_gen_setup(stt,4,str); +} + +#endif + +/* end of sparcsi.c */ + diff -urN linux-2.2.10-ref/drivers/scsi/t348.c linux-2.2.10/drivers/scsi/t348.c --- linux-2.2.10-ref/drivers/scsi/t348.c Wed Dec 31 19:00:00 1969 +++ linux-2.2.10/drivers/scsi/t348.c Wed Jun 23 14:31:38 1999 @@ -0,0 +1,320 @@ +/* + t348.c (c) 1997-1999 Grant Guenther + + This is the low-level protocol module for the Adaptec APA-348 + (aka Trantor T348) parallel port SCSI adapter. It forms part + of the 'ppSCSI' suite of drivers. + +*/ + +#define T348_VERSION "0.91" + +#define PPSC_BASE +#define PPSC_HA_MODULE + +#include "ppscsi.h" + +#define j44(a,b) (((a<<1)&0xf0)+((b>>3)&0x0f)) + +static char t348_map[256]; /* status bits permutation */ + +static void t348_init( PHA *pha) + +/* { REQ, BSY, MSG, CD, IO} */ + +{ char key[5] = {0x20,0x40,0x10,0x08,0x04}; + + ppsc_make_map(t348_map,key,0); + sprintf(pha->ident,"t348 %s (%s), Adaptec APA-348", + T348_VERSION,PPSC_H_VERSION); +} + +static void t348_write_regr( PHA *pha, int regr, int value) + +{ w0(0x40+regr); w2(1); w2(0); w0(value); w2(8); w2(0); +} + +static int t348_read_regr( PHA *pha, int regr) + +{ int s,a,b; + + w0(0x10+regr); s = r2(); w2(s|1); w2(s); w2(8); + w0(0x80); a = r1(); w0(0); b = r1(); w2(0); + return j44(a,b); +} + +static void t348_connect( PHA *pha) + +{ int t; + + pha->saved_r0 = r0(); + w0(0); + t = r2(); + w2(t%16); w0(0xfe); w2(t%4); w2((t%4)+8); w2(0); + pha->saved_r2 = t; + +} + +static void t348_disconnect( PHA *pha) + +{ w0(0x71); w2(1); w2(0); + w0(pha->saved_r0); + w2(pha->saved_r2); + +} + +static int t348_test_proto( PHA *pha) + +{ int k, e, a, b; + int wnt[3] = {0x6c, 0x55, 0xaa}; + + e = 0; + + t348_connect(pha); + + switch (pha->mode) { + + case 0: w0(0x70); w2(1); w2(0); w0(0); + for (k=0;k<3;k++) { + w2(8); a = r1(); w2(0); + w2(8); w2(8); w2(8); w2(8); w2(8); + b = r1(); w2(0); + if (j44(b,a) != wnt[k]) e++; + } + break; + + case 1: w0(0x50); w2(1); w2(0); + for (k=0;k<3;k++) { + w2(0xe0); w2(0xe8); + if (r0() != wnt[k]) e++; + w2(0xe0); w2(0xe8); + } + + } + + t348_disconnect(pha); + + return e; +} + +/* The T348 appears to contain a NCR 5380 core. The following + functions use the 5380 registers. See NCR5380.h for clues. +*/ + +#define WR(r,v) t348_write_regr(pha,r,v) +#define RR(r) (t348_read_regr(pha,r)) + +static int t348_select( PHA *pha, int initiator, int target) + +{ WR(3,0); WR(1,1); + WR(0,(1 << initiator)); WR(2,1); udelay(100); + if (RR(1) != 0x41) { + WR(1,0); + return -1; + } + + WR(1,5); WR(0,(1 << initiator)|(1 << target)); + WR(2,0); WR(2,0); WR(2,0); + return 0; +} + +static int t348_test_select( PHA *pha) + +{ return ((RR(4) & 0x42) == 0x42); +} + +static void t348_select_finish( PHA *pha) + +{ WR(3,2); WR(1,5); WR(1,1); +} + +static void t348_deselect( PHA *pha) + +{ WR(1,0); +} + +static int t348_get_bus_status( PHA *pha) + +{ int s; + + s = RR(4); + return t348_map[s]; +} + +static void t348_slow_start( PHA *pha, char *val) + +{ int ph, io; + + ph = ((RR(4)>>2)&7); + io = (ph & 1); + + WR(3,ph); + WR(1,1-io); + if (io) *val = RR(0); else WR(0,*val); + WR(1,0x10+(1-io)); +} + +static int t348_slow_done( PHA *pha) + +{ return ((RR(4) & 0x20) == 0); +} + +static void t348_slow_end( PHA *pha) + +{ int io; + + io = ((RR(4)>>2)&1); + + WR(1,1-io); +} + +static void t348_start_block( PHA *pha, int rd) + +{ if (rd) { + + WR(3,1); WR(1,0); + WR(2,2); WR(7,3); + WR(3,1); WR(1,0); + + switch (pha->mode) { + + case 0: w0(0x31); w2(1); w2(0); w0(0x80); w2(8); + break; + + case 1: w0(0x21); w2(1); w2(0); w2(0xe8); + break; + } + + } else { + + WR(3,0); WR(1,1); + WR(2,2); WR(5,0); + WR(3,0); WR(1,1); + + w0(0x61); w2(1); w2(0); + } +} + +static int t348_transfer_ready( PHA *pha) + +{ if (r1() & 0x80) return 1; + + if (pha->data_dir == 0) return 0; + return -1; +} + +static int t348_transfer_block( PHA *pha, char * buf, int buflen, int rd) + +{ int k, a, b; + + k = 0; + while (k < buflen) { + + if (t348_transfer_ready(pha) <= 0) break; + + if (rd) { + switch(pha->mode) { + + case 0: a = r1(); w0(0); b = r1(); w0(0xc0); + buf[k++] = j44(a,b); + a = r1(); w0(0x40); b = r1(); w0(0x80); + buf[k++] = j44(a,b); + break; + + case 1: buf[k++] = r0(); w2(0xea); + buf[k++] = r0(); w2(0xe8); + break; + } + + } else { + + w0(buf[k++]); w2(2); + w0(buf[k++]); w2(0); + } + + } + + return k; +} + +static int t348_transfer_done( PHA *pha) + +{ return 1; +} + +static void t348_end_block( PHA *pha, int rd) + +{ w2(0); + WR(2,0); +} + + +static void t348_reset_bus( PHA *pha) + +{ WR(1,1); WR(3,0); + WR(2,0); + WR(1,0x80); udelay(60); + WR(1,0); + WR(2,0); + WR(1,1); WR(3,0); + WR(2,0); +} + +char *(mode_strings[2]) = {"Nybble","PS/2"}; + +static struct ppsc_protocol t348_psp = { + + {&host0,&host1,&host2,&host3}, /* params */ + &host_structs, /* hosts */ + 2, /* num_modes */ + 2, /* epp_first */ + 1, /* default_delay */ + 1, /* can_message */ + 0, /* sg_tablesize */ + mode_strings, + t348_init, + NULL, + t348_connect, + t348_disconnect, + t348_test_proto, + t348_select, + t348_test_select, + t348_select_finish, + t348_deselect, + t348_get_bus_status, + t348_slow_start, + t348_slow_done, + t348_slow_end, + t348_start_block, + t348_transfer_block, + t348_transfer_ready, + t348_transfer_done, + t348_end_block, + t348_reset_bus + }; + +int t348_detect( Scsi_Host_Template *tpnt ) + +{ return ppsc_detect( &t348_psp, tpnt, verbose); +} + +struct proc_dir_entry proc_scsi_t348 = + {PROC_SCSI_T348,4,"t348",S_IFDIR|S_IRUGO|S_IXUGO,2}; + +#ifdef MODULE + +Scsi_Host_Template driver_template = PPSC_TEMPLATE(t348); + +#include "scsi_module.c" + +#else + +void t348_setup( char *str, int *ints) + +{ ppsc_gen_setup(stt,4,str); +} + +#endif + +/* end of t348.c */ + diff -urN linux-2.2.10-ref/drivers/scsi/t358.c linux-2.2.10/drivers/scsi/t358.c --- linux-2.2.10-ref/drivers/scsi/t358.c Wed Dec 31 19:00:00 1969 +++ linux-2.2.10/drivers/scsi/t358.c Wed Jun 23 14:31:38 1999 @@ -0,0 +1,397 @@ +/* + t358.c (c) 1997-1999 Grant Guenther + + This is the low-level protocol module for the Adaptec APA-358 + (aka Trantor T358) parallel port SCSI adapter. It forms part + of the 'ppSCSI' suite of drivers. + +*/ + +#define T358_VERSION "0.90" + +#define PPSC_BASE +#define PPSC_HA_MODULE + +#include "ppscsi.h" + +#define j44(a,b) (((a<<1)&0xf0)+((b>>3)&0x0f)) + +static char t358_map[256]; /* status bits permutation */ + +static void t358_init( PHA *pha) + +/* { REQ, BSY, MSG, CD, IO} */ + +{ char key[5] = {0x20,0x40,0x10,0x08,0x04}; + + ppsc_make_map(t358_map,key,0); + sprintf(pha->ident,"t358 %s (%s), Adaptec APA-358", + T358_VERSION,PPSC_H_VERSION); +} + +static void t358_write_regr( PHA *pha, int regr, int value) + +{ int x; + + switch (pha->mode) { + + case 0: + case 1: w0(regr); x = r2(); w2(1); w2(9); w2(0); w2(0); + w0(value); w2(1); w2(3); w2(0); w2(x); + break; + + case 2: w2(0xc0); w3(regr); w4(value); + break; + + } +} + +static int t358_read_regr( PHA *pha, int regr) + +{ int h, l; + + switch (pha->mode) { + + case 0: w0(regr); w2(1); w2(9); w2(0); w2(0); + w0(0x80); w2(2); h = r1(); + w0(0); l = r1(); w2(0); + return j44(h,l); + + case 1: w0(regr); h = r2(); w2(1); w2(9); w2(0); w2(0); + w2(0xe2); l = r0(); w2(h); + return l; + + case 2: h = r2(); w2(0xe0); w3(regr); w2(0xe0); + l = r4(); w2(h); + return l; + + } + return 0; +} + +static void t358_read_block( PHA *pha, char *buf, int len) + +{ int k, h, l; + + switch (pha->mode) { + + case 0: w0(0x10); w2(1); w2(9); w2(0); w2(0); + for (k=0;kmode) { + + case 0: + case 1: w0(0x10); x = r2(); + w2(1); w2(9); w2(0); w2(0); + for (k=0;ksaved_r0 = r0(); + w0(0); + pha->saved_r2 = r2(); + b = pha->saved_r2 % 4; + w0(0xf7); w2(b+4); w2(b); w2(b+8); w2(b); w2(0); + + if (pha->mode) { w0(0x80); w2(1); w2(9); w2(1); w2(0); } + else { w0(0xa0); w2(1); w2(9); w2(0); } + +} + +static void t358_disconnect( PHA *pha) + +{ w0(pha->saved_r0); + w2(pha->saved_r2); +} + +static int t358_test_proto( PHA *pha) + +{ int h, l, a, b; + int j = 0, k = 0, e = 0; + + t358_connect(pha); + + switch (pha->mode) { + + case 0: w0(0x80); w2(8); h = r1(); w0(0); l = r1(); + w2(0); w2(8); a = r1(); w0(0); b = r1(); w2(0); + k = j44(h,l); j = j44(a,b); + break; + + case 1: w2(0xe0); w0(0); w2(0xe8); k = r0(); + w2(0xe0); w2(0xe8); j = r0(); w2(0xe0); + break; + + case 2: w0(0xa0); w2(1); w2(9); w2(0); + w0(0x80); w2(8); h = r1(); w0(0); l = r1(); + w2(0); w2(8); a = r1(); w0(0); b = r1(); w2(0); + k = j44(h,l); j = j44(a,b); + w0(0x80); w2(1); w2(9); w2(1); w2(0); + + } + + if (V_PROBE) printk("%s: Signature: %x %x\n",pha->device,k,j); + + if ((k != 0xe8) || (j != 0xff)) e++; + + t358_disconnect(pha); + + if (!e) { + + t358_connect(pha); + + for (j=0;j<256;j++) { + t358_write_regr(pha,0,j); + k = t358_read_regr(pha,0); + if (k != j) e++; + } + + t358_disconnect(pha); + + } + + return e; +} + +/* The T358 appears to contain a NCR 53c400 core. Check NCR5380.h + for hints about the regrs ... */ + +#define WR(r,v) t358_write_regr(pha,r+8,v) +#define RR(r) (t358_read_regr(pha,r+8)) + +static int t358_select( PHA *pha, int initiator, int target) + +{ WR(3,0); WR(1,1); + WR(0,(1 << initiator)); WR(2,1); udelay(100); + if (RR(1) != 0x41) { + WR(1,0); + return -1; + } + + WR(1,5); WR(0,(1 << initiator)|(1 << target)); + WR(2,0); WR(2,0); WR(2,0); + return 0; +} + +static int t358_test_select( PHA *pha) + +{ return ((RR(4) & 0x42) == 0x42); +} + +static void t358_select_finish( PHA *pha) + +{ WR(3,2); WR(1,5); WR(1,1); +} + +static void t358_deselect( PHA *pha) + +{ WR(1,0); +} + +static int t358_get_bus_status( PHA *pha) + +{ int s; + + s = RR(4); + return t358_map[s]; +} + +static void t358_slow_start( PHA *pha, char *val) + +{ int ph, io; + + ph = ((RR(4)>>2)&7); + io = (ph & 1); + + WR(3,ph); + WR(1,1-io); + if (io) *val = RR(0); else WR(0,*val); + WR(1,0x10+(1-io)); +} + +static int t358_slow_done( PHA *pha) + +{ return ((RR(4) & 0x20) == 0); +} + +static void t358_slow_end( PHA *pha) + +{ int io; + + io = ((RR(4)>>2)&1); + + WR(1,1-io); +} + +static void t358_start_block( PHA *pha, int rd) + +{ if (rd) { + WR(3,1); WR(1,0); + WR(2,2); + WR(0x10,0x40); WR(2,0); WR(2,0xa); + WR(3,1); WR(1,0); WR(7,3); + } else { + WR(3,0); WR(1,1); + WR(2,2); + WR(0x10,0); WR(2,0); WR(2,0xa); + WR(3,0); WR(1,1); WR(5,0); + } + WR(0x11,pha->tlen/128); +} + +static int t358_transfer_ready( PHA *pha) + +{ int r; + + r = RR(0x10); + + if (!(r & 4)) return 128; /* 4 is host buffer not ready */ + + if (r & 1) return -1; /* last block transferred */ + + return 0; +} + +static int t358_transfer_block( PHA *pha, char * buf, int buflen, int rd) + +{ int k, n; + + k = 0; + while (k < buflen) { + + n = t358_transfer_ready(pha); + + if (n <= 0) break; + + if (n > (buflen - k)) n = buflen - k; + + if (rd) t358_read_block(pha,buf,n); + else t358_write_block(pha,buf,n); + + k += n; buf += n; + + } + + return k; +} + +static int t358_transfer_done( PHA *pha) + +{ if (RR(0x10) & 1) return 1; /* last block transferred */ + return 0; +} + +static void t358_end_block( PHA *pha, int rd) + +{ WR(2,0); +} + + +static void t358_reset_bus( PHA *pha) + +{ WR(1,1); WR(3,0); + WR(2,0); + WR(1,0x80); udelay(60); + WR(1,0); + WR(2,0); + WR(1,1); WR(3,0); + WR(2,0); +} + +char *(mode_strings[3]) = {"Nybble","PS/2","EPP"}; + +static struct ppsc_protocol t358_psp = { + + {&host0,&host1,&host2,&host3}, /* params */ + &host_structs, /* hosts */ + 3, /* num_modes */ + 2, /* epp_first */ + 1, /* default_delay */ + 1, /* can_message */ + 16, /* sg_tablesize */ + mode_strings, + t358_init, + NULL, + t358_connect, + t358_disconnect, + t358_test_proto, + t358_select, + t358_test_select, + t358_select_finish, + t358_deselect, + t358_get_bus_status, + t358_slow_start, + t358_slow_done, + t358_slow_end, + t358_start_block, + t358_transfer_block, + t358_transfer_ready, + t358_transfer_done, + t358_end_block, + t358_reset_bus + }; + +int t358_detect( Scsi_Host_Template *tpnt ) + +{ return ppsc_detect( &t358_psp, tpnt, verbose); +} + +struct proc_dir_entry proc_scsi_t358 = + {PROC_SCSI_T358,4,"t358",S_IFDIR|S_IRUGO|S_IXUGO,2}; + +#ifdef MODULE + +Scsi_Host_Template driver_template = PPSC_TEMPLATE(t358); + +#include "scsi_module.c" + +#else + +void t358_setup( char *str, int *ints) + +{ ppsc_gen_setup(stt,4,str); +} + +#endif + +/* end of t358.c */ + diff -urN linux-2.2.10-ref/include/linux/proc_fs.h linux-2.2.10/include/linux/proc_fs.h --- linux-2.2.10-ref/include/linux/proc_fs.h Tue May 11 13:36:09 1999 +++ linux-2.2.10/include/linux/proc_fs.h Wed Jun 23 14:37:39 1999 @@ -206,6 +206,12 @@ PROC_SCSI_INI9100U, PROC_SCSI_INIA100, PROC_SCSI_FCAL, + PROC_SCSI_ONSCSI, + PROC_SCSI_EPST, + PROC_SCSI_EPSA2, + PROC_SCSI_T348, + PROC_SCSI_T358, + PROC_SCSI_SPARCSI, PROC_SCSI_SCSI_DEBUG, PROC_SCSI_NOT_PRESENT, PROC_SCSI_FILE, /* I'm assuming here that we */