summaryrefslogtreecommitdiffstats
path: root/drivers/block/paride
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1998-03-17 22:05:47 +0000
committerRalf Baechle <ralf@linux-mips.org>1998-03-17 22:05:47 +0000
commit27cfca1ec98e91261b1a5355d10a8996464b63af (patch)
tree8e895a53e372fa682b4c0a585b9377d67ed70d0e /drivers/block/paride
parent6a76fb7214c477ccf6582bd79c5b4ccc4f9c41b1 (diff)
Look Ma' what I found on my harddisk ...
o New faster syscalls for 2.1.x, too o Upgrade to 2.1.89. Don't try to run this. It's flaky as hell. But feel free to debug ...
Diffstat (limited to 'drivers/block/paride')
-rw-r--r--drivers/block/paride/Config.in20
-rw-r--r--drivers/block/paride/Makefile141
-rw-r--r--drivers/block/paride/aten.c166
-rw-r--r--drivers/block/paride/bpck.c476
-rw-r--r--drivers/block/paride/comm.c222
-rw-r--r--drivers/block/paride/dstr.c237
-rw-r--r--drivers/block/paride/epat.c315
-rw-r--r--drivers/block/paride/epia.c318
-rw-r--r--drivers/block/paride/frpw.c256
-rw-r--r--drivers/block/paride/kbic.c305
-rw-r--r--drivers/block/paride/on20.c157
-rw-r--r--drivers/block/paride/on26.c261
-rw-r--r--drivers/block/paride/paride.c485
-rw-r--r--drivers/block/paride/paride.h157
-rw-r--r--drivers/block/paride/pcd.c816
-rw-r--r--drivers/block/paride/pd.c1088
-rw-r--r--drivers/block/paride/pf.c1072
-rw-r--r--drivers/block/paride/pseudo.h138
-rw-r--r--drivers/block/paride/pt.c959
-rw-r--r--drivers/block/paride/setup.h56
20 files changed, 7645 insertions, 0 deletions
diff --git a/drivers/block/paride/Config.in b/drivers/block/paride/Config.in
new file mode 100644
index 000000000..cc9479184
--- /dev/null
+++ b/drivers/block/paride/Config.in
@@ -0,0 +1,20 @@
+#
+# PARIDE configuration
+#
+comment 'Parallel IDE high-level drivers'
+dep_tristate ' Parallel port IDE disks' CONFIG_PARIDE_PD $CONFIG_PARIDE
+dep_tristate ' Parallel port ATAPI CD-ROMs' CONFIG_PARIDE_PCD $CONFIG_PARIDE
+dep_tristate ' Parallel port ATAPI disks' CONFIG_PARIDE_PF $CONFIG_PARIDE
+dep_tristate ' Parallel port ATAPI tapes' CONFIG_PARIDE_PT $CONFIG_PARIDE
+comment 'Parallel IDE protocol modules'
+dep_tristate ' ATEN EH-100 protocol' CONFIG_PARIDE_ATEN $CONFIG_PARIDE
+dep_tristate ' MicroSolutions backpack protocol' CONFIG_PARIDE_BPCK $CONFIG_PARIDE
+dep_tristate ' DataStor Commuter protocol' CONFIG_PARIDE_COMM $CONFIG_PARIDE
+dep_tristate ' DataStor EP-2000 protocol' CONFIG_PARIDE_DSTR $CONFIG_PARIDE
+dep_tristate ' Shuttle EPAT/EPEZ protocol' CONFIG_PARIDE_EPAT $CONFIG_PARIDE
+dep_tristate ' Shuttle EPIA protocol' CONFIG_PARIDE_EPIA $CONFIG_PARIDE
+dep_tristate ' FreeCom power protocol' CONFIG_PARIDE_FRPW $CONFIG_PARIDE
+dep_tristate ' KingByte KBIC-951A/971A protocols' CONFIG_PARIDE_KBIC $CONFIG_PARIDE
+dep_tristate ' OnSpec 90c20 protocol' CONFIG_PARIDE_ON20 $CONFIG_PARIDE
+dep_tristate ' OnSpec 90c26 protocol' CONFIG_PARIDE_ON26 $CONFIG_PARIDE
+#
diff --git a/drivers/block/paride/Makefile b/drivers/block/paride/Makefile
new file mode 100644
index 000000000..61e710801
--- /dev/null
+++ b/drivers/block/paride/Makefile
@@ -0,0 +1,141 @@
+#
+# Makefile for PARIDE
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now inherited from the
+# parent makes..
+
+SUB_DIRS :=
+MOD_SUB_DIRS := $(SUB_DIRS)
+ALL_SUB_DIRS := $(SUB_DIRS)
+
+L_TARGET := paride.a
+MX_OBJS :=
+LX_OBJS :=
+MI_OBJS :=
+MIX_OBJS :=
+
+ifeq ($(CONFIG_PARIDE),y)
+ LX_OBJS += paride.o
+else
+ ifeq ($(CONFIG_PARIDE),m)
+ MX_OBJS += paride.o
+ endif
+endif
+
+ifeq ($(CONFIG_PARIDE_PD),y)
+ LX_OBJS += pd.o
+else
+ ifeq ($(CONFIG_PARIDE_PD),m)
+ MX_OBJS += pd.o
+ endif
+endif
+
+ifeq ($(CONFIG_PARIDE_PCD),y)
+ LX_OBJS += pcd.o
+else
+ ifeq ($(CONFIG_PARIDE_PCD),m)
+ MX_OBJS += pcd.o
+ endif
+endif
+
+ifeq ($(CONFIG_PARIDE_PF),y)
+ LX_OBJS += pf.o
+else
+ ifeq ($(CONFIG_PARIDE_PF),m)
+ MX_OBJS += pf.o
+ endif
+endif
+
+ifeq ($(CONFIG_PARIDE_PT),y)
+ LX_OBJS += pt.o
+else
+ ifeq ($(CONFIG_PARIDE_PT),m)
+ MX_OBJS += pt.o
+ endif
+endif
+
+ifeq ($(CONFIG_PARIDE_ATEN),y)
+ LX_OBJS += aten.o
+else
+ ifeq ($(CONFIG_PARIDE_ATEN),m)
+ MX_OBJS += aten.o
+ endif
+endif
+
+ifeq ($(CONFIG_PARIDE_BPCK),y)
+ LX_OBJS += bpck.o
+else
+ ifeq ($(CONFIG_PARIDE_BPCK),m)
+ MX_OBJS += bpck.o
+ endif
+endif
+
+ifeq ($(CONFIG_PARIDE_COMM),y)
+ LX_OBJS += comm.o
+else
+ ifeq ($(CONFIG_PARIDE_COMM),m)
+ MX_OBJS += comm.o
+ endif
+endif
+
+ifeq ($(CONFIG_PARIDE_DSTR),y)
+ LX_OBJS += dstr.o
+else
+ ifeq ($(CONFIG_PARIDE_DSTR),m)
+ MX_OBJS += dstr.o
+ endif
+endif
+
+ifeq ($(CONFIG_PARIDE_KBIC),y)
+ LX_OBJS += kbic.o
+else
+ ifeq ($(CONFIG_PARIDE_KBIC),m)
+ MX_OBJS += kbic.o
+ endif
+endif
+
+ifeq ($(CONFIG_PARIDE_EPAT),y)
+ LX_OBJS += epat.o
+else
+ ifeq ($(CONFIG_PARIDE_EPAT),m)
+ MX_OBJS += epat.o
+ endif
+endif
+
+ifeq ($(CONFIG_PARIDE_EPIA),y)
+ LX_OBJS += epia.o
+else
+ ifeq ($(CONFIG_PARIDE_EPIA),m)
+ MX_OBJS += epia.o
+ endif
+endif
+
+ifeq ($(CONFIG_PARIDE_FRPW),y)
+ LX_OBJS += frpw.o
+else
+ ifeq ($(CONFIG_PARIDE_FRPW),m)
+ MX_OBJS += frpw.o
+ endif
+endif
+
+ifeq ($(CONFIG_PARIDE_ON20),y)
+ LX_OBJS += on20.o
+else
+ ifeq ($(CONFIG_PARIDE_ON20),m)
+ MX_OBJS += on20.o
+ endif
+endif
+
+ifeq ($(CONFIG_PARIDE_ON26),y)
+ LX_OBJS += on26.o
+else
+ ifeq ($(CONFIG_PARIDE_ON26),m)
+ MX_OBJS += on26.o
+ endif
+endif
+
+include $(TOPDIR)/Rules.make
diff --git a/drivers/block/paride/aten.c b/drivers/block/paride/aten.c
new file mode 100644
index 000000000..dd11b03f1
--- /dev/null
+++ b/drivers/block/paride/aten.c
@@ -0,0 +1,166 @@
+/*
+ aten.c (c) 1997 Grant R. Guenther <grant@torque.net>
+ Under the terms of the GNU public license.
+
+ aten.c is a low-level protocol driver for the ATEN EH-100
+ parallel port adapter. The EH-100 supports 4-bit and 8-bit
+ modes only. There is also an EH-132 which supports EPP mode
+ transfers. The EH-132 is not yet supported.
+
+*/
+
+#define ATEN_VERSION "1.0"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "paride.h"
+
+#define j44(a,b) ((((a>>4)&0x0f)|(b&0xf0))^0x88)
+
+/* cont = 0 - access the IDE register file
+ cont = 1 - access the IDE command set
+*/
+
+static int cont_map[2] = { 0x08, 0x20 };
+
+static void aten_write_regr( PIA *pi, int cont, int regr, int val)
+
+{ int r;
+
+ r = regr + cont_map[cont] + 0x80;
+
+ w0(r); w2(0xe); w2(6); w0(val); w2(7); w2(6); w2(0xc);
+}
+
+static int aten_read_regr( PIA *pi, int cont, int regr )
+
+{ int a, b, r;
+
+ r = regr + cont_map[cont] + 0x40;
+
+ switch (pi->mode) {
+
+ case 0: w0(r); w2(0xe); w2(6);
+ w2(7); w2(6); w2(0);
+ a = r1(); w0(0x10); b = r1(); w2(0xc);
+ return j44(a,b);
+
+ case 1: r |= 0x10;
+ w0(r); w2(0xe); w2(6); w0(0xff);
+ w2(0x27); w2(0x26); w2(0x20);
+ a = r0();
+ w2(0x26); w2(0xc);
+ return a;
+ }
+ return -1;
+}
+
+static void aten_read_block( PIA *pi, char * buf, int count )
+
+{ int k, a, b, c, d;
+
+ switch (pi->mode) {
+
+ case 0: w0(0x48); w2(0xe); w2(6);
+ for (k=0;k<count/2;k++) {
+ w2(7); w2(6); w2(2);
+ a = r1(); w0(0x58); b = r1();
+ w2(0); d = r1(); w0(0x48); c = r1();
+ buf[2*k] = j44(c,d);
+ buf[2*k+1] = j44(a,b);
+ }
+ w2(0xc);
+ break;
+
+ case 1: w0(0x58); w2(0xe); w2(6);
+ for (k=0;k<count/2;k++) {
+ w2(0x27); w2(0x26); w2(0x22);
+ a = r0(); w2(0x20); b = r0();
+ buf[2*k] = b; buf[2*k+1] = a;
+ }
+ w2(0x26); w2(0xc);
+ break;
+ }
+}
+
+static void aten_write_block( PIA *pi, char * buf, int count )
+
+{ int k;
+
+ w0(0x88); w2(0xe); w2(6);
+ for (k=0;k<count/2;k++) {
+ w0(buf[2*k+1]); w2(0xe); w2(6);
+ w0(buf[2*k]); w2(7); w2(6);
+ }
+ w2(0xc);
+}
+
+static void aten_connect ( PIA *pi )
+
+{ pi->saved_r0 = r0();
+ pi->saved_r2 = r2();
+ w2(0xc);
+}
+
+static void aten_disconnect ( PIA *pi )
+
+{ w0(pi->saved_r0);
+ w2(pi->saved_r2);
+}
+
+static void aten_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{ char *mode_string[2] = {"4-bit","8-bit"};
+
+ printk("%s: aten %s, ATEN EH-100 at 0x%x, ",
+ pi->device,ATEN_VERSION,pi->port);
+ printk("mode %d (%s), delay %d\n",pi->mode,
+ mode_string[pi->mode],pi->delay);
+
+}
+
+static void aten_inc_use ( void )
+
+{ MOD_INC_USE_COUNT;
+}
+
+static void aten_dec_use ( void )
+
+{ MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol aten = {"aten",0,2,2,1,1,
+ aten_write_regr,
+ aten_read_regr,
+ aten_write_block,
+ aten_read_block,
+ aten_connect,
+ aten_disconnect,
+ 0,
+ 0,
+ 0,
+ aten_log_adapter,
+ aten_inc_use,
+ aten_dec_use
+ };
+
+
+#ifdef MODULE
+
+int init_module(void)
+
+{ return pi_register( &aten ) - 1;
+}
+
+void cleanup_module(void)
+
+{ pi_unregister( &aten );
+}
+
+#endif
+
+/* end of aten.c */
diff --git a/drivers/block/paride/bpck.c b/drivers/block/paride/bpck.c
new file mode 100644
index 000000000..21caad680
--- /dev/null
+++ b/drivers/block/paride/bpck.c
@@ -0,0 +1,476 @@
+/*
+ bpck.c (c) 1996,1997 Grant R. Guenther <grant@torque.net>
+ Under the terms of the GNU public license.
+
+ bpck.c is a low-level protocol driver for the MicroSolutions
+ "backpack" parallel port IDE adapter.
+
+*/
+
+#define BPCK_VERSION "1.0"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "paride.h"
+
+#undef r2
+#undef w2
+
+#define PC pi->private
+#define r2() (PC=(in_p(2) & 0xff))
+#define w2(byte) {out_p(2,byte); PC = byte;}
+#define t2(pat) {PC ^= pat; out_p(2,PC);}
+#define e2() {PC &= 0xfe; out_p(2,PC);}
+#define o2() {PC |= 1; out_p(2,PC);}
+
+#define j44(l,h) (((l>>3)&0x7)|((l>>4)&0x8)|((h<<1)&0x70)|(h&0x80))
+
+/* cont = 0 - access the IDE register file
+ cont = 1 - access the IDE command set
+ cont = 2 - use internal bpck register addressing
+*/
+
+static int cont_map[3] = { 0x40, 0x48, 0 };
+
+static int bpck_read_regr( PIA *pi, int cont, int regr )
+
+{ int r, l, h;
+
+ r = regr + cont_map[cont];
+
+ switch (pi->mode) {
+
+ case 0: w0(r & 0xf); w0(r); t2(2); t2(4);
+ l = r1();
+ t2(4);
+ h = r1();
+ return j44(l,h);
+
+ case 1: w0(r & 0xf); w0(r); t2(2);
+ e2(); t2(0x20);
+ t2(4); h = r0();
+ t2(1); t2(0x20);
+ return h;
+
+ case 2:
+ case 3:
+ case 4: w0(r); w2(9); w2(0); w2(0x20);
+ h = r4();
+ w2(0);
+ return h;
+
+ }
+ return -1;
+}
+
+static void bpck_write_regr( PIA *pi, int cont, int regr, int val )
+
+{ int r;
+
+ r = regr + cont_map[cont];
+
+ switch (pi->mode) {
+
+ case 0:
+ case 1: w0(r);
+ t2(2);
+ w0(val);
+ o2(); t2(4); t2(1);
+ break;
+
+ case 2:
+ case 3:
+ case 4: w0(r); w2(9); w2(0);
+ w0(val); w2(1); w2(3); w2(0);
+ break;
+
+ }
+}
+
+/* These macros access the bpck registers in native addressing */
+
+#define WR(r,v) bpck_write_regr(pi,2,r,v)
+#define RR(r) (bpck_read_regr(pi,2,r))
+
+static void bpck_write_block( PIA *pi, char * buf, int count )
+
+{ int i;
+
+ switch (pi->mode) {
+
+ case 0: WR(4,0x40);
+ w0(0x40); t2(2); t2(1);
+ for (i=0;i<count;i++) { w0(buf[i]); t2(4); }
+ WR(4,0);
+ break;
+
+ case 1: WR(4,0x50);
+ w0(0x40); t2(2); t2(1);
+ for (i=0;i<count;i++) { w0(buf[i]); t2(4); }
+ WR(4,0x10);
+ break;
+
+ case 2: WR(4,0x48);
+ w0(0x40); w2(9); w2(0); w2(1);
+ for (i=0;i<count;i++) w4(buf[i]);
+ w2(0);
+ WR(4,8);
+ break;
+
+ case 3: WR(4,0x48);
+ w0(0x40); w2(9); w2(0); w2(1);
+ for (i=0;i<count/2;i++) w4w(((u16 *)buf)[i]);
+ w2(0);
+ WR(4,8);
+ break;
+
+ case 4: WR(4,0x48);
+ w0(0x40); w2(9); w2(0); w2(1);
+ for (i=0;i<count/4;i++) w4l(((u32 *)buf)[i]);
+ w2(0);
+ WR(4,8);
+ break;
+ }
+}
+
+static void bpck_read_block( PIA *pi, char * buf, int count )
+
+{ int i, l, h;
+
+ switch (pi->mode) {
+
+ case 0: WR(4,0x40);
+ w0(0x40); t2(2);
+ for (i=0;i<count;i++) {
+ t2(4); l = r1();
+ t2(4); h = r1();
+ buf[i] = j44(l,h);
+ }
+ WR(4,0);
+ break;
+
+ case 1: WR(4,0x50);
+ w0(0x40); t2(2); t2(0x20);
+ for(i=0;i<count;i++) { t2(4); buf[i] = r0(); }
+ t2(1); t2(0x20);
+ WR(4,0x10);
+ break;
+
+ case 2: WR(4,0x48);
+ w0(0x40); w2(9); w2(0); w2(0x20);
+ for (i=0;i<count;i++) buf[i] = r4();
+ w2(0);
+ WR(4,8);
+ break;
+
+ case 3: WR(4,0x48);
+ w0(0x40); w2(9); w2(0); w2(0x20);
+ for (i=0;i<count/2;i++) ((u16 *)buf)[i] = r4w();
+ w2(0);
+ WR(4,8);
+ break;
+
+ case 4: WR(4,0x48);
+ w0(0x40); w2(9); w2(0); w2(0x20);
+ for (i=0;i<count/4;i++) ((u32 *)buf)[i] = r4l();
+ w2(0);
+ WR(4,8);
+ break;
+
+ }
+}
+
+static int bpck_probe_unit ( PIA *pi )
+
+{ int o1, o0, f7, id;
+ int t, s;
+
+ id = pi->unit;
+ s = 0;
+ w2(4); w2(0xe); r2(); t2(2);
+ o1 = r1()&0xf8;
+ o0 = r0();
+ w0(255-id); w2(4); w0(id);
+ t2(8); t2(8); t2(8);
+ t2(2); t = r1()&0xf8;
+ f7 = ((id % 8) == 7);
+ if ((f7) || (t != o1)) { t2(2); s = r1()&0xf8; }
+ if ((t == o1) && ((!f7) || (s == o1))) {
+ w2(0x4c); w0(o0);
+ return 0;
+ }
+ t2(8); w0(0); t2(2); w2(0x4c); w0(o0);
+ return 1;
+}
+
+static void bpck_connect ( PIA *pi )
+
+{ pi->saved_r0 = r0();
+ w0(0xff-pi->unit); w2(4); w0(pi->unit);
+ t2(8); t2(8); t2(8);
+ t2(2); t2(2);
+
+ switch (pi->mode) {
+
+ case 0: t2(8); WR(4,0);
+ break;
+
+ case 1: t2(8); WR(4,0x10);
+ break;
+
+ case 2:
+ case 3:
+ case 4: w2(0); WR(4,8);
+ break;
+
+ }
+
+ WR(5,8);
+
+ if (pi->devtype == PI_PCD) {
+ WR(0x46,0x10); /* fiddle with ESS logic ??? */
+ WR(0x4c,0x38);
+ WR(0x4d,0x88);
+ WR(0x46,0xa0);
+ WR(0x41,0);
+ WR(0x4e,8);
+ }
+}
+
+static void bpck_disconnect ( PIA *pi )
+
+{ w0(0);
+ if (pi->mode >= 2) { w2(9); w2(0); } else t2(2);
+ w2(0x4c); w0(pi->saved_r0);
+}
+
+static void bpck_force_spp ( PIA *pi )
+
+/* This fakes the EPP protocol to turn off EPP ... */
+
+{ pi->saved_r0 = r0();
+ w0(0xff-pi->unit); w2(4); w0(pi->unit);
+ t2(8); t2(8); t2(8);
+ t2(2); t2(2);
+
+ w2(0);
+ w0(4); w2(9); w2(0);
+ w0(0); w2(1); w2(3); w2(0);
+ w0(0); w2(9); w2(0);
+ w2(0x4c); w0(pi->saved_r0);
+}
+
+#define TEST_LEN 16
+
+static int bpck_test_proto( PIA *pi, char * scratch, int verbose )
+
+{ int i, e, l, h, om;
+ char buf[TEST_LEN];
+
+ bpck_force_spp(pi);
+
+ switch (pi->mode) {
+
+ case 0: bpck_connect(pi);
+ WR(0x13,0x7f);
+ w0(0x13); t2(2);
+ for(i=0;i<TEST_LEN;i++) {
+ t2(4); l = r1();
+ t2(4); h = r1();
+ buf[i] = j44(l,h);
+ }
+ bpck_disconnect(pi);
+ break;
+
+ case 1: bpck_connect(pi);
+ WR(0x13,0x7f);
+ w0(0x13); t2(2); t2(0x20);
+ for(i=0;i<TEST_LEN;i++) { t2(4); buf[i] = r0(); }
+ t2(1); t2(0x20);
+ bpck_disconnect(pi);
+ break;
+
+ case 2:
+ case 3:
+ case 4: om = pi->mode;
+ pi->mode = 0;
+ bpck_connect(pi);
+ WR(7,3);
+ WR(4,8);
+ bpck_disconnect(pi);
+
+ pi->mode = om;
+ bpck_connect(pi);
+ w0(0x13); w2(9); w2(1); w0(0); w2(3); w2(0); w2(0xe0);
+
+ switch (pi->mode) {
+ case 2: for (i=0;i<TEST_LEN;i++) buf[i] = r4();
+ break;
+ case 3: for (i=0;i<TEST_LEN/2;i++) ((u16 *)buf)[i] = r4w();
+ break;
+ case 4: for (i=0;i<TEST_LEN/4;i++) ((u32 *)buf)[i] = r4l();
+ break;
+ }
+
+ w2(0);
+ WR(7,0);
+ bpck_disconnect(pi);
+
+ break;
+
+ }
+
+ if (verbose) {
+ printk("%s: bpck: 0x%x unit %d mode %d: ",
+ pi->device,pi->port,pi->unit,pi->mode);
+ for (i=0;i<TEST_LEN;i++) printk("%3d",buf[i]);
+ printk("\n");
+ }
+
+ e = 0;
+ for (i=0;i<TEST_LEN;i++) if (buf[i] != (i+1)) e++;
+ return e;
+}
+
+static void bpck_read_eeprom ( PIA *pi, char * buf )
+
+{ int i,j,k,n,p,v,f, om;
+
+ bpck_force_spp(pi);
+
+ om = pi->mode;
+ pi->mode = 0;
+
+ bpck_connect(pi);
+
+ n = 0;
+ WR(4,0);
+ for (i=0;i<64;i++) {
+ WR(6,8);
+ WR(6,0xc);
+ p = 0x100;
+ for (k=0;k<9;k++) {
+ f = (((i + 0x180) & p) != 0) * 2;
+ WR(6,f+0xc);
+ WR(6,f+0xd);
+ WR(6,f+0xc);
+ p = (p >> 1);
+ }
+ for (j=0;j<2;j++) {
+ v = 0;
+ for (k=0;k<8;k++) {
+ WR(6,0xc);
+ WR(6,0xd);
+ WR(6,0xc);
+ f = RR(0);
+ v = 2*v + (f == 0x84);
+ }
+ buf[2*i+1-j] = v;
+ }
+ }
+ WR(6,8);
+ WR(6,0);
+ WR(5,8);
+
+ bpck_disconnect(pi);
+
+ if (om >= 2) {
+ bpck_connect(pi);
+ WR(7,3);
+ WR(4,8);
+ bpck_disconnect(pi);
+ }
+
+ pi->mode = om;
+}
+
+static int bpck_test_port ( PIA *pi ) /* check for 8-bit port */
+
+{ int i, r, m;
+
+ w2(0x2c); i = r0(); w0(255-i); r = r0(); w0(i);
+ m = -1;
+ if (r == i) m = 2;
+ if (r == (255-i)) m = 0;
+
+ w2(0xc); i = r0(); w0(255-i); r = r0(); w0(i);
+ if (r != (255-i)) m = -1;
+
+ if (m == 0) { w2(6); w2(0xc); r = r0(); w0(0xaa); w0(r); w0(0xaa); }
+ if (m == 2) { w2(0x26); w2(0xc); }
+
+ if (m == -1) return 0;
+ return 5;
+}
+
+static void bpck_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{ char *mode_string[5] = { "4-bit","8-bit","EPP-8",
+ "EPP-16","EPP-32" };
+
+#ifdef DUMP_EEPROM
+ int i;
+#endif
+
+ bpck_read_eeprom(pi,scratch);
+
+#ifdef DUMP_EEPROM
+ if (verbose) {
+ for(i=0;i<128;i++)
+ if ((scratch[i] < ' ') || (scratch[i] > '~'))
+ scratch[i] = '.';
+ printk("%s: bpck EEPROM: %64.64s\n",pi->device,scratch);
+ printk("%s: %64.64s\n",pi->device,&scratch[64]);
+ }
+#endif
+
+ printk("%s: bpck %s, backpack %8.8s unit %d",
+ pi->device,BPCK_VERSION,&scratch[110],pi->unit);
+ printk(" at 0x%x, mode %d (%s), delay %d\n",pi->port,
+ pi->mode,mode_string[pi->mode],pi->delay);
+}
+
+static void bpck_inc_use( void )
+
+{ MOD_INC_USE_COUNT;
+}
+
+static void bpck_dec_use( void )
+
+{ MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol bpck = { "bpck",0,5,2,4,256,
+ bpck_write_regr,
+ bpck_read_regr,
+ bpck_write_block,
+ bpck_read_block,
+ bpck_connect,
+ bpck_disconnect,
+ bpck_test_port,
+ bpck_probe_unit,
+ bpck_test_proto,
+ bpck_log_adapter,
+ bpck_inc_use,
+ bpck_dec_use
+ };
+
+#ifdef MODULE
+
+int init_module(void)
+
+{ return pi_register(&bpck) - 1;
+}
+
+void cleanup_module(void)
+
+{ pi_unregister(&bpck);
+}
+
+#endif
+
+/* end of bpck.c */
diff --git a/drivers/block/paride/comm.c b/drivers/block/paride/comm.c
new file mode 100644
index 000000000..794bbb9b4
--- /dev/null
+++ b/drivers/block/paride/comm.c
@@ -0,0 +1,222 @@
+/*
+ comm.c (c) 1997 Grant R. Guenther <grant@torque.net>
+ Under the terms of the GNU public license.
+
+ comm.c is a low-level protocol driver for some older models
+ of the DataStor "Commuter" parallel to IDE adapter. Some of
+ the parallel port devices marketed by Arista currently
+ use this adapter.
+*/
+
+#define COMM_VERSION "1.0"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "paride.h"
+
+/* mode codes: 0 nybble reads, 8-bit writes
+ 1 8-bit reads and writes
+ 2 8-bit EPP mode
+*/
+
+#define j44(a,b) (((a>>3)&0x0f)|((b<<1)&0xf0))
+
+#define P1 w2(5);w2(0xd);w2(0xd);w2(5);w2(4);
+#define P2 w2(5);w2(7);w2(7);w2(5);w2(4);
+
+/* cont = 0 - access the IDE register file
+ cont = 1 - access the IDE command set
+*/
+
+static int cont_map[2] = { 0x08, 0x10 };
+
+static int comm_read_regr( PIA *pi, int cont, int regr )
+
+{ int l, h, r;
+
+ r = regr + cont_map[cont];
+
+ switch (pi->mode) {
+
+ case 0: w0(r); P1; w0(0);
+ w2(6); l = r1(); w0(0x80); h = r1(); w2(4);
+ return j44(l,h);
+
+ case 1: w0(r+0x20); P1;
+ w0(0); w2(0x26); h = r0(); w2(4);
+ return h;
+
+ case 2:
+ case 3:
+ case 4: w3(r+0x20); r1();
+ w2(0x24); h = r4(); w2(4);
+ return h;
+
+ }
+ return -1;
+}
+
+static void comm_write_regr( PIA *pi, int cont, int regr, int val )
+
+{ int r;
+
+ r = regr + cont_map[cont];
+
+ switch (pi->mode) {
+
+ case 0:
+ case 1: w0(r); P1; w0(val); P2;
+ break;
+
+ case 2:
+ case 3:
+ case 4: w3(r); r1(); w4(val);
+ break;
+ }
+}
+
+static void comm_connect ( PIA *pi )
+
+{ pi->saved_r0 = r0();
+ pi->saved_r2 = r2();
+ w2(4); w0(0xff); w2(6);
+ w2(4); w0(0xaa); w2(6);
+ w2(4); w0(0x00); w2(6);
+ w2(4); w0(0x87); w2(6);
+ w2(4); w0(0xe0); w2(0xc); w2(0xc); w2(4);
+}
+
+static void comm_disconnect ( PIA *pi )
+
+{ w2(0); w2(0); w2(0); w2(4);
+ w0(pi->saved_r0);
+ w2(pi->saved_r2);
+}
+
+static void comm_read_block( PIA *pi, char * buf, int count )
+
+{ int i, l, h;
+
+ switch (pi->mode) {
+
+ case 0: w0(0x48); P1;
+ for(i=0;i<count;i++) {
+ w0(0); w2(6); l = r1();
+ w0(0x80); h = r1(); w2(4);
+ buf[i] = j44(l,h);
+ }
+ break;
+
+ case 1: w0(0x68); P1; w0(0);
+ for(i=0;i<count;i++) {
+ w2(0x26); buf[i] = r0(); w2(0x24);
+ }
+ w2(4);
+ break;
+
+ case 2: w3(0x68); r1(); w2(0x24);
+ for (i=0;i<count;i++) buf[i] = r4();
+ w2(4);
+ break;
+
+ case 3: w3(0x68); r1(); w2(0x24);
+ for (i=0;i<count/2;i++) ((u16 *)buf)[i] = r4w();
+ w2(4);
+ break;
+
+ case 4: w3(0x68); r1(); w2(0x24);
+ for (i=0;i<count/4;i++) ((u32 *)buf)[i] = r4l();
+ w2(4);
+ break;
+
+ }
+}
+
+/* NB: Watch out for the byte swapped writes ! */
+
+static void comm_write_block( PIA *pi, char * buf, int count )
+
+{ int k;
+
+ switch (pi->mode) {
+
+ case 0:
+ case 1: w0(0x68); P1;
+ for (k=0;k<count;k++) {
+ w2(5); w0(buf[k^1]); w2(7);
+ }
+ w2(5); w2(4);
+ break;
+
+ case 2: w3(0x48); r1();
+ for (k=0;k<count;k++) w4(buf[k^1]);
+ break;
+
+ case 3: w3(0x48); r1();
+ for (k=0;k<count/2;k++) w4w(pi_swab16(buf,k));
+ break;
+
+ case 4: w3(0x48); r1();
+ for (k=0;k<count/4;k++) w4l(pi_swab32(buf,k));
+ break;
+
+
+ }
+}
+
+static void comm_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{ char *mode_string[5] = {"4-bit","8-bit","EPP-8","EPP-16","EPP-32"};
+
+ printk("%s: comm %s, DataStor Commuter at 0x%x, ",
+ pi->device,COMM_VERSION,pi->port);
+ printk("mode %d (%s), delay %d\n",pi->mode,
+ mode_string[pi->mode],pi->delay);
+
+}
+
+static void comm_inc_use ( void )
+
+{ MOD_INC_USE_COUNT;
+}
+
+static void comm_dec_use ( void )
+
+{ MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol comm = {"comm",0,5,2,1,1,
+ comm_write_regr,
+ comm_read_regr,
+ comm_write_block,
+ comm_read_block,
+ comm_connect,
+ comm_disconnect,
+ 0,
+ 0,
+ 0,
+ comm_log_adapter,
+ comm_inc_use,
+ comm_dec_use
+ };
+
+
+#ifdef MODULE
+
+int init_module(void)
+
+{ return pi_register( &comm ) - 1;
+}
+
+void cleanup_module(void)
+
+{ pi_unregister( &comm );
+}
+
+#endif
+
+/* end of comm.c */
diff --git a/drivers/block/paride/dstr.c b/drivers/block/paride/dstr.c
new file mode 100644
index 000000000..53cedcc2c
--- /dev/null
+++ b/drivers/block/paride/dstr.c
@@ -0,0 +1,237 @@
+/*
+ dstr.c (c) 1997 Grant R. Guenther <grant@torque.net>
+ Under the terms of the GNU public license.
+
+ dstr.c is a low-level protocol driver for the
+ DataStor EP2000 parallel to IDE adapter chip.
+
+*/
+
+#define DSTR_VERSION "1.0"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "paride.h"
+
+/* mode codes: 0 nybble reads, 8-bit writes
+ 1 8-bit reads and writes
+ 2 8-bit EPP mode
+ 3 EPP-16
+ 4 EPP-32
+*/
+
+#define j44(a,b) (((a>>3)&0x07)|((~a>>4)&0x08)|((b<<1)&0x70)|((~b)&0x80))
+
+#define P1 w2(5);w2(0xd);w2(5);w2(4);
+#define P2 w2(5);w2(7);w2(5);w2(4);
+#define P3 w2(6);w2(4);w2(6);w2(4);
+
+/* cont = 0 - access the IDE register file
+ cont = 1 - access the IDE command set
+*/
+
+static int cont_map[2] = { 0x20, 0x40 };
+
+static int dstr_read_regr( PIA *pi, int cont, int regr )
+
+{ int a, b, r;
+
+ r = regr + cont_map[cont];
+
+ w0(0x81); P1;
+ if (pi->mode) { w0(0x11); } else { w0(1); }
+ P2; w0(r); P1;
+
+ switch (pi->mode) {
+
+ case 0: w2(6); a = r1(); w2(4); w2(6); b = r1(); w2(4);
+ return j44(a,b);
+
+ case 1: w0(0); w2(0x26); a = r0(); w2(4);
+ return a;
+
+ case 2:
+ case 3:
+ case 4: w2(0x24); a = r4(); w2(4);
+ return a;
+
+ }
+ return -1;
+}
+
+static void dstr_write_regr( PIA *pi, int cont, int regr, int val )
+
+{ int r;
+
+ r = regr + cont_map[cont];
+
+ w0(0x81); P1;
+ if (pi->mode >= 2) { w0(0x11); } else { w0(1); }
+ P2; w0(r); P1;
+
+ switch (pi->mode) {
+
+ case 0:
+ case 1: w0(val); w2(5); w2(7); w2(5); w2(4);
+ break;
+
+ case 2:
+ case 3:
+ case 4: w4(val);
+ break;
+ }
+}
+
+#define CCP(x) w0(0xff);w2(0xc);w2(4);\
+ w0(0xaa);w0(0x55);w0(0);w0(0xff);w0(0x87);w0(0x78);\
+ w0(x);w2(5);w2(4);
+
+static void dstr_connect ( PIA *pi )
+
+{ pi->saved_r0 = r0();
+ pi->saved_r2 = r2();
+ w2(4); CCP(0xe0); w0(0xff);
+}
+
+static void dstr_disconnect ( PIA *pi )
+
+{ CCP(0x30);
+ w0(pi->saved_r0);
+ w2(pi->saved_r2);
+}
+
+static void dstr_read_block( PIA *pi, char * buf, int count )
+
+{ int k, a, b;
+
+ w0(0x81); P1;
+ if (pi->mode) { w0(0x19); } else { w0(9); }
+ P2; w0(0x82); P1; P3; w0(0x20); P1;
+
+ switch (pi->mode) {
+
+ case 0: for (k=0;k<count;k++) {
+ w2(6); a = r1(); w2(4);
+ w2(6); b = r1(); w2(4);
+ buf[k] = j44(a,b);
+ }
+ break;
+
+ case 1: w0(0);
+ for (k=0;k<count;k++) {
+ w2(0x26); buf[k] = r0(); w2(0x24);
+ }
+ w2(4);
+ break;
+
+ case 2: w2(0x24);
+ for (k=0;k<count;k++) buf[k] = r4();
+ w2(4);
+ break;
+
+ case 3: w2(0x24);
+ for (k=0;k<count/2;k++) ((u16 *)buf)[k] = r4w();
+ w2(4);
+ break;
+
+ case 4: w2(0x24);
+ for (k=0;k<count/4;k++) ((u32 *)buf)[k] = r4l();
+ w2(4);
+ break;
+
+ }
+}
+
+static void dstr_write_block( PIA *pi, char * buf, int count )
+
+{ int k;
+
+ w0(0x81); P1;
+ if (pi->mode) { w0(0x19); } else { w0(9); }
+ P2; w0(0x82); P1; P3; w0(0x20); P1;
+
+ switch (pi->mode) {
+
+ case 0:
+ case 1: for (k=0;k<count;k++) {
+ w2(5); w0(buf[k]); w2(7);
+ }
+ w2(5); w2(4);
+ break;
+
+ case 2: w2(0xc5);
+ for (k=0;k<count;k++) w4(buf[k]);
+ w2(0xc4);
+ break;
+
+ case 3: w2(0xc5);
+ for (k=0;k<count/2;k++) w4w(((u16 *)buf)[k]);
+ w2(0xc4);
+ break;
+
+ case 4: w2(0xc5);
+ for (k=0;k<count/4;k++) w4l(((u32 *)buf)[k]);
+ w2(0xc4);
+ break;
+
+ }
+}
+
+
+static void dstr_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{ char *mode_string[5] = {"4-bit","8-bit","EPP-8",
+ "EPP-16","EPP-32"};
+
+ printk("%s: dstr %s, DataStor EP2000 at 0x%x, ",
+ pi->device,DSTR_VERSION,pi->port);
+ printk("mode %d (%s), delay %d\n",pi->mode,
+ mode_string[pi->mode],pi->delay);
+
+}
+
+static void dstr_inc_use ( void )
+
+{ MOD_INC_USE_COUNT;
+}
+
+static void dstr_dec_use ( void )
+
+{ MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol dstr = {"dstr",0,5,2,1,1,
+ dstr_write_regr,
+ dstr_read_regr,
+ dstr_write_block,
+ dstr_read_block,
+ dstr_connect,
+ dstr_disconnect,
+ 0,
+ 0,
+ 0,
+ dstr_log_adapter,
+ dstr_inc_use,
+ dstr_dec_use
+ };
+
+
+#ifdef MODULE
+
+int init_module(void)
+
+{ return pi_register( &dstr ) - 1;
+}
+
+void cleanup_module(void)
+
+{ pi_unregister( &dstr );
+}
+
+#endif
+
+/* end of dstr.c */
diff --git a/drivers/block/paride/epat.c b/drivers/block/paride/epat.c
new file mode 100644
index 000000000..f6c19c994
--- /dev/null
+++ b/drivers/block/paride/epat.c
@@ -0,0 +1,315 @@
+/*
+ epat.c (c) 1997 Grant R. Guenther <grant@torque.net>
+ Under the terms of the GNU public license.
+
+ This is the low level protocol driver for the EPAT parallel
+ to IDE adapter from Shuttle Technologies. This adapter is
+ used in many popular parallel port disk products such as the
+ SyQuest EZ drives, the Avatar Shark and the Imation SuperDisk.
+
+*/
+
+#define EPAT_VERSION "1.0"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "paride.h"
+
+#define j44(a,b) (((a>>4)&0x0f)+(b&0xf0))
+#define j53(a,b) (((a>>3)&0x1f)+((b<<4)&0xe0))
+
+/* cont = 0 IDE register file
+ cont = 1 IDE control registers
+ cont = 2 internal EPAT registers
+*/
+
+static int cont_map[3] = { 0x18, 0x10, 0 };
+
+static void epat_write_regr( PIA *pi, int cont, int regr, int val)
+
+{ int r;
+
+ r = regr + cont_map[cont];
+
+ switch (pi->mode) {
+
+ case 0:
+ case 1:
+ case 2: w0(0x60+r); w2(1); w0(val); w2(4);
+ break;
+
+ case 3:
+ case 4:
+ case 5: w3(0x40+r); w4(val);
+ break;
+
+ }
+}
+
+static int epat_read_regr( PIA *pi, int cont, int regr )
+
+{ int a, b, r;
+
+ r = regr + cont_map[cont];
+
+ switch (pi->mode) {
+
+ case 0: w0(r); w2(1); w2(3);
+ a = r1(); w2(4); b = r1();
+ return j44(a,b);
+
+ case 1: w0(0x40+r); w2(1); w2(4);
+ a = r1(); b = r2(); w0(0xff);
+ return j53(a,b);
+
+ case 2: w0(0x20+r); w2(1); w2(0x25);
+ a = r0(); w2(4);
+ return a;
+
+ case 3:
+ case 4:
+ case 5: w3(r); w2(0x24); a = r4(); w2(4);
+ return a;
+
+ }
+ return -1; /* never gets here */
+}
+
+static void epat_read_block( PIA *pi, char * buf, int count )
+
+{ int k, ph, a, b;
+
+ switch (pi->mode) {
+
+ case 0: w0(7); w2(1); w2(3); w0(0xff);
+ ph = 0;
+ for(k=0;k<count;k++) {
+ if (k == count-1) w0(0xfd);
+ w2(6+ph); a = r1();
+ if (a & 8) b = a;
+ else { w2(4+ph); b = r1(); }
+ buf[k] = j44(a,b);
+ ph = 1 - ph;
+ }
+ w0(0); w2(4);
+ break;
+
+ case 1: w0(0x47); w2(1); w2(5); w0(0xff);
+ ph = 0;
+ for(k=0;k<count;k++) {
+ if (k == count-1) w0(0xfd);
+ w2(4+ph);
+ a = r1(); b = r2();
+ buf[k] = j53(a,b);
+ ph = 1 - ph;
+ }
+ w0(0); w2(4);
+ break;
+
+ case 2: w0(0x27); w2(1); w2(0x25); w0(0);
+ ph = 0;
+ for(k=0;k<count-1;k++) {
+ w2(0x24+ph);
+ buf[k] = r0();
+ ph = 1 - ph;
+ }
+ w2(0x26); w2(0x27); buf[count-1] = r0();
+ w2(0x25); w2(4);
+ break;
+
+ case 3: w3(0x80); w2(0x24);
+ for(k=0;k<count-1;k++) buf[k] = r4();
+ w2(4); w3(0xa0); w2(0x24); buf[count-1] = r4();
+ w2(4);
+ break;
+
+ case 4: w3(0x80); w2(0x24);
+ for(k=0;k<(count/2)-1;k++) ((u16 *)buf)[k] = r4w();
+ buf[count-2] = r4();
+ w2(4); w3(0xa0); w2(0x24); buf[count-1] = r4();
+ w2(4);
+ break;
+
+ case 5: w3(0x80); w2(0x24);
+ for(k=0;k<(count/4)-1;k++) ((u32 *)buf)[k] = r4l();
+ for(k=count-4;k<count-1;k++) buf[k] = r4();
+ w2(4); w3(0xa0); w2(0x24); buf[count-1] = r4();
+ w2(4);
+ break;
+
+ }
+}
+
+static void epat_write_block( PIA *pi, char * buf, int count )
+
+{ int ph, k;
+
+ switch (pi->mode) {
+
+ case 0:
+ case 1:
+ case 2: w0(0x67); w2(1); w2(5);
+ ph = 0;
+ for(k=0;k<count;k++) {
+ w0(buf[k]);
+ w2(4+ph);
+ ph = 1 - ph;
+ }
+ w2(7); w2(4);
+ break;
+
+ case 3: w3(0xc0);
+ for(k=0;k<count;k++) w4(buf[k]);
+ w2(4);
+ break;
+
+ case 4: w3(0xc0);
+ for(k=0;k<(count/2);k++) w4w(((u16 *)buf)[k]);
+ w2(4);
+ break;
+
+ case 5: w3(0xc0);
+ for(k=0;k<(count/4);k++) w4l(((u32 *)buf)[k]);
+ w2(4);
+ break;
+
+ }
+}
+
+/* these macros access the EPAT registers in native addressing */
+
+#define WR(r,v) epat_write_regr(pi,2,r,v)
+#define RR(r) (epat_read_regr(pi,2,r))
+
+/* and these access the IDE task file */
+
+#define WRi(r,v) epat_write_regr(pi,0,r,v)
+#define RRi(r) (epat_read_regr(pi,0,r))
+
+/* FIXME: the CCP stuff should be fixed to handle multiple EPATs on a chain */
+
+#define CCP(x) w2(4);w0(0x22);w0(0xaa);w0(0x55);w0(0);w0(0xff);\
+ w0(0x87);w0(0x78);w0(x);w2(4);w2(5);w2(4);w0(0xff);
+
+static void epat_connect ( PIA *pi )
+
+{ pi->saved_r0 = r0();
+ pi->saved_r2 = r2();
+ CCP(0); CCP(0xe0);
+ w0(0); w2(1); w2(4);
+ if (pi->mode >= 3) {
+ w0(0); w2(1); w2(4); w2(0xc);
+ w0(0x40); w2(6); w2(7); w2(4); w2(0xc); w2(4);
+ }
+ WR(8,0x10); WR(0xc,0x14); WR(0xa,0x38); WR(0x12,0x10);
+}
+
+static void epat_disconnect ( PIA *pi )
+
+{ CCP(0x30);
+ w0(pi->saved_r0);
+ w2(pi->saved_r2);
+}
+
+static int epat_test_proto( PIA *pi, char * scratch, int verbose )
+
+{ int k, j, f, cc;
+ int e[2] = {0,0};
+
+ epat_connect(pi);
+ cc = RR(0xd);
+ epat_disconnect(pi);
+
+ epat_connect(pi);
+ for (j=0;j<2;j++) {
+ WRi(6,0xa0+j*0x10);
+ for (k=0;k<256;k++) {
+ WRi(2,k^0xaa);
+ WRi(3,k^0x55);
+ if (RRi(2) != (k^0xaa)) e[j]++;
+ }
+ }
+ epat_disconnect(pi);
+
+ f = 0;
+ epat_connect(pi);
+ WR(0x13,1); WR(0x13,0); WR(0xa,0x11);
+ epat_read_block(pi,scratch,512);
+
+ for (k=0;k<256;k++) {
+ if ((scratch[2*k] & 0xff) != k) f++;
+ if ((scratch[2*k+1] & 0xff) != (0xff-k)) f++;
+ }
+ epat_disconnect(pi);
+
+ if (verbose) {
+ printk("%s: epat: port 0x%x, mode %d, ccr %x, test=(%d,%d,%d)\n",
+ pi->device,pi->port,pi->mode,cc,e[0],e[1],f);
+ }
+
+ return (e[0] && e[1]) || f;
+}
+
+static void epat_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{ int ver;
+ char *mode_string[6] =
+ {"4-bit","5/3","8-bit","EPP-8","EPP-16","EPP-32"};
+
+ epat_connect(pi);
+ WR(0xa,0x38); /* read the version code */
+ ver = RR(0xb);
+ epat_disconnect(pi);
+
+ printk("%s: epat %s, Shuttle EPAT chip %x at 0x%x, ",
+ pi->device,EPAT_VERSION,ver,pi->port);
+ printk("mode %d (%s), delay %d\n",pi->mode,
+ mode_string[pi->mode],pi->delay);
+
+}
+
+static void epat_inc_use ( void )
+
+{ MOD_INC_USE_COUNT;
+}
+
+static void epat_dec_use ( void )
+
+{ MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol epat = {"epat",0,6,3,1,1,
+ epat_write_regr,
+ epat_read_regr,
+ epat_write_block,
+ epat_read_block,
+ epat_connect,
+ epat_disconnect,
+ 0,
+ 0,
+ epat_test_proto,
+ epat_log_adapter,
+ epat_inc_use,
+ epat_dec_use
+ };
+
+
+#ifdef MODULE
+
+int init_module(void)
+
+{ return pi_register( &epat) - 1;
+}
+
+void cleanup_module(void)
+
+{ pi_unregister( &epat);
+}
+
+#endif
+
+/* end of epat.c */
diff --git a/drivers/block/paride/epia.c b/drivers/block/paride/epia.c
new file mode 100644
index 000000000..73d83fe8d
--- /dev/null
+++ b/drivers/block/paride/epia.c
@@ -0,0 +1,318 @@
+/*
+ epia.c (c) 1997 Grant R. Guenther <grant@torque.net>
+ Under the terms of the GNU public license.
+
+ epia.c is a low-level protocol driver for Shuttle Technologies
+ EPIA parallel to IDE adapter chip. This device is now obsolete
+ and has been replaced with the EPAT chip, which is supported
+ by epat.c, however, some devices based on EPIA are still
+ available.
+
+*/
+
+#define EPIA_VERSION "1.0"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "paride.h"
+
+/* mode codes: 0 nybble reads on port 1, 8-bit writes
+ 1 5/3 reads on ports 1 & 2, 8-bit writes
+ 2 8-bit reads and writes
+ 3 8-bit EPP mode
+ 4 16-bit EPP
+ 5 32-bit EPP
+*/
+
+#define j44(a,b) (((a>>4)&0x0f)+(b&0xf0))
+#define j53(a,b) (((a>>3)&0x1f)+((b<<4)&0xe0))
+
+/* cont = 0 IDE register file
+ cont = 1 IDE control registers
+*/
+
+static int cont_map[2] = { 0, 0x80 };
+
+static int epia_read_regr( PIA *pi, int cont, int regr )
+
+{ int a, b, r;
+
+ regr += cont_map[cont];
+
+ switch (pi->mode) {
+
+ case 0: r = regr^0x39;
+ w0(r); w2(1); w2(3); w0(r);
+ a = r1(); w2(1); b = r1(); w2(4);
+ return j44(a,b);
+
+ case 1: r = regr^0x31;
+ w0(r); w2(1); w0(r&0x37);
+ w2(3); w2(5); w0(r|0xf0);
+ a = r1(); b = r2(); w2(4);
+ return j53(a,b);
+
+ case 2: r = regr^0x29;
+ w0(r); w2(1); w2(0X21); w2(0x23);
+ a = r0(); w2(4);
+ return a;
+
+ case 3:
+ case 4:
+ case 5: w3(regr); w2(0x24); a = r4(); w2(4);
+ return a;
+
+ }
+ return -1;
+}
+
+static void epia_write_regr( PIA *pi, int cont, int regr, int val)
+
+{ int r;
+
+ regr += cont_map[cont];
+
+ switch (pi->mode) {
+
+ case 0:
+ case 1:
+ case 2: r = regr^0x19;
+ w0(r); w2(1); w0(val); w2(3); w2(4);
+ break;
+
+ case 3:
+ case 4:
+ case 5: r = regr^0x40;
+ w3(r); w4(val); w2(4);
+ break;
+ }
+}
+
+#define WR(r,v) epia_write_regr(pi,0,r,v)
+#define RR(r) (epia_read_regr(pi,0,r))
+
+/* The use of register 0x84 is entirely unclear - it seems to control
+ some EPP counters ... currently we know about 3 different block
+ sizes: the standard 512 byte reads and writes, 12 byte writes and
+ 2048 byte reads (the last two being used in the CDrom drivers.
+*/
+
+static void epia_connect ( PIA *pi )
+
+{ pi->saved_r0 = r0();
+ pi->saved_r2 = r2();
+
+ w2(4); w0(0xa0); w0(0x50); w0(0xc0); w0(0x30); w0(0xa0); w0(0);
+ w2(1); w2(4);
+ if (pi->mode >= 3) {
+ w0(0xa); w2(1); w2(4); w0(0x82); w2(4); w2(0xc); w2(4);
+ w2(0x24); w2(0x26); w2(4);
+ }
+ WR(0x86,8);
+}
+
+static void epia_disconnect ( PIA *pi )
+
+{ WR(0x84,0x10);
+ w0(pi->saved_r0);
+ w2(1); w2(4);
+ w0(pi->saved_r0);
+ w2(pi->saved_r2);
+}
+
+static void epia_read_block( PIA *pi, char * buf, int count )
+
+{ int k, ph, a, b;
+
+ switch (pi->mode) {
+
+ case 0: w0(0x81); w2(1); w2(3); w0(0xc1);
+ ph = 1;
+ for (k=0;k<count;k++) {
+ w2(2+ph); a = r1();
+ w2(4+ph); b = r1();
+ buf[k] = j44(a,b);
+ ph = 1 - ph;
+ }
+ w0(0); w2(4);
+ break;
+
+ case 1: w0(0x91); w2(1); w0(0x10); w2(3);
+ w0(0x51); w2(5); w0(0xd1);
+ ph = 1;
+ for (k=0;k<count;k++) {
+ w2(4+ph);
+ a = r1(); b = r2();
+ buf[k] = j53(a,b);
+ ph = 1 - ph;
+ }
+ w0(0); w2(4);
+ break;
+
+ case 2: w0(0x89); w2(1); w2(0x23); w2(0x21);
+ ph = 1;
+ for (k=0;k<count;k++) {
+ w2(0x24+ph);
+ buf[k] = r0();
+ ph = 1 - ph;
+ }
+ w2(6); w2(4);
+ break;
+
+ case 3: if (count > 512) WR(0x84,3);
+ w3(0); w2(0x24);
+ for (k=0;k<count;k++) buf[k] = r4();
+ w2(4); WR(0x84,0);
+ break;
+
+ case 4: if (count > 512) WR(0x84,3);
+ w3(0); w2(0x24);
+ for (k=0;k<count/2;k++) ((u16 *)buf)[k] = r4w();
+ w2(4); WR(0x84,0);
+ break;
+
+ case 5: if (count > 512) WR(0x84,3);
+ w3(0); w2(0x24);
+ for (k=0;k<count/4;k++) ((u32 *)buf)[k] = r4l();
+ w2(4); WR(0x84,0);
+ break;
+
+ }
+}
+
+static void epia_write_block( PIA *pi, char * buf, int count )
+
+{ int ph, k, last, d;
+
+ switch (pi->mode) {
+
+ case 0:
+ case 1:
+ case 2: w0(0xa1); w2(1); w2(3); w2(1); w2(5);
+ ph = 0; last = 0x8000;
+ for (k=0;k<count;k++) {
+ d = buf[k];
+ if (d != last) { last = d; w0(d); }
+ w2(4+ph);
+ ph = 1 - ph;
+ }
+ w2(7); w2(4);
+ break;
+
+ case 3: if (count < 512) WR(0x84,1);
+ w3(0x40);
+ for (k=0;k<count;k++) w4(buf[k]);
+ if (count < 512) WR(0x84,0);
+ break;
+
+ case 4: if (count < 512) WR(0x84,1);
+ w3(0x40);
+ for (k=0;k<count/2;k++) w4w(((u16 *)buf)[k]);
+ if (count < 512) WR(0x84,0);
+ break;
+
+ case 5: if (count < 512) WR(0x84,1);
+ w3(0x40);
+ for (k=0;k<count/4;k++) w4l(((u32 *)buf)[k]);
+ if (count < 512) WR(0x84,0);
+ break;
+
+ }
+
+}
+
+static int epia_test_proto( PIA *pi, char * scratch, int verbose )
+
+{ int j, k, f;
+ int e[2] = {0,0};
+
+ epia_connect(pi);
+ for (j=0;j<2;j++) {
+ WR(6,0xa0+j*0x10);
+ for (k=0;k<256;k++) {
+ WR(2,k^0xaa);
+ WR(3,k^0x55);
+ if (RR(2) != (k^0xaa)) e[j]++;
+ }
+ }
+ epia_disconnect(pi);
+
+ f = 0;
+ epia_connect(pi);
+ WR(0x84,8);
+ epia_read_block(pi,scratch,512);
+ for (k=0;k<256;k++) {
+ if ((scratch[2*k] & 0xff) != ((k+1) & 0xff)) f++;
+ if ((scratch[2*k+1] & 0xff) != ((-2-k) & 0xff)) f++;
+ }
+ WR(0x84,0);
+ epia_disconnect(pi);
+
+ if (verbose) {
+ printk("%s: epia: port 0x%x, mode %d, test=(%d,%d,%d)\n",
+ pi->device,pi->port,pi->mode,e[0],e[1],f);
+ }
+
+ return (e[0] && e[1]) || f;
+
+}
+
+
+static void epia_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{ char *mode_string[6] = {"4-bit","5/3","8-bit",
+ "EPP-8","EPP-16","EPP-32"};
+
+ printk("%s: epia %s, Shuttle EPIA at 0x%x, ",
+ pi->device,EPIA_VERSION,pi->port);
+ printk("mode %d (%s), delay %d\n",pi->mode,
+ mode_string[pi->mode],pi->delay);
+
+}
+
+static void epia_inc_use ( void )
+
+{ MOD_INC_USE_COUNT;
+}
+
+static void epia_dec_use ( void )
+
+{ MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol epia = {"epia",0,6,3,1,1,
+ epia_write_regr,
+ epia_read_regr,
+ epia_write_block,
+ epia_read_block,
+ epia_connect,
+ epia_disconnect,
+ 0,
+ 0,
+ epia_test_proto,
+ epia_log_adapter,
+ epia_inc_use,
+ epia_dec_use
+ };
+
+
+#ifdef MODULE
+
+int init_module(void)
+
+{ return pi_register( &epia ) - 1;
+}
+
+void cleanup_module(void)
+
+{ pi_unregister( &epia );
+}
+
+#endif
+
+/* end of epia.c */
+
diff --git a/drivers/block/paride/frpw.c b/drivers/block/paride/frpw.c
new file mode 100644
index 000000000..fe7b1452f
--- /dev/null
+++ b/drivers/block/paride/frpw.c
@@ -0,0 +1,256 @@
+/*
+ frpw.c (c) 1997 Grant R. Guenther <grant@torque.net>
+ Under the terms of the GNU public license
+
+ frpw.c is a low-level protocol driver for the Freecom "Power"
+ parallel port IDE adapter.
+
+*/
+
+#define FRPW_VERSION "1.0"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "paride.h"
+
+#define cec4 w2(0xc);w2(0xe);w2(0xe);w2(0xc);w2(4);w2(4);w2(4);
+#define j44(l,h) (((l>>4)&0x0f)|(h&0xf0))
+
+/* cont = 0 - access the IDE register file
+ cont = 1 - access the IDE command set
+*/
+
+static int cont_map[2] = { 0x08, 0x10 };
+
+static int frpw_read_regr( PIA *pi, int cont, int regr )
+
+{ int h,l,r;
+
+ r = regr + cont_map[cont];
+
+ w2(4);
+ w0(r); cec4;
+ w2(6); l = r1();
+ w2(4); h = r1();
+ w2(4);
+
+ return j44(l,h);
+
+}
+
+static void frpw_write_regr( PIA *pi, int cont, int regr, int val)
+
+{ int r;
+
+ r = regr + cont_map[cont];
+
+ w2(4); w0(r); cec4;
+ w0(val);
+ w2(5);w2(7);w2(5);w2(4);
+}
+
+static void frpw_read_block_int( PIA *pi, char * buf, int count, int regr )
+
+{ int h, l, k, ph;
+
+ switch(pi->mode) {
+
+ case 0: w2(4); w0(regr); cec4;
+ for (k=0;k<count;k++) {
+ w2(6); l = r1();
+ w2(4); h = r1();
+ buf[k] = j44(l,h);
+ }
+ w2(4);
+ break;
+
+ case 1: ph = 2;
+ w2(4); w0(regr + 0xc0); cec4;
+ w0(0xff);
+ for (k=0;k<count;k++) {
+ w2(0xa4 + ph);
+ buf[k] = r0();
+ ph = 2 - ph;
+ }
+ w2(0xac); w2(0xa4); w2(4);
+ break;
+
+ case 2: w2(4); w0(regr + 0x80); cec4;
+ for (k=0;k<count;k++) buf[k] = r4();
+ w2(0xac); w2(0xa4);
+ w2(4);
+ break;
+
+ case 3: w2(4); w0(regr + 0x80); cec4;
+ for (k=0;k<count-2;k++) buf[k] = r4();
+ w2(0xac); w2(0xa4);
+ buf[count-2] = r4();
+ buf[count-1] = r4();
+ w2(4);
+ break;
+
+ }
+}
+
+static void frpw_read_block( PIA *pi, char * buf, int count)
+
+{ frpw_read_block_int(pi,buf,count,0x08);
+}
+
+static void frpw_write_block( PIA *pi, char * buf, int count )
+
+{ int k;
+
+ switch(pi->mode) {
+
+ case 0:
+ case 1:
+ case 2: w2(4); w0(8); cec4; w2(5);
+ for (k=0;k<count;k++) {
+ w0(buf[k]);
+ w2(7);w2(5);
+ }
+ w2(4);
+ break;
+
+ case 3: w2(4); w0(0xc8); cec4; w2(5);
+ for (k=0;k<count;k++) w4(buf[k]);
+ w2(4);
+ break;
+ }
+}
+
+static void frpw_connect ( PIA *pi )
+
+{ pi->saved_r0 = r0();
+ pi->saved_r2 = r2();
+ w2(4);
+}
+
+static void frpw_disconnect ( PIA *pi )
+
+{ w2(4); w0(0x20); cec4;
+ w0(pi->saved_r0);
+ w2(pi->saved_r2);
+}
+
+/* Stub logic to see if PNP string is available - used to distinguish
+ between the Xilinx and ASIC implementations of the Freecom adapter.
+*/
+
+static int frpw_test_pnp ( PIA *pi )
+
+{ int olddelay, a, b;
+
+ olddelay = pi->delay;
+ pi->delay = 10;
+
+ pi->saved_r0 = r0();
+ pi->saved_r2 = r2();
+
+ w2(4); w0(4); w2(6); w2(7);
+ a = r1() & 0xff; w2(4); b = r1() & 0xff;
+ w2(0xc); w2(0xe); w2(4);
+
+ pi->delay = olddelay;
+ w0(pi->saved_r0);
+ w2(pi->saved_r2);
+
+ return ((~a&0x40) && (b&0x40));
+}
+
+/* We use pi->private to record the chip type:
+ 0 = untested, 2 = Xilinx, 3 = ASIC
+*/
+
+static int frpw_test_proto( PIA *pi, char * scratch, int verbose )
+
+{ int k, r;
+
+ if (!pi->private) pi->private = frpw_test_pnp(pi) + 2;
+
+ if ((pi->private == 2) && (pi->mode > 2)) {
+ if (verbose)
+ printk("%s: frpw: Xilinx does not support mode %d\n",
+ pi->device, pi->mode);
+ return 1;
+ }
+
+ if ((pi->private == 3) && (pi->mode == 2)) {
+ if (verbose)
+ printk("%s: frpw: ASIC does not support mode 2\n",
+ pi->device);
+ return 1;
+ }
+
+ frpw_connect(pi);
+ frpw_read_block_int(pi,scratch,512,0x10);
+ r = 0;
+ for (k=0;k<128;k++) if (scratch[k] != k) r++;
+ frpw_disconnect(pi);
+
+ if (verbose) {
+ printk("%s: frpw: port 0x%x, mode %d, test=%d\n",
+ pi->device,pi->port,pi->mode,r);
+ }
+
+ return r;
+}
+
+
+static void frpw_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{ char *mode_string[4] = {"4-bit","8-bit","EPP-X","EPP-A"};
+
+ printk("%s: frpw %s, Freecom (%s) adapter at 0x%x, ", pi->device,
+ FRPW_VERSION,(pi->private == 2)?"Xilinx":"ASIC",pi->port);
+ printk("mode %d (%s), delay %d\n",pi->mode,
+ mode_string[pi->mode],pi->delay);
+
+}
+
+static void frpw_inc_use ( void )
+
+{ MOD_INC_USE_COUNT;
+}
+
+static void frpw_dec_use ( void )
+
+{ MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol frpw = {"frpw",0,4,2,2,1,
+ frpw_write_regr,
+ frpw_read_regr,
+ frpw_write_block,
+ frpw_read_block,
+ frpw_connect,
+ frpw_disconnect,
+ 0,
+ 0,
+ frpw_test_proto,
+ frpw_log_adapter,
+ frpw_inc_use,
+ frpw_dec_use
+ };
+
+
+#ifdef MODULE
+
+int init_module(void)
+
+{ return pi_register( &frpw ) - 1;
+}
+
+void cleanup_module(void)
+
+{ pi_unregister( &frpw );
+}
+
+#endif
+
+/* end of frpw.c */
diff --git a/drivers/block/paride/kbic.c b/drivers/block/paride/kbic.c
new file mode 100644
index 000000000..47f5cd29f
--- /dev/null
+++ b/drivers/block/paride/kbic.c
@@ -0,0 +1,305 @@
+/*
+ kbic.c (c) 1997 Grant R. Guenther <grant@torque.net>
+ Under the terms of the GNU public license.
+
+ This is a low-level driver for the KBIC-951A and KBIC-971A
+ parallel to IDE adapter chips from KingByte Information Systems.
+
+ The chips are almost identical, however, the wakeup code
+ required for the 971A interferes with the correct operation of
+ the 951A, so this driver registers itself twice, once for
+ each chip.
+
+*/
+
+#define KBIC_VERSION "1.0"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "paride.h"
+
+#define r12w() (delay_p,inw(pi->port+1)&0xffff)
+
+#define j44(a,b) ((((a>>4)&0x0f)|(b&0xf0))^0x88)
+#define j53(w) (((w>>3)&0x1f)|((w>>4)&0xe0))
+
+
+/* cont = 0 - access the IDE register file
+ cont = 1 - access the IDE command set
+*/
+
+static int cont_map[2] = { 0x80, 0x40 };
+
+static int kbic_read_regr( PIA *pi, int cont, int regr )
+
+{ int a, b, s;
+
+ s = cont_map[cont];
+
+ switch (pi->mode) {
+
+ case 0: w0(regr|0x18|s); w2(4); w2(6); w2(4); w2(1); w0(8);
+ a = r1(); w0(0x28); b = r1(); w2(4);
+ return j44(a,b);
+
+ case 1: w0(regr|0x38|s); w2(4); w2(6); w2(4); w2(5); w0(8);
+ a = r12w(); w2(4);
+ return j53(a);
+
+ case 2: w0(regr|0x08|s); w2(4); w2(6); w2(4); w2(0xa5); w2(0xa1);
+ a = r0(); w2(4);
+ return a;
+
+ case 3:
+ case 4:
+ case 5: w0(0x20|s); w2(4); w2(6); w2(4); w3(regr);
+ a = r4(); b = r4(); w2(4); w2(0); w2(4);
+ return a;
+
+ }
+ return -1;
+}
+
+static void kbic_write_regr( PIA *pi, int cont, int regr, int val)
+
+{ int s;
+
+ s = cont_map[cont];
+
+ switch (pi->mode) {
+
+ case 0:
+ case 1:
+ case 2: w0(regr|0x10|s); w2(4); w2(6); w2(4);
+ w0(val); w2(5); w2(4);
+ break;
+
+ case 3:
+ case 4:
+ case 5: w0(0x20|s); w2(4); w2(6); w2(4); w3(regr);
+ w4(val); w4(val);
+ w2(4); w2(0); w2(4);
+ break;
+
+ }
+}
+
+static void k951_connect ( PIA *pi )
+
+{ pi->saved_r0 = r0();
+ pi->saved_r2 = r2();
+ w2(4);
+}
+
+static void k951_disconnect ( PIA *pi )
+
+{ w0(pi->saved_r0);
+ w2(pi->saved_r2);
+}
+
+#define CCP(x) w2(0xc4);w0(0xaa);w0(0x55);w0(0);w0(0xff);w0(0x87);\
+ w0(0x78);w0(x);w2(0xc5);w2(0xc4);w0(0xff);
+
+static void k971_connect ( PIA *pi )
+
+{ pi->saved_r0 = r0();
+ pi->saved_r2 = r2();
+ CCP(0x20);
+ w2(4);
+}
+
+static void k971_disconnect ( PIA *pi )
+
+{ CCP(0x30);
+ w0(pi->saved_r0);
+ w2(pi->saved_r2);
+}
+
+/* counts must be congruent to 0 MOD 4, but all known applications
+ have this property.
+*/
+
+static void kbic_read_block( PIA *pi, char * buf, int count )
+
+{ int k, a, b;
+
+ switch (pi->mode) {
+
+ case 0: w0(0x98); w2(4); w2(6); w2(4);
+ for (k=0;k<count/2;k++) {
+ w2(1); w0(8); a = r1();
+ w0(0x28); b = r1();
+ buf[2*k] = j44(a,b);
+ w2(5); b = r1();
+ w0(8); a = r1();
+ buf[2*k+1] = j44(a,b);
+ w2(4);
+ }
+ break;
+
+ case 1: w0(0xb8); w2(4); w2(6); w2(4);
+ for (k=0;k<count/4;k++) {
+ w0(0xb8);
+ w2(4); w2(5);
+ w0(8); buf[4*k] = j53(r12w());
+ w0(0xb8); buf[4*k+1] = j53(r12w());
+ w2(4); w2(5);
+ buf[4*k+3] = j53(r12w());
+ w0(8); buf[4*k+2] = j53(r12w());
+ }
+ w2(4);
+ break;
+
+ case 2: w0(0x88); w2(4); w2(6); w2(4);
+ for (k=0;k<count/2;k++) {
+ w2(0xa0); w2(0xa1); buf[2*k] = r0();
+ w2(0xa5); buf[2*k+1] = r0();
+ }
+ w2(4);
+ break;
+
+ case 3: w0(0xa0); w2(4); w2(6); w2(4); w3(0);
+ for (k=0;k<count;k++) buf[k] = r4();
+ w2(4); w2(0); w2(4);
+ break;
+
+ case 4: w0(0xa0); w2(4); w2(6); w2(4); w3(0);
+ for (k=0;k<count/2;k++) ((u16 *)buf)[k] = r4w();
+ w2(4); w2(0); w2(4);
+ break;
+
+ case 5: w0(0xa0); w2(4); w2(6); w2(4); w3(0);
+ for (k=0;k<count/4;k++) ((u32 *)buf)[k] = r4l();
+ w2(4); w2(0); w2(4);
+ break;
+
+
+ }
+}
+
+static void kbic_write_block( PIA *pi, char * buf, int count )
+
+{ int k;
+
+ switch (pi->mode) {
+
+ case 0:
+ case 1:
+ case 2: w0(0x90); w2(4); w2(6); w2(4);
+ for(k=0;k<count/2;k++) {
+ w0(buf[2*k+1]); w2(0); w2(4);
+ w0(buf[2*k]); w2(5); w2(4);
+ }
+ break;
+
+ case 3: w0(0xa0); w2(4); w2(6); w2(4); w3(0);
+ for(k=0;k<count/2;k++) {
+ w4(buf[2*k+1]);
+ w4(buf[2*k]);
+ }
+ w2(4); w2(0); w2(4);
+ break;
+
+ case 4: w0(0xa0); w2(4); w2(6); w2(4); w3(0);
+ for(k=0;k<count/2;k++) w4w(pi_swab16(buf,k));
+ w2(4); w2(0); w2(4);
+ break;
+
+ case 5: w0(0xa0); w2(4); w2(6); w2(4); w3(0);
+ for(k=0;k<count/4;k++) w4l(pi_swab32(buf,k));
+ w2(4); w2(0); w2(4);
+ break;
+
+ }
+
+}
+
+static void kbic_log_adapter( PIA *pi, char * scratch,
+ int verbose, char * chip )
+
+{ char *mode_string[6] = {"4-bit","5/3","8-bit",
+ "EPP-8","EPP_16","EPP-32"};
+
+ printk("%s: kbic %s, KingByte %s at 0x%x, ",
+ pi->device,KBIC_VERSION,chip,pi->port);
+ printk("mode %d (%s), delay %d\n",pi->mode,
+ mode_string[pi->mode],pi->delay);
+
+}
+
+static void k951_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{ kbic_log_adapter(pi,scratch,verbose,"KBIC-951A");
+}
+
+static void k971_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{ kbic_log_adapter(pi,scratch,verbose,"KBIC-971A");
+}
+
+static void kbic_inc_use ( void )
+
+{ MOD_INC_USE_COUNT;
+}
+
+static void kbic_dec_use ( void )
+
+{ MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol k951 = {"k951",0,6,3,1,1,
+ kbic_write_regr,
+ kbic_read_regr,
+ kbic_write_block,
+ kbic_read_block,
+ k951_connect,
+ k951_disconnect,
+ 0,
+ 0,
+ 0,
+ k951_log_adapter,
+ kbic_inc_use,
+ kbic_dec_use
+ };
+
+
+struct pi_protocol k971 = {"k971",0,6,3,1,1,
+ kbic_write_regr,
+ kbic_read_regr,
+ kbic_write_block,
+ kbic_read_block,
+ k971_connect,
+ k971_disconnect,
+ 0,
+ 0,
+ 0,
+ k971_log_adapter,
+ kbic_inc_use,
+ kbic_dec_use
+ };
+
+#ifdef MODULE
+
+int init_module(void)
+
+{ int s5,s7;
+
+ s5 = pi_register(&k951);
+ s7 = pi_register(&k971);
+
+ return (s5 || s7) - 1;
+}
+
+void cleanup_module(void)
+
+{ pi_unregister( &k951 );
+ pi_unregister( &k971 );
+}
+
+#endif
+
+/* end of kbic.c */
diff --git a/drivers/block/paride/on20.c b/drivers/block/paride/on20.c
new file mode 100644
index 000000000..c9b3fa35b
--- /dev/null
+++ b/drivers/block/paride/on20.c
@@ -0,0 +1,157 @@
+/*
+ on20.c (c) 1996 Grant R. Guenther <grant@torque.net>
+ Under the terms of the GNU public license.
+
+ on20.c is a low-level protocol driver for the
+ Onspec 90c20 parallel to IDE adapter.
+*/
+
+#define ON20_VERSION "1.0"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "paride.h"
+
+#define op(f) w2(4);w0(f);w2(5);w2(0xd);w2(5);w2(0xd);w2(5);w2(4);
+#define vl(v) w2(4);w0(v);w2(5);w2(7);w2(5);w2(4);
+
+#define j44(a,b) (((a>>4)&0x0f)|(b&0xf0))
+
+/* cont = 0 - access the IDE register file
+ cont = 1 - access the IDE command set
+*/
+
+static int on20_read_regr( PIA *pi, int cont, int regr )
+
+{ int h,l, r ;
+
+ r = (regr<<2) + 1 + cont;
+
+ op(1); vl(r); op(0);
+
+ switch (pi->mode) {
+
+ case 0: w2(4); w2(6); l = r1();
+ w2(4); w2(6); h = r1();
+ w2(4); w2(6); w2(4); w2(6); w2(4);
+ return j44(l,h);
+
+ case 1: w2(4); w2(0x26); r = r0();
+ w2(4); w2(0x26); w2(4);
+ return r;
+
+ }
+ return -1;
+}
+
+static void on20_write_regr( PIA *pi, int cont, int regr, int val )
+
+{ int r;
+
+ r = (regr<<2) + 1 + cont;
+
+ op(1); vl(r);
+ op(0); vl(val);
+ op(0); vl(val);
+}
+
+static void on20_connect ( PIA *pi)
+
+{ pi->saved_r0 = r0();
+ pi->saved_r2 = r2();
+
+ w2(4);w0(0);w2(0xc);w2(4);w2(6);w2(4);w2(6);w2(4);
+ if (pi->mode) { op(2); vl(8); op(2); vl(9); }
+ else { op(2); vl(0); op(2); vl(8); }
+}
+
+static void on20_disconnect ( PIA *pi )
+
+{ w2(4);w0(7);w2(4);w2(0xc);w2(4);
+ w0(pi->saved_r0);
+ w2(pi->saved_r2);
+}
+
+static void on20_read_block( PIA *pi, char * buf, int count )
+
+{ int k, l, h;
+
+ op(1); vl(1); op(0);
+
+ for (k=0;k<count;k++)
+ if (pi->mode) {
+ w2(4); w2(0x26); buf[k] = r0();
+ } else {
+ w2(6); l = r1(); w2(4);
+ w2(6); h = r1(); w2(4);
+ buf[k] = j44(l,h);
+ }
+ w2(4);
+}
+
+static void on20_write_block( PIA *pi, char * buf, int count )
+
+{ int k;
+
+ op(1); vl(1); op(0);
+
+ for (k=0;k<count;k++) { w2(5); w0(buf[k]); w2(7); }
+ w2(4);
+}
+
+static void on20_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{ char *mode_string[2] = {"4-bit","8-bit"};
+
+ printk("%s: on20 %s, OnSpec 90c20 at 0x%x, ",
+ pi->device,ON20_VERSION,pi->port);
+ printk("mode %d (%s), delay %d\n",pi->mode,
+ mode_string[pi->mode],pi->delay);
+
+}
+
+static void on20_inc_use ( void )
+
+{ MOD_INC_USE_COUNT;
+}
+
+static void on20_dec_use ( void )
+
+{ MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol on20 = {"on20",0,2,2,1,1,
+ on20_write_regr,
+ on20_read_regr,
+ on20_write_block,
+ on20_read_block,
+ on20_connect,
+ on20_disconnect,
+ 0,
+ 0,
+ 0,
+ on20_log_adapter,
+ on20_inc_use,
+ on20_dec_use
+ };
+
+
+#ifdef MODULE
+
+int init_module(void)
+
+{ return pi_register( &on20 ) - 1;
+}
+
+void cleanup_module(void)
+
+{ pi_unregister( &on20 );
+}
+
+#endif
+
+/* end of on20.c */
diff --git a/drivers/block/paride/on26.c b/drivers/block/paride/on26.c
new file mode 100644
index 000000000..21e6e017e
--- /dev/null
+++ b/drivers/block/paride/on26.c
@@ -0,0 +1,261 @@
+/*
+ on26.c (c) 1997 Grant R. Guenther <grant@torque.net>
+ Under the terms of the GNU public license.
+
+ on26.c is a low-level protocol driver for the
+ OnSpec 90c26 parallel to IDE adapter chip.
+
+*/
+
+#define ON26_VERSION "1.0"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "paride.h"
+
+/* mode codes: 0 nybble reads, 8-bit writes
+ 1 8-bit reads and writes
+ 2 8-bit EPP mode
+ 3 EPP-16
+ 4 EPP-32
+*/
+
+#define j44(a,b) (((a>>4)&0x0f)|(b&0xf0))
+
+#define P1 w2(5);w2(0xd);w2(5);w2(0xd);w2(5);w2(4);
+#define P2 w2(5);w2(7);w2(5);w2(4);
+
+/* cont = 0 - access the IDE register file
+ cont = 1 - access the IDE command set
+*/
+
+static int on26_read_regr( PIA *pi, int cont, int regr )
+
+{ int a, b, r;
+
+ r = (regr<<2) + 1 + cont;
+
+ switch (pi->mode) {
+
+ case 0: w0(1); P1; w0(r); P2; w0(0); P1;
+ w2(6); a = r1(); w2(4);
+ w2(6); b = r1(); w2(4);
+ w2(6); w2(4); w2(6); w2(4);
+ return j44(a,b);
+
+ case 1: w0(1); P1; w0(r); P2; w0(0); P1;
+ w2(0x26); a = r0(); w2(4); w2(0x26); w2(4);
+ return a;
+
+ case 2:
+ case 3:
+ case 4: w3(1); w3(1); w2(5); w4(r); w2(4);
+ w3(0); w3(0); w2(0x24); a = r4(); w2(4);
+ w2(0x24); r4(); w2(4);
+ return a;
+
+ }
+ return -1;
+}
+
+static void on26_write_regr( PIA *pi, int cont, int regr, int val )
+
+{ int r;
+
+ r = (regr<<2) + 1 + cont;
+
+ switch (pi->mode) {
+
+ case 0:
+ case 1: w0(1); P1; w0(r); P2; w0(0); P1;
+ w0(val); P2; w0(val); P2;
+ break;
+
+ case 2:
+ case 3:
+ case 4: w3(1); w3(1); w2(5); w4(r); w2(4);
+ w3(0); w3(0);
+ w2(5); w4(val); w2(4);
+ w2(5); w4(val); w2(4);
+ break;
+ }
+}
+
+#define CCP(x) w0(0xff);w0(0xaa);w0(0x55);w0(0);w0(0xff);\
+ w0(0x87);w0(0x78);w0(x);w2(4);
+
+static void on26_connect ( PIA *pi )
+
+{ int x;
+
+ pi->saved_r0 = r0();
+ pi->saved_r2 = r2();
+
+ CCP(0x20);
+ w2(0xcd); w2(0xcc); w0(0xff);
+ x = 8; if (pi->mode) x = 9;
+
+ w0(2); P1; w0(8); P2;
+ w0(2); P1; w0(x); P2;
+}
+
+static void on26_disconnect ( PIA *pi )
+
+{ if (pi->mode >= 2) { w3(4); w3(4); w3(4); w3(4); }
+ else { w0(4); P1; w0(4); P1; }
+ CCP(0x30);
+ w2(0xcd); w2(0xcc); w0(0xff);
+ w0(pi->saved_r0);
+ w2(pi->saved_r2);
+}
+
+static void on26_read_block( PIA *pi, char * buf, int count )
+
+{ int k, a, b;
+
+ switch (pi->mode) {
+
+ case 0: w0(1); P1; w0(1); P2; w0(2); P1; w0(0x18); P2; w0(0); P1;
+ udelay(10);
+ for (k=0;k<count;k++) {
+ w2(6); a = r1();
+ w2(4); b = r1();
+ buf[k] = j44(a,b);
+ }
+ w0(2); P1; w0(8); P2;
+ break;
+
+ case 1: w0(1); P1; w0(1); P2; w0(2); P1; w0(0x19); P2; w0(0); P1;
+ udelay(10);
+ for (k=0;k<count/2;k++) {
+ w2(0x26); buf[2*k] = r0();
+ w2(0x24); buf[2*k+1] = r0();
+ }
+ w0(2); P1; w0(9); P2;
+ break;
+
+ case 2: w3(1); w3(1); w2(5); w4(1); w2(4);
+ w3(0); w3(0); w2(0x24);
+ udelay(10);
+ for (k=0;k<count;k++) buf[k] = r4();
+ w2(4);
+ break;
+
+ case 3: w3(1); w3(1); w2(5); w4(1); w2(4);
+ w3(0); w3(0); w2(0x24);
+ udelay(10);
+ for (k=0;k<count/2;k++) ((u16 *)buf)[k] = r4w();
+ w2(4);
+ break;
+
+ case 4: w3(1); w3(1); w2(5); w4(1); w2(4);
+ w3(0); w3(0); w2(0x24);
+ udelay(10);
+ for (k=0;k<count/4;k++) ((u32 *)buf)[k] = r4l();
+ w2(4);
+ break;
+
+ }
+}
+
+static void on26_write_block( PIA *pi, char * buf, int count )
+
+{ int k;
+
+ switch (pi->mode) {
+
+ case 0:
+ case 1: w0(1); P1; w0(1); P2;
+ w0(2); P1; w0(0x18+pi->mode); P2; w0(0); P1;
+ udelay(10);
+ for (k=0;k<count/2;k++) {
+ w2(5); w0(buf[2*k]);
+ w2(7); w0(buf[2*k+1]);
+ }
+ w2(5); w2(4);
+ w0(2); P1; w0(8+pi->mode); P2;
+ break;
+
+ case 2: w3(1); w3(1); w2(5); w4(1); w2(4);
+ w3(0); w3(0); w2(0xc5);
+ udelay(10);
+ for (k=0;k<count;k++) w4(buf[k]);
+ w2(0xc4);
+ break;
+
+ case 3: w3(1); w3(1); w2(5); w4(1); w2(4);
+ w3(0); w3(0); w2(0xc5);
+ udelay(10);
+ for (k=0;k<count/2;k++) w4w(((u16 *)buf)[k]);
+ w2(0xc4);
+ break;
+
+ case 4: w3(1); w3(1); w2(5); w4(1); w2(4);
+ w3(0); w3(0); w2(0xc5);
+ udelay(10);
+ for (k=0;k<count/4;k++) w4l(((u32 *)buf)[k]);
+ w2(0xc4);
+ break;
+
+ }
+
+}
+
+static void on26_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{ char *mode_string[5] = {"4-bit","8-bit","EPP-8",
+ "EPP-16","EPP-32"};
+
+ printk("%s: on26 %s, OnSpec 90c26 at 0x%x, ",
+ pi->device,ON26_VERSION,pi->port);
+ printk("mode %d (%s), delay %d\n",pi->mode,
+ mode_string[pi->mode],pi->delay);
+
+}
+
+static void on26_inc_use ( void )
+
+{ MOD_INC_USE_COUNT;
+}
+
+static void on26_dec_use ( void )
+
+{ MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol on26 = {"on26",0,5,2,1,1,
+ on26_write_regr,
+ on26_read_regr,
+ on26_write_block,
+ on26_read_block,
+ on26_connect,
+ on26_disconnect,
+ 0,
+ 0,
+ 0,
+ on26_log_adapter,
+ on26_inc_use,
+ on26_dec_use
+ };
+
+
+#ifdef MODULE
+
+int init_module(void)
+
+{ return pi_register( &on26 ) - 1;
+}
+
+void cleanup_module(void)
+
+{ pi_unregister( &on26 );
+}
+
+#endif
+
+/* end of on26.c */
+
diff --git a/drivers/block/paride/paride.c b/drivers/block/paride/paride.c
new file mode 100644
index 000000000..0d7cf2f01
--- /dev/null
+++ b/drivers/block/paride/paride.c
@@ -0,0 +1,485 @@
+/*
+ paride.c (c) 1997 Grant R. Guenther <grant@torque.net>
+ Under the terms of the GNU public license.
+
+ This is the base module for the family of device drivers
+ that support parallel port IDE devices.
+
+*/
+
+#define PI_VERSION "1.0"
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/string.h>
+
+#ifdef CONFIG_PARPORT_MODULE
+#define CONFIG_PARPORT
+#endif
+
+#ifdef CONFIG_PARPORT
+#include <linux/parport.h>
+#endif
+
+#include "paride.h"
+
+#define MAX_PROTOS 32
+
+static struct pi_protocol *protocols[MAX_PROTOS];
+
+
+void pi_write_regr( PIA *pi, int cont, int regr, int val)
+
+{ pi->proto->write_regr(pi,cont,regr,val);
+}
+
+int pi_read_regr( PIA *pi, int cont, int regr)
+
+{ return pi->proto->read_regr(pi,cont,regr);
+}
+
+void pi_write_block( PIA *pi, char * buf, int count)
+
+{ pi->proto->write_block(pi,buf,count);
+}
+
+void pi_read_block( PIA *pi, char * buf, int count)
+
+{ pi->proto->read_block(pi,buf,count);
+}
+
+#ifdef CONFIG_PARPORT
+
+static void pi_wake_up( void *p)
+
+{ PIA *pi = (PIA *) p;
+ long flags;
+ void (*cont)(void) = NULL;
+
+ save_flags(flags);
+ cli();
+
+ if (pi->claim_cont && !parport_claim(pi->pardev)) {
+ cont = pi->claim_cont;
+ pi->claim_cont = NULL;
+ pi->claimed = 1;
+ }
+
+ restore_flags(flags);
+ wake_up(&(pi->parq));
+ if (cont) cont();
+}
+
+#endif
+
+void pi_do_claimed( PIA *pi, void(*cont)(void))
+
+#ifdef CONFIG_PARPORT
+
+{ long flags;
+
+ save_flags(flags);
+ cli();
+
+ if (!pi->pardev || !parport_claim(pi->pardev)) {
+ pi->claimed = 1;
+ restore_flags(flags);
+ cont();
+ } else {
+ pi->claim_cont = cont;
+ restore_flags(flags);
+ }
+}
+
+#else
+
+{ cont();
+}
+
+#endif
+
+static void pi_claim( PIA *pi)
+
+{ if (pi->claimed) return;
+ pi->claimed = 1;
+#ifdef CONFIG_PARPORT
+ if (pi->pardev)
+ while (parport_claim((struct pardevice *)(pi->pardev)))
+ sleep_on(&(pi->parq));
+#endif
+}
+
+static void pi_unclaim( PIA *pi)
+
+{ pi->claimed = 0;
+#ifdef CONFIG_PARPORT
+ if (pi->pardev) parport_release((struct pardevice *)(pi->pardev));
+#endif
+}
+
+void pi_connect( PIA *pi)
+
+{ pi_claim(pi);
+ pi->proto->connect(pi);
+}
+
+void pi_disconnect( PIA *pi)
+
+{ pi->proto->disconnect(pi);
+ pi_unclaim(pi);
+}
+
+static void pi_unregister_parport( PIA *pi)
+
+{
+#ifdef CONFIG_PARPORT
+ if (pi->pardev) {
+ parport_unregister_device((struct pardevice *)(pi->pardev));
+ pi->pardev = NULL;
+ }
+#endif
+}
+
+void pi_release( PIA *pi)
+
+{ pi_unregister_parport(pi);
+ if ((!pi->pardev)&&(pi->reserved))
+ release_region(pi->port,pi->reserved);
+ pi->proto->dec_use();
+}
+
+#define WR(r,v) pi_write_regr(pi,0,r,v)
+#define RR(r) (pi_read_regr(pi,0,r))
+
+static int pi_test_proto( PIA *pi, char * scratch, int verbose )
+
+{ int j, k;
+ int e[2] = {0,0};
+
+ if (pi->proto->test_proto) {
+ pi_claim(pi);
+ j = pi->proto->test_proto(pi,scratch,verbose);
+ pi_unclaim(pi);
+ return j;
+ }
+
+ pi_connect(pi);
+
+ for (j=0;j<2;j++) {
+ WR(6,0xa0+j*0x10);
+ for (k=0;k<256;k++) {
+ WR(2,k^0xaa);
+ WR(3,k^0x55);
+ if (RR(2) != (k^0xaa)) e[j]++;
+ }
+ }
+
+ pi_disconnect(pi);
+
+ if (verbose)
+ printk("%s: %s: port 0x%x, mode %d, test=(%d,%d)\n",
+ pi->device,pi->proto->name,pi->port,
+ pi->mode,e[0],e[1]);
+
+ return (e[0] && e[1]); /* not here if both > 0 */
+}
+
+int pi_register( PIP *pr)
+
+{ int k;
+
+ for (k=0;k<MAX_PROTOS;k++)
+ if (protocols[k] && !strcmp(pr->name,protocols[k]->name)) {
+ printk("paride: %s protocol already registered\n",pr->name);
+ return 0;
+ }
+ k = 0;
+ while((k<MAX_PROTOS) && (protocols[k])) k++;
+ if (k == MAX_PROTOS) {
+ printk("paride: protocol table full\n");
+ return 0;
+ }
+ MOD_INC_USE_COUNT;
+ protocols[k] = pr;
+ pr->index = k;
+ printk("paride: %s registered as protocol %d\n",pr->name,k);
+ return 1;
+}
+
+void pi_unregister( PIP *pr)
+
+{ if (!pr) return;
+ if (protocols[pr->index] != pr) {
+ printk("paride: %s not registered\n",pr->name);
+ return;
+ }
+ protocols[pr->index] = 0;
+ MOD_DEC_USE_COUNT;
+}
+
+static void pi_register_parport( PIA *pi, int verbose)
+
+{
+#ifdef CONFIG_PARPORT
+
+ struct parport *pp;
+
+ pp = parport_enumerate();
+
+ while((pp)&&(pp->base != pi->port)) pp = pp->next;
+
+ if (!pp) return;
+
+ pi->pardev = (void *) parport_register_device(
+ pp,pi->device,NULL,pi_wake_up,NULL,0,(void *)pi);
+
+ pi->parq = NULL;
+
+ if (verbose) printk("%s: 0x%x is %s\n",pi->device,pi->port,pp->name);
+
+ pi->parname = pp->name;
+
+#endif
+}
+
+static int pi_probe_mode( PIA *pi, int max, char * scratch, int verbose)
+
+{ int best, range;
+
+ if (pi->mode != -1) {
+ if (pi->mode >= max) return 0;
+ range = 3;
+ if (pi->mode >= pi->proto->epp_first) range = 8;
+ if ((range == 8) && (pi->port % 8)) return 0;
+ if ((!pi->pardev) && check_region(pi->port,range)) return 0;
+ pi->reserved = range;
+ return (!pi_test_proto(pi,scratch,verbose));
+ }
+ best = -1;
+ for(pi->mode=0;pi->mode<max;pi->mode++) {
+ range = 3;
+ if (pi->mode >= pi->proto->epp_first) range = 8;
+ if ((range == 8) && (pi->port % 8)) break;
+ if ((!pi->pardev) && check_region(pi->port,range)) break;
+ pi->reserved = range;
+ if (!pi_test_proto(pi,scratch,verbose)) best = pi->mode;
+ }
+ pi->mode = best;
+ return (best > -1);
+}
+
+static int pi_probe_unit( PIA *pi, int unit, char * scratch, int verbose)
+
+{ int max,s,e;
+
+ s = unit; e = s+1;
+
+ if (s == -1) {
+ s = 0;
+ e = pi->proto->max_units;
+ }
+
+ pi_register_parport(pi,verbose);
+
+ if ((!pi->pardev) && check_region(pi->port,3)) return 0;
+
+ if (pi->proto->test_port) {
+ pi_claim(pi);
+ max = pi->proto->test_port(pi);
+ pi_unclaim(pi);
+ }
+ else max = pi->proto->max_mode;
+
+ if (pi->proto->probe_unit) {
+ pi_claim(pi);
+ for (pi->unit=s;pi->unit<e;pi->unit++)
+ if (pi->proto->probe_unit(pi)) {
+ pi_unclaim(pi);
+ if (pi_probe_mode(pi,max,scratch,verbose)) return 1;
+ pi_unregister_parport(pi);
+ return 0;
+ }
+ pi_unclaim(pi);
+ pi_unregister_parport(pi);
+ return 0;
+ }
+
+ if (!pi_probe_mode(pi,max,scratch,verbose)) {
+ pi_unregister_parport(pi);
+ return 0;
+ }
+ return 1;
+
+}
+
+int pi_init(PIA *pi, int autoprobe, int port, int mode,
+ int unit, int protocol, int delay, char * scratch,
+ int devtype, int verbose, char *device )
+
+{ int p,k,s,e;
+ int lpts[7] = {0x3bc,0x378,0x278,0x268,0x27c,0x26c,0};
+
+ s = protocol; e = s+1;
+
+ if (autoprobe) {
+ s = 0;
+ e = MAX_PROTOS;
+ } else if ((s < 0) || (s >= MAX_PROTOS) || (port <= 0) ||
+ (!protocols[s]) || (unit < 0) ||
+ (unit >= protocols[s]->max_units)) {
+ printk("%s: Invalid parameters\n",device);
+ return 0;
+ }
+
+ for (p=s;p<e;p++) {
+ if (protocols[p]) {
+ pi->proto = protocols[p];
+ pi->proto->inc_use();
+ if (delay == -1) pi->delay = pi->proto->default_delay;
+ else pi->delay = delay;
+ pi->devtype = devtype;
+ pi->device = device;
+ pi->private = 0;
+
+ pi->parname = NULL;
+ pi->pardev = NULL;
+ pi->parq = NULL;
+ pi->claimed = 0;
+ pi->claim_cont = NULL;
+
+ pi->mode = mode;
+ if (port != -1) {
+ pi->port = port;
+ if (pi_probe_unit(pi,unit,scratch,verbose)) break;
+ pi->port = 0;
+ } else {
+ k = 0;
+ while ((pi->port = lpts[k++]))
+ if (pi_probe_unit(pi,unit,scratch,verbose)) break;
+ if (pi->port) break;
+ }
+ pi->proto->dec_use();
+ }
+ }
+
+ if (!pi->port) {
+ if (autoprobe) printk("%s: Autoprobe failed\n",device);
+ else printk("%s: Adapter not found\n",device);
+ return 0;
+ }
+
+ if (!pi->pardev)
+ request_region(pi->port,pi->reserved,pi->device);
+
+ if (pi->parname)
+ printk("%s: Sharing %s at 0x%x\n",pi->device,
+ pi->parname,pi->port);
+
+ pi->proto->log_adapter(pi,scratch,verbose);
+
+ return 1;
+}
+
+#ifdef MODULE
+
+int init_module(void)
+
+{ int k;
+
+ for (k=0;k<MAX_PROTOS;k++) protocols[k] = 0;
+ printk("paride: version %s installed\n",PI_VERSION);
+ return 0;
+}
+
+void cleanup_module(void)
+
+{
+}
+
+#else
+
+void paride_init( void )
+
+{
+
+#ifdef CONFIG_PARIDE_ATEN
+ { extern struct pi_protocol aten;
+ pi_register(&aten);
+ };
+#endif
+#ifdef CONFIG_PARIDE_BPCK
+ { extern struct pi_protocol bpck;
+ pi_register(&bpck);
+ };
+#endif
+#ifdef CONFIG_PARIDE_COMM
+ { extern struct pi_protocol comm;
+ pi_register(&comm);
+ };
+#endif
+#ifdef CONFIG_PARIDE_DSTR
+ { extern struct pi_protocol dstr;
+ pi_register(&dstr);
+ };
+#endif
+#ifdef CONFIG_PARIDE_EPAT
+ { extern struct pi_protocol epat;
+ pi_register(&epat);
+ };
+#endif
+#ifdef CONFIG_PARIDE_EPIA
+ { extern struct pi_protocol epia;
+ pi_register(&epia);
+ };
+#endif
+#ifdef CONFIG_PARIDE_FRPW
+ { extern struct pi_protocol frpw;
+ pi_register(&frpw);
+ };
+#endif
+#ifdef CONFIG_PARIDE_KBIC
+ { extern struct pi_protocol k951;
+ extern struct pi_protocol k971;
+ pi_register(&k951);
+ pi_register(&k971);
+ };
+#endif
+#ifdef CONFIG_PARIDE_ON20
+ { extern struct pi_protocol on20;
+ pi_register(&on20);
+ };
+#endif
+#ifdef CONFIG_PARIDE_ON26
+ { extern struct pi_protocol on26;
+ pi_register(&on26);
+ };
+#endif
+
+#ifdef CONFIG_PARIDE_PD
+ { extern int pd_init(void);
+ pd_init();
+ };
+#endif
+#ifdef CONFIG_PARIDE_PCD
+ { extern int pcd_init(void);
+ pcd_init();
+ };
+#endif
+#ifdef CONFIG_PARIDE_PF
+ { extern int pf_init(void);
+ pf_init();
+ };
+#endif
+#ifdef CONFIG_PARIDE_PT
+ { extern int pt_init(void);
+ pt_init();
+ };
+#endif
+}
+
+#endif
+
+/* end of paride.c */
diff --git a/drivers/block/paride/paride.h b/drivers/block/paride/paride.h
new file mode 100644
index 000000000..04de51441
--- /dev/null
+++ b/drivers/block/paride/paride.h
@@ -0,0 +1,157 @@
+/* paride.h (c) 1997 Grant R. Guenther <grant@torque.net>
+ Under the terms of the GPL.
+
+ This file defines the interface between the high-level parallel
+ IDE device drivers (pd, pf, pcd, pt) and the adapter chips.
+
+*/
+
+#define PARIDE_H_VERSION "1.0"
+
+/* Some adapters need to know what kind of device they are in
+
+ Values for devtype:
+*/
+
+#define PI_PD 0 /* IDE disk */
+#define PI_PCD 1 /* ATAPI CDrom */
+#define PI_PF 2 /* ATAPI disk */
+#define PI_PT 3 /* ATAPI tape */
+
+/* The paride module contains no state, instead the drivers allocate
+ a pi_adapter data structure and pass it to paride in every operation.
+
+*/
+
+struct pi_adapter {
+
+ struct pi_protocol *proto; /* adapter protocol */
+ int port; /* base address of parallel port */
+ int mode; /* transfer mode in use */
+ int delay; /* adapter delay setting */
+ int devtype; /* device type: PI_PD etc. */
+ char *device; /* name of driver */
+ int unit; /* unit number for chained adapters */
+ int saved_r0; /* saved port state */
+ int saved_r2; /* saved port state */
+ int reserved; /* number of ports reserved */
+ int private; /* for protocol module */
+
+ struct wait_queue *parq; /* semaphore for parport sharing */
+ void *pardev; /* pointer to pardevice */
+ char *parname; /* parport name */
+ int claimed; /* parport has already been claimed */
+ void (*claim_cont)(void); /* continuation for parport wait */
+};
+
+typedef struct pi_adapter PIA;
+
+/* functions exported by paride to the high level drivers */
+
+extern int pi_init(PIA *pi,
+ int autoprobe, /* 1 to autoprobe */
+ int port, /* base port address */
+ int mode, /* -1 for autoprobe */
+ int unit, /* unit number, if supported */
+ int protocol, /* protocol to use */
+ int delay, /* -1 to use adapter specific default */
+ char * scratch, /* address of 512 byte buffer */
+ int devtype, /* device type: PI_PD, PI_PCD, etc ... */
+ int verbose, /* log verbose data while probing */
+ char *device /* name of the driver */
+ ); /* returns 0 on failure, 1 on success */
+
+extern void pi_release(PIA *pi);
+
+/* registers are addressed as (cont,regr)
+
+ cont: 0 for command register file, 1 for control register(s)
+ regr: 0-7 for register number.
+
+*/
+
+extern void pi_write_regr(PIA *pi, int cont, int regr, int val);
+
+extern int pi_read_regr(PIA *pi, int cont, int regr);
+
+extern void pi_write_block(PIA *pi, char * buf, int count);
+
+extern void pi_read_block(PIA *pi, char * buf, int count);
+
+extern void pi_connect(PIA *pi);
+
+extern void pi_disconnect(PIA *pi);
+
+extern void pi_do_claimed(PIA *pi, void (*cont)(void));
+
+/* macros and functions exported to the protocol modules */
+
+#define delay_p (pi->delay?udelay(pi->delay):0)
+#define out_p(offs,byte) outb(byte,pi->port+offs); delay_p;
+#define in_p(offs) (delay_p,inb(pi->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,pi->port+4); delay_p;}
+#define w4l(data) {outl(data,pi->port+4); delay_p;}
+#define r4w() (delay_p,inw(pi->port+4)&0xffff)
+#define r4l() (delay_p,inl(pi->port+4)&0xffffffff)
+
+static inline u16 pi_swab16( char *b, int k)
+
+{ union { u16 u; char t[2]; } r;
+
+ r.t[0]=b[2*k+1]; r.t[1]=b[2*k];
+ return r.u;
+}
+
+static inline u32 pi_swab32( char *b, int k)
+
+{ union { u32 u; char f[4]; } r;
+
+ r.f[0]=b[4*k+1]; r.f[1]=b[4*k];
+ r.f[2]=b[4*k+3]; r.f[3]=b[4*k+2];
+ return r.u;
+}
+
+struct pi_protocol {
+
+ char name[8]; /* name for this protocol */
+ int index; /* index into protocol table */
+
+ int max_mode; /* max mode number */
+ int epp_first; /* modes >= this use 8 ports */
+
+ int default_delay; /* delay parameter if not specified */
+ int max_units; /* max chained units probed for */
+
+ void (*write_regr)(PIA *,int,int,int);
+ int (*read_regr)(PIA *,int,int);
+ void (*write_block)(PIA *,char *,int);
+ void (*read_block)(PIA *,char *,int);
+
+ void (*connect)(PIA *);
+ void (*disconnect)(PIA *);
+
+ int (*test_port)(PIA *);
+ int (*probe_unit)(PIA *);
+ int (*test_proto)(PIA *,char *,int);
+ void (*log_adapter)(PIA *,char *,int);
+
+ void (*inc_use)(void);
+ void (*dec_use)(void);
+};
+
+typedef struct pi_protocol PIP;
+
+extern int pi_register( PIP * );
+extern void pi_unregister ( PIP * );
+
+/* end of paride.h */
diff --git a/drivers/block/paride/pcd.c b/drivers/block/paride/pcd.c
new file mode 100644
index 000000000..ca520d32b
--- /dev/null
+++ b/drivers/block/paride/pcd.c
@@ -0,0 +1,816 @@
+/*
+ pcd.c (c) 1997 Grant R. Guenther <grant@torque.net>
+ Under the terms of the GNU public license.
+
+ This is high-level driver for parallel port ATAPI CDrom
+ drives based on chips supported by the paride module.
+
+ By default, the driver will autoprobe for a single parallel
+ port ATAPI CDrom drive, but if their individual parameters are
+ specified, the driver can handle up to 4 drives.
+
+ The behaviour of the pcd driver can be altered by setting
+ some parameters from the insmod command line. The following
+ parameters are adjustable:
+
+ drive0 These four arguments can be arrays of
+ drive1 1-6 integers as follows:
+ drive2
+ drive3 <prt>,<pro>,<uni>,<mod>,<slv>,<dly>
+
+ Where,
+
+ <prt> is the base of the parallel port address for
+ the corresponding drive. (required)
+
+ <pro> is the protocol number for the adapter that
+ supports this drive. These numbers are
+ logged by 'paride' when the protocol modules
+ are initialised. (0 if not given)
+
+ <uni> for those adapters that support chained
+ devices, this is the unit selector for the
+ chain of devices on the given port. It should
+ be zero for devices that don't support chaining.
+ (0 if not given)
+
+ <mod> this can be -1 to choose the best mode, or one
+ of the mode numbers supported by the adapter.
+ (-1 if not given)
+
+ <slv> ATAPI CDroms can be jumpered to master or slave.
+ Set this to 0 to choose the master drive, 1 to
+ choose the slave, -1 (the default) to choose the
+ first drive found.
+
+ <dly> some parallel ports require the driver to
+ go more slowly. -1 sets a default value that
+ should work with the chosen protocol. Otherwise,
+ set this to a small integer, the larger it is
+ the slower the port i/o. In some cases, setting
+ this to zero will speed up the device. (default -1)
+
+ major You may use this parameter to overide the
+ default major number (46) that this driver
+ will use. Be sure to change the device
+ name as well.
+
+ name This parameter is a character string that
+ contains the name the kernel will use for this
+ device (in /proc output, for instance).
+ (default "pcd")
+
+ verbose This parameter controls the amount of logging
+ that is done while the driver probes for
+ devices. Set it to 0 for a quiet load, or 1 to
+ see all the progress messages. (default 0)
+
+ nice This parameter controls the driver's use of
+ idle CPU time, at the expense of some speed.
+
+ If this driver is built into the kernel, you can use kernel
+ the following command line parameters, with the same values
+ as the corresponding module parameters listed above:
+
+ pcd.drive0
+ pcd.drive1
+ pcd.drive2
+ pcd.drive3
+ pcd.nice
+
+ In addition, you can use the parameter pcd.disable to disable
+ the driver entirely.
+
+*/
+
+/* Changes:
+
+ 1.01 GRG 1997.01.24 Added test unit ready support
+
+*/
+
+#define PCD_VERSION "1.01"
+#define PCD_MAJOR 46
+#define PCD_NAME "pcd"
+#define PCD_UNITS 4
+
+/* Here are things one can override from the insmod command.
+ Most are autoprobed by paride unless set here. Verbose is on
+ by default.
+
+*/
+
+static int verbose = 0;
+static int major = PCD_MAJOR;
+static char *name = PCD_NAME;
+static int nice = 0;
+static int disable = 0;
+
+static int drive0[6] = {0,0,0,-1,-1,-1};
+static int drive1[6] = {0,0,0,-1,-1,-1};
+static int drive2[6] = {0,0,0,-1,-1,-1};
+static int drive3[6] = {0,0,0,-1,-1,-1};
+
+static int (*drives[4])[6] = {&drive0,&drive1,&drive2,&drive3};
+static int pcd_drive_count;
+
+#define D_PRT 0
+#define D_PRO 1
+#define D_UNI 2
+#define D_MOD 3
+#define D_SLV 4
+#define D_DLY 5
+
+#define DU (*drives[unit])
+
+/* end of parameters */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/cdrom.h>
+
+#include <asm/uaccess.h>
+
+#ifndef MODULE
+
+#include "setup.h"
+
+static STT pcd_stt[6] = {{"drive0",6,drive0},
+ {"drive1",6,drive1},
+ {"drive2",6,drive2},
+ {"drive3",6,drive3},
+ {"disable",1,&disable},
+ {"nice",1,&nice}};
+
+void pcd_setup( char *str, int *ints)
+
+{ generic_setup(pcd_stt,6,str);
+}
+
+#endif
+
+MODULE_PARM(verbose,"i");
+MODULE_PARM(major,"i");
+MODULE_PARM(name,"s");
+MODULE_PARM(nice,"i");
+MODULE_PARM(drive0,"1-6i");
+MODULE_PARM(drive1,"1-6i");
+MODULE_PARM(drive2,"1-6i");
+MODULE_PARM(drive3,"1-6i");
+
+#include "paride.h"
+
+/* set up defines for blk.h, why don't all drivers do it this way ? */
+
+#define MAJOR_NR major
+#define DEVICE_NAME "PCD"
+#define DEVICE_REQUEST do_pcd_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#include <linux/blk.h>
+
+#include "pseudo.h"
+
+#define PCD_RETRIES 5
+#define PCD_TMO 800 /* timeout in jiffies */
+#define PCD_DELAY 50 /* spin delay in uS */
+#define PCD_READY_TMO 20
+
+#define PCD_SPIN (10000/PCD_DELAY)*PCD_TMO
+
+#define IDE_ERR 0x01
+#define IDE_DRQ 0x08
+#define IDE_READY 0x40
+#define IDE_BUSY 0x80
+
+int pcd_init(void);
+void cleanup_module( void );
+
+static int pcd_open(struct inode *inode, struct file *file);
+static void do_pcd_request(void);
+static void do_pcd_read(int unit);
+static int pcd_ioctl(struct inode *inode,struct file *file,
+ unsigned int cmd, unsigned long arg);
+
+static int pcd_release (struct inode *inode, struct file *file);
+
+static int pcd_detect(void);
+static void pcd_lock(int unit);
+static void pcd_unlock(int unit);
+static void pcd_eject(int unit);
+static int pcd_check_media(int unit);
+static void do_pcd_read_drq(void);
+
+static int pcd_blocksizes[PCD_UNITS];
+
+#define PCD_NAMELEN 8
+
+struct pcd_unit {
+ struct pi_adapter pia; /* interface to paride layer */
+ struct pi_adapter *pi;
+ int drive; /* master/slave */
+ int last_sense; /* result of last request sense */
+ int access; /* count of active opens */
+ int present; /* does this unit exist ? */
+ char name[PCD_NAMELEN]; /* pcd0, pcd1, etc */
+ };
+
+struct pcd_unit pcd[PCD_UNITS];
+
+/* 'unit' must be defined in all functions - either as a local or a param */
+
+#define PCD pcd[unit]
+#define PI PCD.pi
+
+static char pcd_scratch[64];
+static char pcd_buffer[2048]; /* raw block buffer */
+static int pcd_bufblk = -1; /* block in buffer, in CD units,
+ -1 for nothing there. See also
+ pd_unit.
+ */
+
+/* the variables below are used mainly in the I/O request engine, which
+ processes only one request at a time.
+*/
+
+static int pcd_unit = -1; /* unit of current request & bufblk */
+static int pcd_retries; /* retries on current request */
+static int pcd_busy = 0; /* request being processed ? */
+static int pcd_sector; /* address of next requested sector */
+static int pcd_count; /* number of blocks still to do */
+static char * pcd_buf; /* buffer for request in progress */
+
+/* kernel glue structures */
+
+static struct file_operations pcd_fops = {
+ NULL, /* lseek - default */
+ block_read, /* read - general block-dev read */
+ block_write, /* write - general block-dev write */
+ NULL, /* readdir - bad */
+ NULL, /* select */
+ pcd_ioctl, /* ioctl */
+ NULL, /* mmap */
+ pcd_open, /* open */
+ pcd_release, /* release */
+ block_fsync, /* fsync */
+ NULL, /* fasync */
+ NULL, /* media change ? */
+ NULL /* revalidate new media */
+};
+
+static void pcd_init_units( void )
+
+{ int unit, j;
+
+ pcd_drive_count = 0;
+ for (unit=0;unit<PCD_UNITS;unit++) {
+ PCD.pi = & PCD.pia;
+ PCD.access = 0;
+ PCD.present = 0;
+ PCD.last_sense = 0;
+ j = 0;
+ while ((j < PCD_NAMELEN-2) && (PCD.name[j]=name[j])) j++;
+ PCD.name[j++] = '0' + unit;
+ PCD.name[j] = 0;
+ PCD.drive = DU[D_SLV];
+ if (DU[D_PRT]) pcd_drive_count++;
+ }
+}
+
+int pcd_init (void) /* preliminary initialisation */
+
+{ int i;
+
+ if (disable) return -1;
+
+ pcd_init_units();
+
+ if (pcd_detect()) return -1;
+
+ if (register_blkdev(MAJOR_NR,name,&pcd_fops)) {
+ printk("pcd: unable to get major number %d\n",MAJOR_NR);
+ return -1;
+ }
+ blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+ read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read ahead */
+
+ for (i=0;i<PCD_UNITS;i++) pcd_blocksizes[i] = 1024;
+ blksize_size[MAJOR_NR] = pcd_blocksizes;
+
+ return 0;
+}
+
+static int pcd_open (struct inode *inode, struct file *file)
+
+{ int unit = DEVICE_NR(inode->i_rdev);
+
+ if ((unit >= PCD_UNITS) || (!PCD.present)) return -ENODEV;
+
+ if (file->f_mode & 2) return -EROFS; /* wants to write ? */
+
+ MOD_INC_USE_COUNT;
+
+ if (pcd_check_media(unit)) {
+ MOD_DEC_USE_COUNT;
+ return -ENXIO;
+ }
+
+ pcd_lock(unit);
+
+ PCD.access++;
+ return 0;
+}
+
+static void do_pcd_request (void)
+
+{ int unit;
+
+ if (pcd_busy) return;
+ while (1) {
+ if ((!CURRENT) || (CURRENT->rq_status == RQ_INACTIVE)) return;
+ INIT_REQUEST;
+ if (CURRENT->cmd == READ) {
+ unit = MINOR(CURRENT->rq_dev);
+ if (unit != pcd_unit) {
+ pcd_bufblk = -1;
+ pcd_unit = unit;
+ }
+ pcd_sector = CURRENT->sector;
+ pcd_count = CURRENT->nr_sectors;
+ pcd_buf = CURRENT->buffer;
+ do_pcd_read(unit);
+ if (pcd_busy) return;
+ }
+ else end_request(0);
+ }
+}
+
+static int pcd_ioctl(struct inode *inode,struct file *file,
+ unsigned int cmd, unsigned long arg)
+
+/* we currently support only the EJECT ioctl. */
+
+{ int unit = DEVICE_NR(inode->i_rdev);
+ if ((unit >= PCD_UNITS) || (!PCD.present)) return -ENODEV;
+
+ switch (cmd) {
+ case CDROMEJECT: if (PCD.access == 1) {
+ pcd_eject(unit);
+ return 0;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int pcd_release (struct inode *inode, struct file *file)
+
+{ kdev_t devp;
+ int unit;
+
+ struct super_block *sb;
+
+ devp = inode->i_rdev;
+ unit = DEVICE_NR(devp);
+
+ if ((unit >= PCD_UNITS) || (PCD.access <= 0))
+ return -EINVAL;
+
+ PCD.access--;
+
+ if (!PCD.access) {
+ fsync_dev(devp);
+
+ sb = get_super(devp);
+ if (sb) invalidate_inodes(sb);
+
+ invalidate_buffers(devp);
+ pcd_unlock(unit);
+
+ }
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+
+}
+
+#ifdef MODULE
+
+/* Glue for modules ... */
+
+int init_module(void)
+
+{ int err;
+ long flags;
+
+ save_flags(flags);
+ cli();
+
+ err = pcd_init();
+
+ restore_flags(flags);
+ return err;
+}
+
+void cleanup_module(void)
+
+{ long flags;
+ int unit;
+
+ save_flags(flags);
+ cli();
+ unregister_blkdev(MAJOR_NR,name);
+
+ for (unit=0;unit<PCD_UNITS;unit++)
+ if (PCD.present) pi_release(PI);
+
+ restore_flags(flags);
+}
+
+#endif
+
+#define WR(c,r,v) pi_write_regr(PI,c,r,v)
+#define RR(c,r) (pi_read_regr(PI,c,r))
+
+static int pcd_wait( int unit, int go, int stop, char * fun, char * msg )
+
+{ int j, r, e, s, p;
+
+ j = 0;
+ while ((((r=RR(1,6))&go)||(stop&&(!(r&stop))))&&(j++<PCD_SPIN))
+ udelay(PCD_DELAY);
+
+ if ((r&(IDE_ERR&stop))||(j>=PCD_SPIN)) {
+ s = RR(0,7);
+ e = RR(0,1);
+ p = RR(0,2);
+ if (j >= PCD_SPIN) e |= 0x100;
+ if (fun) printk("%s: %s %s: alt=0x%x stat=0x%x err=0x%x"
+ " loop=%d phase=%d\n",
+ PCD.name,fun,msg,r,s,e,j,p);
+ return (s<<8)+r;
+ }
+ return 0;
+}
+
+static int pcd_command( int unit, char * cmd, int dlen, char * fun )
+
+{ pi_connect(PI);
+
+ WR(0,6,0xa0 + 0x10*PCD.drive);
+
+ if (pcd_wait(unit,IDE_BUSY|IDE_DRQ,0,fun,"before command")) {
+ pi_disconnect(PI);
+ return -1;
+ }
+
+ WR(0,4,dlen % 256);
+ WR(0,5,dlen / 256);
+ WR(0,7,0xa0); /* ATAPI packet command */
+
+ if (pcd_wait(unit,IDE_BUSY,IDE_DRQ|IDE_ERR,fun,"command DRQ")) {
+ pi_disconnect(PI);
+ return -1;
+ }
+
+ if (RR(0,2) != 1) {
+ printk("%s: %s: command phase error\n",PCD.name,fun);
+ pi_disconnect(PI);
+ return -1;
+ }
+
+ pi_write_block(PI,cmd,12);
+
+ return 0;
+}
+
+static int pcd_completion( int unit, char * buf, char * fun )
+
+{ int r, s, n;
+
+ r = pcd_wait(unit,IDE_BUSY,IDE_DRQ|IDE_READY|IDE_ERR,fun,"completion");
+
+ if ((RR(0,2)&2) && (RR(0,7)&IDE_DRQ)) {
+ n = (RR(0,4)+256*RR(0,5));
+ pi_read_block(PI,buf,n);
+ }
+
+ s = pcd_wait(unit,IDE_BUSY,IDE_READY|IDE_ERR,fun,"data done");
+
+ pi_disconnect(PI);
+
+ return (r?r:s);
+}
+
+static void pcd_req_sense( int unit, int quiet )
+
+{ char rs_cmd[12] = { 0x03,0,0,0,16,0,0,0,0,0,0,0 };
+ char buf[16];
+ int r;
+
+ r = pcd_command(unit,rs_cmd,16,"Request sense");
+ udelay(1000);
+ if (!r) pcd_completion(unit,buf,"Request sense");
+
+ PCD.last_sense = -1;
+ if (!r) {
+ if (!quiet) printk("%s: Sense key: %x, ASC: %x, ASQ: %x\n",
+ PCD.name,buf[2]&0xf,buf[12],buf[13]);
+ PCD.last_sense = (buf[2]&0xf) | ((buf[12]&0xff)<<8)
+ | ((buf[13]&0xff)<<16) ;
+ }
+}
+
+static int pcd_atapi( int unit, char * cmd, int dlen, char * buf, char * fun )
+
+{ int r;
+
+ r = pcd_command(unit,cmd,dlen,fun);
+ udelay(1000);
+ if (!r) r = pcd_completion(unit,buf,fun);
+ if (r) pcd_req_sense(unit,!fun);
+
+ return r;
+}
+
+#define DBMSG(msg) NULL
+
+static void pcd_lock(int unit)
+
+{ char lo_cmd[12] = { 0x1e,0,0,0,1,0,0,0,0,0,0,0 };
+ char cl_cmd[12] = { 0x1b,0,0,0,3,0,0,0,0,0,0,0 };
+
+ pcd_atapi(unit,cl_cmd,0,pcd_scratch,DBMSG("cd1"));
+ pcd_atapi(unit,cl_cmd,0,pcd_scratch,DBMSG("cd2"));
+ pcd_atapi(unit,cl_cmd,0,pcd_scratch,DBMSG("cd3"));
+ pcd_atapi(unit,cl_cmd,0,pcd_scratch,DBMSG("cd4"));
+ pcd_atapi(unit,cl_cmd,0,pcd_scratch,"close door");
+
+ pcd_atapi(unit,lo_cmd,0,pcd_scratch,DBMSG("ld"));
+ pcd_atapi(unit,lo_cmd,0,pcd_scratch,"lock door");
+}
+
+static void pcd_unlock( int unit )
+
+{ char un_cmd[12] = { 0x1e,0,0,0,0,0,0,0,0,0,0,0 };
+
+ pcd_atapi(unit,un_cmd,0,pcd_scratch,"unlock door");
+}
+
+static void pcd_eject( int unit)
+
+{ char ej_cmd[12] = { 0x1b,0,0,0,2,0,0,0,0,0,0,0 };
+
+ pcd_unlock(unit);
+ pcd_atapi(unit,ej_cmd,0,pcd_scratch,"eject");
+}
+
+#define PCD_RESET_TMO 30 /* in tenths of a second */
+
+static void pcd_sleep( int cs )
+
+{ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + cs;
+ schedule();
+}
+
+static int pcd_reset( int unit )
+
+/* the ATAPI standard actually specifies the contents of all 7 registers
+ after a reset, but the specification is ambiguous concerning the last
+ two bytes, and different drives interpret the standard differently.
+*/
+
+{ int i, k, flg;
+ int expect[5] = {1,1,1,0x14,0xeb};
+ long flags;
+
+ pi_connect(PI);
+ WR(0,6,0xa0 + 0x10*PCD.drive);
+ WR(0,7,8);
+
+ save_flags(flags);
+ sti();
+
+ pcd_sleep(2); /* delay a bit*/
+
+ k = 0;
+ while ((k++ < PCD_RESET_TMO) && (RR(1,6)&IDE_BUSY))
+ pcd_sleep(10);
+
+ restore_flags(flags);
+
+ flg = 1;
+ for(i=0;i<5;i++) flg &= (RR(0,i+1) == expect[i]);
+
+ if (verbose) {
+ printk("%s: Reset (%d) signature = ",PCD.name,k);
+ for (i=0;i<5;i++) printk("%3x",RR(0,i+1));
+ if (!flg) printk(" (incorrect)");
+ printk("\n");
+ }
+
+ pi_disconnect(PI);
+ return flg-1;
+}
+
+static int pcd_ready_wait( int unit, int tmo )
+
+{ char tr_cmd[12] = {0,0,0,0,0,0,0,0,0,0,0,0};
+ int k, p;
+
+ k = 0;
+ while (k < tmo) {
+ PCD.last_sense = 0;
+ pcd_atapi(unit,tr_cmd,0,NULL,DBMSG("test unit ready"));
+ p = PCD.last_sense;
+ if (!p) return 0;
+ if (!((p == 0x010402)||((p & 0xff) == 6))) return p;
+ k++;
+ pcd_sleep(100);
+ }
+ return 0x000020; /* timeout */
+}
+
+static int pcd_check_media( int unit )
+
+{ char rc_cmd[12] = { 0x25,0,0,0,0,0,0,0,0,0,0,0};
+
+ pcd_ready_wait(unit,PCD_READY_TMO);
+ return (pcd_atapi(unit,rc_cmd,8,pcd_scratch,DBMSG("check media")));
+}
+
+static int pcd_identify( int unit, char * id )
+
+{ int k, s;
+ char id_cmd[12] = {0x12,0,0,0,36,0,0,0,0,0,0,0};
+
+ pcd_bufblk = -1;
+
+ s = pcd_atapi(unit,id_cmd,36,pcd_buffer,"identify");
+
+ if (s) return -1;
+ if ((pcd_buffer[0] & 0x1f) != 5) {
+ if (verbose) printk("%s: %s is not a CDrom\n",
+ PCD.name,PCD.drive?"Slave":"Master");
+ return -1;
+ }
+ for (k=0;k<16;k++) id[k] = pcd_buffer[16+k]; id[16] = 0;
+ k = 16; while ((k >= 0) && (id[k] <= 0x20)) { id[k] = 0; k--; }
+
+ printk("%s: %s: %s\n",PCD.name,PCD.drive?"Slave":"Master",id);
+
+ return 0;
+}
+
+static int pcd_probe( int unit, int ms, char * id )
+
+/* returns 0, with id set if drive is detected
+ -1, if drive detection failed
+*/
+
+{ if (ms == -1) {
+ for (PCD.drive=0;PCD.drive<=1;PCD.drive++)
+ if (!pcd_reset(unit) && !pcd_identify(unit,id))
+ return 0;
+ } else {
+ PCD.drive = ms;
+ if (!pcd_reset(unit) && !pcd_identify(unit,id))
+ return 0;
+ }
+ return -1;
+}
+
+static int pcd_detect( void )
+
+{ char id[18];
+ int k, unit;
+
+ printk("%s: %s version %s, major %d, nice %d\n",
+ name,name,PCD_VERSION,major,nice);
+
+ k = 0;
+ if (pcd_drive_count == 0) { /* nothing spec'd - so autoprobe for 1 */
+ unit = 0;
+ if (pi_init(PI,1,-1,-1,-1,-1,-1,pcd_buffer,
+ PI_PCD,verbose,PCD.name)) {
+ if (!pcd_probe(unit,-1,id)) {
+ PCD.present = 1;
+ k++;
+ } else pi_release(PI);
+ }
+
+ } else for (unit=0;unit<PCD_UNITS;unit++) if (DU[D_PRT])
+ if (pi_init(PI,0,DU[D_PRT],DU[D_MOD],DU[D_UNI],
+ DU[D_PRO],DU[D_DLY],pcd_buffer,PI_PCD,verbose,
+ PCD.name)) {
+ if (!pcd_probe(unit,DU[D_SLV],id)) {
+ PCD.present = 1;
+ k++;
+ } else pi_release(PI);
+ }
+
+ if (k) return 0;
+
+ printk("%s: No CDrom drive found\n",name);
+ return -1;
+}
+
+/* I/O request processing */
+
+static int pcd_ready( void )
+
+{ int unit = pcd_unit;
+
+ return (((RR(1,6)&(IDE_BUSY|IDE_DRQ))==IDE_DRQ)) ;
+}
+
+static void pcd_transfer( void )
+
+{ int k, o;
+
+ while (pcd_count && (pcd_sector/4 == pcd_bufblk)) {
+ o = (pcd_sector % 4) * 512;
+ for(k=0;k<512;k++) pcd_buf[k] = pcd_buffer[o+k];
+ pcd_count--;
+ pcd_buf += 512;
+ pcd_sector++;
+ }
+}
+
+static void pcd_start( void )
+
+{ int unit = pcd_unit;
+ int b, i;
+ char rd_cmd[12] = {0xa8,0,0,0,0,0,0,0,0,1,0,0};
+
+ pcd_bufblk = pcd_sector / 4;
+ b = pcd_bufblk;
+ for(i=0;i<4;i++) {
+ rd_cmd[5-i] = b & 0xff;
+ b = b >> 8;
+ }
+
+
+ if (pcd_command(unit,rd_cmd,2048,"read block")) {
+ pcd_bufblk = -1;
+ pcd_busy = 0;
+ cli();
+ end_request(0);
+ do_pcd_request();
+ return;
+ }
+
+ udelay(1000);
+
+ ps_set_intr(do_pcd_read_drq,pcd_ready,PCD_TMO,nice);
+
+}
+
+static void do_pcd_read( int unit )
+
+{ pcd_busy = 1;
+ pcd_retries = 0;
+ pcd_transfer();
+ if (!pcd_count) {
+ end_request(1);
+ pcd_busy = 0;
+ return;
+ }
+ sti();
+
+ pi_do_claimed(PI,pcd_start);
+}
+
+static void do_pcd_read_drq( void )
+
+{ int unit = pcd_unit;
+
+ sti();
+
+ if (pcd_completion(unit,pcd_buffer,"read block")) {
+ if (pcd_retries < PCD_RETRIES) {
+ udelay(1000);
+ pcd_retries++;
+ pi_do_claimed(PI,pcd_start);
+ return;
+ }
+ cli();
+ pcd_busy = 0;
+ pcd_bufblk = -1;
+ end_request(0);
+ do_pcd_request();
+ return;
+ }
+
+ do_pcd_read(unit);
+ do_pcd_request();
+}
+
+/* end of pcd.c */
diff --git a/drivers/block/paride/pd.c b/drivers/block/paride/pd.c
new file mode 100644
index 000000000..aa431ae33
--- /dev/null
+++ b/drivers/block/paride/pd.c
@@ -0,0 +1,1088 @@
+/*
+ pd.c (c) 1997 Grant R. Guenther <grant@torque.net>
+ Under the terms of the GNU public license.
+
+ This is the high-level driver for parallel port IDE hard
+ drives based on chips supported by the paride module.
+
+ By default, the driver will autoprobe for a single parallel
+ port IDE drive, but if their individual parameters are
+ specified, the driver can handle up to 4 drives.
+
+ The behaviour of the pd driver can be altered by setting
+ some parameters from the insmod command line. The following
+ parameters are adjustable:
+
+ drive0 These four arguments can be arrays of
+ drive1 1-7 integers as follows:
+ drive2
+ drive3 <prt>,<pro>,<uni>,<mod>,<geo>,<sby>,<dly>
+
+ Where,
+
+ <prt> is the base of the parallel port address for
+ the corresponding drive. (required)
+
+ <pro> is the protocol number for the adapter that
+ supports this drive. These numbers are
+ logged by 'paride' when the protocol modules
+ are initialised. (0 if not given)
+
+ <uni> for those adapters that support chained
+ devices, this is the unit selector for the
+ chain of devices on the given port. It should
+ be zero for devices that don't support chaining.
+ (0 if not given)
+
+ <mod> this can be -1 to choose the best mode, or one
+ of the mode numbers supported by the adapter.
+ (-1 if not given)
+
+ <geo> this defaults to 0 to indicate that the driver
+ should use the CHS geometry provided by the drive
+ itself. If set to 1, the driver will provide
+ a logical geometry with 64 heads and 32 sectors
+ per track, to be consistent with most SCSI
+ drivers. (0 if not given)
+
+ <sby> set this to zero to disable the power saving
+ standby mode, if needed. (1 if not given)
+
+ <dly> some parallel ports require the driver to
+ go more slowly. -1 sets a default value that
+ should work with the chosen protocol. Otherwise,
+ set this to a small integer, the larger it is
+ the slower the port i/o. In some cases, setting
+ this to zero will speed up the device. (default -1)
+
+
+ major You may use this parameter to overide the
+ default major number (45) that this driver
+ will use. Be sure to change the device
+ name as well.
+
+ name This parameter is a character string that
+ contains the name the kernel will use for this
+ device (in /proc output, for instance).
+ (default "pd")
+
+ cluster The driver will attempt to aggregate requests
+ for adjacent blocks into larger multi-block
+ clusters. The maximum cluster size (in 512
+ byte sectors) is set with this parameter.
+ (default 64)
+
+ verbose This parameter controls the amount of logging
+ that is done while the driver probes for
+ devices. Set it to 0 for a quiet load, or to 1
+ see all the progress messages. (default 0)
+
+ nice This parameter controls the driver's use of
+ idle CPU time, at the expense of some speed.
+
+ If this driver is built into the kernel, you can use kernel
+ the following command line parameters, with the same values
+ as the corresponding module parameters listed above:
+
+ pd.drive0
+ pd.drive1
+ pd.drive2
+ pd.drive3
+ pd.cluster
+ pd.nice
+
+ In addition, you can use the parameter pd.disable to disable
+ the driver entirely.
+
+*/
+
+/* Changes:
+
+ 1.01 GRG 1997.01.24 Restored pd_reset()
+ Added eject ioctl
+
+*/
+
+#define PD_VERSION "1.01"
+#define PD_MAJOR 45
+#define PD_NAME "pd"
+#define PD_UNITS 4
+
+/* Here are things one can override from the insmod command.
+ Most are autoprobed by paride unless set here. Verbose is on
+ by default.
+
+*/
+
+static int verbose = 0;
+static int major = PD_MAJOR;
+static char *name = PD_NAME;
+static int cluster = 64;
+static int nice = 0;
+static int disable = 0;
+
+static int drive0[7] = {0,0,0,-1,0,1,-1};
+static int drive1[7] = {0,0,0,-1,0,1,-1};
+static int drive2[7] = {0,0,0,-1,0,1,-1};
+static int drive3[7] = {0,0,0,-1,0,1,-1};
+
+static int (*drives[4])[7] = {&drive0,&drive1,&drive2,&drive3};
+static int pd_drive_count;
+
+#define D_PRT 0
+#define D_PRO 1
+#define D_UNI 2
+#define D_MOD 3
+#define D_GEO 4
+#define D_SBY 5
+#define D_DLY 6
+
+#define DU (*drives[unit])
+
+/* end of parameters */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/genhd.h>
+#include <linux/hdreg.h>
+#include <linux/cdrom.h> /* for the eject ioctl */
+
+#include <asm/uaccess.h>
+
+#ifndef MODULE
+
+#include "setup.h"
+
+static STT pd_stt[7] = {{"drive0",7,drive0},
+ {"drive1",7,drive1},
+ {"drive2",7,drive2},
+ {"drive3",7,drive3},
+ {"disable",1,&disable},
+ {"cluster",1,&cluster},
+ {"nice",1,&nice}};
+
+void pd_setup( char *str, int *ints)
+
+{ generic_setup(pd_stt,7,str);
+}
+
+#endif
+
+MODULE_PARM(verbose,"i");
+MODULE_PARM(major,"i");
+MODULE_PARM(name,"s");
+MODULE_PARM(cluster,"i");
+MODULE_PARM(nice,"i");
+MODULE_PARM(drive0,"1-7i");
+MODULE_PARM(drive1,"1-7i");
+MODULE_PARM(drive2,"1-7i");
+MODULE_PARM(drive3,"1-7i");
+
+#include "paride.h"
+
+#define PD_BITS 4
+
+/* set up defines for blk.h, why don't all drivers do it this way ? */
+
+#define MAJOR_NR major
+#define DEVICE_NAME "PD"
+#define DEVICE_REQUEST do_pd_request
+#define DEVICE_NR(device) (MINOR(device)>>PD_BITS)
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#include <linux/blk.h>
+
+#include "pseudo.h"
+
+#define PD_PARTNS (1<<PD_BITS)
+#define PD_DEVS PD_PARTNS*PD_UNITS
+
+/* numbers for "SCSI" geometry */
+
+#define PD_LOG_HEADS 64
+#define PD_LOG_SECTS 32
+
+#define PD_ID_OFF 54
+#define PD_ID_LEN 14
+
+#define PD_MAX_RETRIES 5
+#define PD_TMO 800 /* interrupt timeout in jiffies */
+#define PD_SPIN_DEL 50 /* spin delay in micro-seconds */
+
+#define PD_SPIN (10000/PD_SPIN_DEL)*PD_TMO
+
+#define STAT_ERR 0x00001
+#define STAT_INDEX 0x00002
+#define STAT_ECC 0x00004
+#define STAT_DRQ 0x00008
+#define STAT_SEEK 0x00010
+#define STAT_WRERR 0x00020
+#define STAT_READY 0x00040
+#define STAT_BUSY 0x00080
+
+#define ERR_AMNF 0x00100
+#define ERR_TK0NF 0x00200
+#define ERR_ABRT 0x00400
+#define ERR_MCR 0x00800
+#define ERR_IDNF 0x01000
+#define ERR_MC 0x02000
+#define ERR_UNC 0x04000
+#define ERR_TMO 0x10000
+
+#define IDE_READ 0x20
+#define IDE_WRITE 0x30
+#define IDE_READ_VRFY 0x40
+#define IDE_INIT_DEV_PARMS 0x91
+#define IDE_STANDBY 0x96
+#define IDE_ACKCHANGE 0xdb
+#define IDE_DOORLOCK 0xde
+#define IDE_DOORUNLOCK 0xdf
+#define IDE_IDENTIFY 0xec
+#define IDE_EJECT 0xed
+
+int pd_init(void);
+void pd_setup(char * str, int * ints);
+#ifdef MODULE
+void cleanup_module( void );
+#endif
+static void pd_geninit(struct gendisk *ignored);
+static int pd_open(struct inode *inode, struct file *file);
+static void do_pd_request(void);
+static int pd_ioctl(struct inode *inode,struct file *file,
+ unsigned int cmd, unsigned long arg);
+static int pd_release (struct inode *inode, struct file *file);
+static int pd_revalidate(kdev_t dev);
+static int pd_detect(void);
+static void do_pd_read(void);
+static void do_pd_write(void);
+static void do_pd_read_drq( void );
+static void do_pd_write_done( void );
+
+static int pd_identify (int unit);
+static void pd_media_check(int unit);
+static void pd_doorlock(int unit, int func);
+static int pd_check_media(kdev_t dev);
+static void pd_eject( int unit);
+
+static struct hd_struct pd_hd[PD_DEVS];
+static int pd_sizes[PD_DEVS];
+static int pd_blocksizes[PD_DEVS];
+
+#define PD_NAMELEN 8
+
+struct pd_unit {
+ struct pi_adapter pia; /* interface to paride layer */
+ struct pi_adapter *pi;
+ int access; /* count of active opens ... */
+ int capacity; /* Size of this volume in sectors */
+ int heads; /* physical geometry */
+ int sectors;
+ int cylinders;
+ int changed; /* Have we seen a disk change ? */
+ int removable; /* removable media device ? */
+ int standby;
+ int alt_geom;
+ int present;
+ char name[PD_NAMELEN]; /* pda, pdb, etc ... */
+ };
+
+struct pd_unit pd[PD_UNITS];
+
+/* 'unit' must be defined in all functions - either as a local or a param */
+
+#define PD pd[unit]
+#define PI PD.pi
+
+static int pd_valid = 1; /* serialise partition checks */
+static char pd_scratch[512]; /* scratch block buffer */
+
+/* the variables below are used mainly in the I/O request engine, which
+ processes only one request at a time.
+*/
+
+static int pd_retries = 0; /* i/o error retry count */
+static int pd_busy = 0; /* request being processed ? */
+static int pd_block; /* address of next requested block */
+static int pd_count; /* number of blocks still to do */
+static int pd_run; /* sectors in current cluster */
+static int pd_cmd; /* current command READ/WRITE */
+static int pd_unit; /* unit of current request */
+static int pd_dev; /* minor of current request */
+static int pd_poffs; /* partition offset of current minor */
+static char * pd_buf; /* buffer for request in progress */
+
+static struct wait_queue *pd_wait_open = NULL;
+
+static char *pd_errs[17] = { "ERR","INDEX","ECC","DRQ","SEEK","WRERR",
+ "READY","BUSY","AMNF","TK0NF","ABRT","MCR",
+ "IDNF","MC","UNC","???","TMO"};
+
+/* kernel glue structures */
+
+static struct gendisk pd_gendisk = {
+ PD_MAJOR, /* Major number */
+ PD_NAME, /* Major name */
+ PD_BITS, /* Bits to shift to get real from partition */
+ PD_PARTNS, /* Number of partitions per real */
+ PD_UNITS, /* maximum number of real */
+ pd_geninit, /* init function */
+ pd_hd, /* hd struct */
+ pd_sizes, /* block sizes */
+ 0, /* number */
+ NULL, /* internal */
+ NULL /* next */
+};
+
+static struct file_operations pd_fops = {
+ NULL, /* lseek - default */
+ block_read, /* read - general block-dev read */
+ block_write, /* write - general block-dev write */
+ NULL, /* readdir - bad */
+ NULL, /* select */
+ pd_ioctl, /* ioctl */
+ NULL, /* mmap */
+ pd_open, /* open */
+ pd_release, /* release */
+ block_fsync, /* fsync */
+ NULL, /* fasync */
+ pd_check_media, /* media change ? */
+ pd_revalidate /* revalidate new media */
+};
+
+void pd_init_units( void )
+
+{ int unit, j;
+
+ pd_drive_count = 0;
+ for (unit=0;unit<PD_UNITS;unit++) {
+ PD.pi = & PD.pia;
+ PD.access = 0;
+ PD.changed = 1;
+ PD.capacity = 0;
+ PD.present = 0;
+ j = 0;
+ while ((j < PD_NAMELEN-2) && (PD.name[j]=name[j])) j++;
+ PD.name[j++] = 'a' + unit;
+ PD.name[j] = 0;
+ PD.alt_geom = DU[D_GEO];
+ PD.standby = DU[D_SBY];
+ if (DU[D_PRT]) pd_drive_count++;
+ }
+}
+
+int pd_init (void)
+
+{ int i;
+
+ if (disable) return -1;
+
+ if (register_blkdev(MAJOR_NR,name,&pd_fops)) {
+ printk("%s: unable to get major number %d\n",
+ name,major);
+ return -1;
+ }
+ blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+ read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read ahead */
+
+ pd_gendisk.major = major;
+ pd_gendisk.major_name = name;
+ pd_gendisk.next = gendisk_head;
+ gendisk_head = &pd_gendisk;
+
+ for(i=0;i<PD_DEVS;i++) pd_blocksizes[i] = 1024;
+ blksize_size[MAJOR_NR] = pd_blocksizes;
+
+ printk("%s: %s version %s, major %d, cluster %d, nice %d\n",
+ name,name,PD_VERSION,major,cluster,nice);
+
+ return 0;
+}
+
+static void pd_geninit (struct gendisk *ignored)
+
+{ pd_init_units();
+ pd_gendisk.nr_real = pd_detect();
+
+#ifdef MODULE
+ if (!pd_gendisk.nr_real) cleanup_module();
+#endif
+
+}
+
+static int pd_open (struct inode *inode, struct file *file)
+
+{ int unit = DEVICE_NR(inode->i_rdev);
+
+ if ((unit >= PD_UNITS) || (!PD.present)) return -ENODEV;
+
+ MOD_INC_USE_COUNT;
+
+ while (!pd_valid) sleep_on(&pd_wait_open);
+
+ PD.access++;
+
+ if (PD.removable) {
+ pd_media_check(unit);
+ pd_doorlock(unit,IDE_DOORLOCK);
+ }
+ return 0;
+}
+
+static int pd_ioctl(struct inode *inode,struct file *file,
+ unsigned int cmd, unsigned long arg)
+
+{ struct hd_geometry *geo = (struct hd_geometry *) arg;
+ int dev, err, unit;
+
+ if ((!inode) || (!inode->i_rdev)) return -EINVAL;
+ dev = MINOR(inode->i_rdev);
+ unit = DEVICE_NR(inode->i_rdev);
+ if (dev >= PD_DEVS) return -EINVAL;
+ if (!PD.present) return -ENODEV;
+
+ switch (cmd) {
+ case CDROMEJECT:
+ if (PD.access == 1) pd_eject(unit);
+ return 0;
+ case HDIO_GETGEO:
+ if (!geo) return -EINVAL;
+ err = verify_area(VERIFY_WRITE,geo,sizeof(*geo));
+ if (err) return err;
+
+ if (PD.alt_geom) {
+ put_user(PD.capacity/(PD_LOG_HEADS*PD_LOG_SECTS),
+ (short *) &geo->cylinders);
+ put_user(PD_LOG_HEADS, (char *) &geo->heads);
+ put_user(PD_LOG_SECTS, (char *) &geo->sectors);
+ } else {
+ put_user(PD.cylinders, (short *) &geo->cylinders);
+ put_user(PD.heads, (char *) &geo->heads);
+ put_user(PD.sectors, (char *) &geo->sectors);
+ }
+ put_user(pd_hd[dev].start_sect,(long *)&geo->start);
+ return 0;
+ case BLKRASET:
+ if(!suser()) return -EACCES;
+ if(!(inode->i_rdev)) return -EINVAL;
+ if(arg > 0xff) return -EINVAL;
+ read_ahead[MAJOR(inode->i_rdev)] = arg;
+ return 0;
+ case BLKRAGET:
+ if (!arg) return -EINVAL;
+ err = verify_area(VERIFY_WRITE,(long *) arg,sizeof(long));
+ if (err) return (err);
+ put_user(read_ahead[MAJOR(inode->i_rdev)],(long *) arg);
+ return (0);
+ case BLKGETSIZE:
+ if (!arg) return -EINVAL;
+ err = verify_area(VERIFY_WRITE,(long *) arg,sizeof(long));
+ if (err) return (err);
+ put_user(pd_hd[dev].nr_sects,(long *) arg);
+ return (0);
+ case BLKFLSBUF:
+ if(!suser()) return -EACCES;
+ if(!(inode->i_rdev)) return -EINVAL;
+ fsync_dev(inode->i_rdev);
+ invalidate_buffers(inode->i_rdev);
+ return 0;
+ case BLKRRPART:
+ return pd_revalidate(inode->i_rdev);
+ RO_IOCTLS(inode->i_rdev,arg);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int pd_release (struct inode *inode, struct file *file)
+
+{ kdev_t devp;
+ int unit;
+
+ struct super_block *sb;
+
+ devp = inode->i_rdev;
+ unit = DEVICE_NR(devp);
+
+ if ((unit >= PD_UNITS) || (PD.access <= 0))
+ return -EINVAL;
+
+ PD.access--;
+
+ if (!PD.access) {
+ fsync_dev(devp);
+
+ sb = get_super(devp);
+ if (sb) invalidate_inodes(sb);
+
+ invalidate_buffers(devp);
+ if (PD.removable) pd_doorlock(unit,IDE_DOORUNLOCK);
+ }
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+}
+
+static int pd_check_media( kdev_t dev)
+
+{ int r, unit;
+
+ unit = DEVICE_NR(dev);
+ if ((unit >= PD_UNITS) || (!PD.present)) return -ENODEV;
+ if (!PD.removable) return 0;
+ pd_media_check(unit);
+ r = PD.changed;
+ PD.changed = 0;
+ return r;
+}
+
+static int pd_revalidate(kdev_t dev)
+
+{ int p, unit, minor;
+ long flags;
+ kdev_t devp;
+
+ struct super_block *sb;
+
+ unit = DEVICE_NR(dev);
+ if ((unit >= PD_UNITS) || (!PD.present)) return -ENODEV;
+
+ save_flags(flags);
+ cli();
+ if (PD.access > 1) {
+ restore_flags(flags);
+ return -EBUSY;
+ }
+ pd_valid = 0;
+ restore_flags(flags);
+
+ for (p=(PD_PARTNS-1);p>=0;p--) {
+ minor = p + unit*PD_PARTNS;
+ devp = MKDEV(MAJOR_NR, minor);
+ fsync_dev(devp);
+
+ sb = get_super(devp);
+ if (sb) invalidate_inodes(sb);
+
+ invalidate_buffers(devp);
+ pd_hd[minor].start_sect = 0;
+ pd_hd[minor].nr_sects = 0;
+ }
+
+ pd_identify(unit);
+ resetup_one_dev(&pd_gendisk,unit);
+
+ pd_valid = 1;
+ wake_up(&pd_wait_open);
+
+ return 0;
+}
+
+#ifdef MODULE
+
+/* Glue for modules ... */
+
+void cleanup_module(void);
+
+int init_module(void)
+
+{ int err, unit;
+ long flags;
+
+ save_flags(flags);
+ cli();
+
+ err = pd_init();
+ if (err) {
+ restore_flags(flags);
+ return err;
+ }
+
+ pd_geninit(&pd_gendisk);
+
+ if (!pd_gendisk.nr_real) {
+ restore_flags(flags);
+ return -1;
+ }
+
+ pd_valid = 0;
+ for (unit=0;unit<PD_UNITS;unit++)
+ if (PD.present) resetup_one_dev(&pd_gendisk,unit);
+ pd_valid = 1;
+
+ restore_flags(flags);
+ return 0;
+}
+
+void cleanup_module(void)
+
+{ struct gendisk **gdp;
+ long flags;
+ int unit;
+
+ save_flags(flags);
+ cli();
+
+ unregister_blkdev(MAJOR_NR,name);
+
+ for(gdp=&gendisk_head;*gdp;gdp=&((*gdp)->next))
+ if (*gdp == &pd_gendisk) break;
+ if (*gdp) *gdp = (*gdp)->next;
+
+ for (unit=0;unit<PD_UNITS;unit++)
+ if (PD.present) pi_release(PI);
+
+ restore_flags(flags);
+}
+
+#endif
+
+#define WR(c,r,v) pi_write_regr(PI,c,r,v)
+#define RR(c,r) (pi_read_regr(PI,c,r))
+
+/* ide command interface */
+
+static void pd_print_error( int unit, char * msg, int status )
+
+{ int i;
+
+ printk("%s: %s: status = 0x%x =",PD.name,msg,status);
+ for(i=0;i<18;i++) if (status & (1<<i)) printk(" %s",pd_errs[i]);
+ printk("\n");
+}
+
+static void pd_reset( int unit )
+
+{ pi_connect(PI);
+ WR(1,6,4);
+ udelay(50);
+ WR(1,6,0);
+ pi_disconnect(PI);
+ udelay(250);
+}
+
+#define DBMSG(msg) NULL
+
+static int pd_wait_for( int unit, int w, char * msg ) /* polled wait */
+
+{ int k, r, e;
+
+ k=0;
+ while(k < PD_SPIN) {
+ r = RR(1,6);
+ k++;
+ if (((r & w) == w) && !(r & STAT_BUSY)) break;
+ udelay(PD_SPIN_DEL);
+ }
+ e = (RR(0,1)<<8) + RR(0,7);
+ if (k >= PD_SPIN) e |= ERR_TMO;
+ if ((e & (STAT_ERR|ERR_TMO)) && (msg != NULL))
+ pd_print_error(unit,msg,e);
+ return e;
+}
+
+static void pd_send_command( int unit, int n, int s, int h,
+ int c0, int c1, int func )
+
+{
+ WR(0,6,0xa0+h);
+ WR(0,1,0); /* the IDE task file */
+ WR(0,2,n);
+ WR(0,3,s);
+ WR(0,4,c0);
+ WR(0,5,c1);
+ WR(0,7,func);
+
+ udelay(1);
+}
+
+static void pd_ide_command( int unit, int func, int block, int count )
+
+/* Don't use this call if the capacity is zero. */
+
+{ int c1, c0, h, s;
+
+ s = ( block % PD.sectors) + 1;
+ h = ( block / PD.sectors) % PD.heads;
+ c0 = ( block / (PD.sectors*PD.heads)) % 256;
+ c1 = ( block / (PD.sectors*PD.heads*256));
+
+ pd_send_command(unit,count,s,h,c0,c1,func);
+}
+
+/* According to the ATA standard, the default CHS geometry should be
+ available following a reset. Some Western Digital drives come up
+ in a mode where only LBA addresses are accepted until the device
+ parameters are initialised.
+*/
+
+static void pd_init_dev_parms( int unit )
+
+{ pi_connect(PI);
+ pd_wait_for(unit,0,DBMSG("before init_dev_parms"));
+ pd_send_command(unit,PD.sectors,0,PD.heads-1,0,0,IDE_INIT_DEV_PARMS);
+ udelay(300);
+ pd_wait_for(unit,0,"Initialise device parameters");
+ pi_disconnect(PI);
+}
+
+static void pd_doorlock( int unit, int func )
+
+{ pi_connect(PI);
+ if (pd_wait_for(unit,STAT_READY,"Lock") & STAT_ERR) {
+ pi_disconnect(PI);
+ return;
+ }
+ pd_send_command(unit,1,0,0,0,0,func);
+ pd_wait_for(unit,STAT_READY,"Lock done");
+ pi_disconnect(PI);
+}
+
+static void pd_eject( int unit )
+
+{ pi_connect(PI);
+ pd_wait_for(unit,0,DBMSG("before unlock on eject"));
+ pd_send_command(unit,1,0,0,0,0,IDE_DOORUNLOCK);
+ pd_wait_for(unit,0,DBMSG("after unlock on eject"));
+ pd_wait_for(unit,0,DBMSG("before eject"));
+ pd_send_command(unit,0,0,0,0,0,IDE_EJECT);
+ pd_wait_for(unit,0,DBMSG("after eject"));
+ pi_disconnect(PI);
+}
+
+static void pd_media_check( int unit )
+
+{ int r;
+
+ pi_connect(PI);
+ r = pd_wait_for(unit,STAT_READY,DBMSG("before media_check"));
+ if (!(r & STAT_ERR)) {
+ pd_send_command(unit,1,1,0,0,0,IDE_READ_VRFY);
+ r = pd_wait_for(unit,STAT_READY,DBMSG("RDY after READ_VRFY"));
+ } else PD.changed = 1; /* say changed if other error */
+ if (r & ERR_MC) {
+ PD.changed = 1;
+ pd_send_command(unit,1,0,0,0,0,IDE_ACKCHANGE);
+ pd_wait_for(unit,STAT_READY,DBMSG("RDY after ACKCHANGE"));
+ pd_send_command(unit,1,1,0,0,0,IDE_READ_VRFY);
+ r = pd_wait_for(unit,STAT_READY,DBMSG("RDY after VRFY"));
+ }
+ pi_disconnect(PI);
+
+}
+
+static void pd_standby_off( int unit )
+
+{ pi_connect(PI);
+ pd_wait_for(unit,0,DBMSG("before STANDBY"));
+ pd_send_command(unit,0,0,0,0,0,IDE_STANDBY);
+ pd_wait_for(unit,0,DBMSG("after STANDBY"));
+ pi_disconnect(PI);
+}
+
+#define word_val(n) ((pd_scratch[2*n]&0xff)+256*(pd_scratch[2*n+1]&0xff))
+
+static int pd_identify( int unit )
+
+{ int j;
+ char id[PD_ID_LEN+1];
+
+ pd_reset(unit);
+
+ pi_connect(PI);
+ WR(0,6,0xa0);
+ pd_wait_for(unit,0,DBMSG("before IDENT"));
+ pd_send_command(unit,1,0,0,0,0,IDE_IDENTIFY);
+
+ if (pd_wait_for(unit,STAT_DRQ,DBMSG("IDENT DRQ")) & STAT_ERR) {
+ pi_disconnect(PI);
+ return 0;
+ }
+ pi_read_block(PI,pd_scratch,512);
+ pi_disconnect(PI);
+ PD.sectors = word_val(6);
+ PD.heads = word_val(3);
+ PD.cylinders = word_val(1);
+ PD.capacity = PD.sectors*PD.heads*PD.cylinders;
+
+ for(j=0;j<PD_ID_LEN;j++) id[j^1] = pd_scratch[j+PD_ID_OFF];
+ j = PD_ID_LEN-1;
+ while ((j >= 0) && (id[j] <= 0x20)) j--;
+ j++; id[j] = 0;
+
+ PD.removable = (word_val(0) & 0x80);
+
+ printk("%s: %s, %d blocks [%dM], (%d/%d/%d), %s media\n",
+ PD.name,id,PD.capacity,PD.capacity/2048,
+ PD.cylinders,PD.heads,PD.sectors,
+ PD.removable?"removable":"fixed");
+
+ if (PD.capacity) pd_init_dev_parms(unit);
+ if (!PD.standby) pd_standby_off(unit);
+
+ pd_hd[unit<<PD_BITS].nr_sects = PD.capacity;
+ pd_hd[unit<<PD_BITS].start_sect = 0;
+
+ return 1;
+}
+
+static int pd_detect( void )
+
+{ long flags;
+ int k, unit;
+
+ k = 0;
+ if (pd_drive_count == 0) { /* nothing spec'd - so autoprobe for 1 */
+ unit = 0;
+ if (pi_init(PI,1,-1,-1,-1,-1,-1,pd_scratch,
+ PI_PD,verbose,PD.name)) {
+ save_flags(flags);
+ sti();
+ if (pd_identify(unit)) {
+ PD.present = 1;
+ k = 1;
+ } else pi_release(PI);
+ restore_flags(flags);
+ }
+
+ } else for (unit=0;unit<PD_UNITS;unit++) if (DU[D_PRT])
+ if (pi_init(PI,0,DU[D_PRT],DU[D_MOD],DU[D_UNI],
+ DU[D_PRO],DU[D_DLY],pd_scratch,
+ PI_PD,verbose,PD.name)) {
+ save_flags(flags);
+ sti();
+ if (pd_identify(unit)) {
+ PD.present = 1;
+ k = unit+1;
+ } else pi_release(PI);
+ restore_flags(flags);
+ }
+
+/* We lie about the number of drives found, as the generic partition
+ scanner assumes that the drives are numbered sequentially from 0.
+ This can result in some bogus error messages if non-sequential
+ drive numbers are used.
+*/
+
+ if (k) return k;
+
+ printk("%s: no valid drive found\n",name);
+ return 0;
+}
+
+/* The i/o request engine */
+
+static int pd_ready( void )
+
+{ int unit = pd_unit;
+
+ return (!(RR(1,6) & STAT_BUSY)) ;
+}
+
+static void do_pd_request (void)
+
+{ struct buffer_head * bh;
+ struct request * req;
+ int unit;
+
+ if (pd_busy) return;
+repeat:
+ if ((!CURRENT) || (CURRENT->rq_status == RQ_INACTIVE)) return;
+ INIT_REQUEST;
+
+ pd_dev = MINOR(CURRENT->rq_dev);
+ pd_unit = unit = DEVICE_NR(CURRENT->rq_dev);
+ pd_block = CURRENT->sector;
+ pd_count = CURRENT->nr_sectors;
+
+ bh = CURRENT->bh;
+ req = CURRENT;
+ if (bh->b_reqnext)
+ printk("%s: OUCH: b_reqnext != NULL\n",PD.name);
+
+ if ((pd_dev >= PD_DEVS) ||
+ ((pd_block+pd_count) > pd_hd[pd_dev].nr_sects)) {
+ end_request(0);
+ goto repeat;
+ }
+
+ pd_cmd = CURRENT->cmd;
+ pd_run = pd_count;
+ while ((pd_run <= cluster) &&
+ (req = req->next) &&
+ (pd_block+pd_run == req->sector) &&
+ (pd_cmd == req->cmd) &&
+ (pd_dev == MINOR(req->rq_dev)))
+ pd_run += req->nr_sectors;
+
+ pd_poffs = pd_hd[pd_dev].start_sect;
+ pd_block += pd_poffs;
+ pd_buf = CURRENT->buffer;
+ pd_retries = 0;
+
+ if (pd_cmd == READ) pi_do_claimed(PI,do_pd_read);
+ else if (pd_cmd == WRITE) pi_do_claimed(PI,do_pd_write);
+ else { end_request(0);
+ goto repeat;
+ }
+}
+
+static void pd_next_buf( int unit )
+
+{ cli();
+ end_request(1);
+ if (!pd_run) { sti(); return; }
+
+/* paranoia */
+
+ if ((!CURRENT) ||
+ (CURRENT->cmd != pd_cmd) ||
+ (MINOR(CURRENT->rq_dev) != pd_dev) ||
+ (CURRENT->rq_status == RQ_INACTIVE) ||
+ (CURRENT->sector+pd_poffs != pd_block))
+ printk("%s: OUCH: request list changed unexpectedly\n",
+ PD.name);
+
+ pd_count = CURRENT->nr_sectors;
+ pd_buf = CURRENT->buffer;
+ sti();
+}
+
+static void do_pd_read( void )
+
+{ int unit = pd_unit;
+
+ pd_busy = 1;
+
+ sti();
+
+ pi_connect(PI);
+ if (pd_wait_for(unit,STAT_READY,"do_pd_read") & STAT_ERR) {
+ pi_disconnect(PI);
+ if (pd_retries < PD_MAX_RETRIES) {
+ pd_retries++;
+ pi_do_claimed(PI,do_pd_read);
+ return;
+ }
+ end_request(0);
+ pd_busy = 0;
+ cli();
+ do_pd_request();
+ return;
+ }
+ pd_ide_command(unit,IDE_READ,pd_block,pd_run);
+ ps_set_intr(do_pd_read_drq,pd_ready,PD_TMO,nice);
+}
+
+static void do_pd_read_drq( void )
+
+{ int unit = pd_unit;
+
+ sti();
+
+ while (1) {
+ if (pd_wait_for(unit,STAT_DRQ,"do_pd_read_drq") & STAT_ERR) {
+ pi_disconnect(PI);
+ if (pd_retries < PD_MAX_RETRIES) {
+ pd_retries++;
+ pi_do_claimed(PI,do_pd_read);
+ return;
+ }
+ end_request(0);
+ pd_busy = 0;
+ cli();
+ do_pd_request();
+ return;
+ }
+ pi_read_block(PI,pd_buf,512);
+ pd_count--; pd_run--;
+ pd_buf += 512;
+ pd_block++;
+ if (!pd_run) break;
+ if (!pd_count) pd_next_buf(unit);
+ }
+ pi_disconnect(PI);
+ end_request(1);
+ pd_busy = 0;
+ cli();
+ do_pd_request();
+}
+
+static void do_pd_write( void )
+
+{ int unit = pd_unit;
+
+ pd_busy = 1;
+
+ sti();
+
+ pi_connect(PI);
+ if (pd_wait_for(unit,STAT_READY,"do_pd_write") & STAT_ERR) {
+ pi_disconnect(PI);
+ if (pd_retries < PD_MAX_RETRIES) {
+ pd_retries++;
+ pi_do_claimed(PI,do_pd_write);
+ return;
+ }
+ end_request(0);
+ pd_busy = 0;
+ cli();
+ do_pd_request();
+ return;
+ }
+ pd_ide_command(unit,IDE_WRITE,pd_block,pd_run);
+ while (1) {
+ if (pd_wait_for(unit,STAT_DRQ,"do_pd_write_drq") & STAT_ERR) {
+ pi_disconnect(PI);
+ if (pd_retries < PD_MAX_RETRIES) {
+ pd_retries++;
+ pi_do_claimed(PI,do_pd_write);
+ return;
+ }
+ end_request(0);
+ pd_busy = 0;
+ cli();
+ do_pd_request();
+ return;
+ }
+ pi_write_block(PI,pd_buf,512);
+ pd_count--; pd_run--;
+ pd_buf += 512;
+ pd_block++;
+ if (!pd_run) break;
+ if (!pd_count) pd_next_buf(unit);
+ }
+ ps_set_intr(do_pd_write_done,pd_ready,PD_TMO,nice);
+}
+
+static void do_pd_write_done( void )
+
+{ int unit = pd_unit;
+
+ sti();
+ if (pd_wait_for(unit,STAT_READY,"do_pd_write_done") & STAT_ERR) {
+ pi_disconnect(PI);
+ if (pd_retries < PD_MAX_RETRIES) {
+ pd_retries++;
+ pi_do_claimed(PI,do_pd_write);
+ return;
+ }
+ end_request(0);
+ pd_busy = 0;
+ cli();
+ do_pd_request();
+ return;
+ }
+ pi_disconnect(PI);
+ end_request(1);
+ pd_busy = 0;
+ cli();
+ do_pd_request();
+}
+
+/* end of pd.c */
+
diff --git a/drivers/block/paride/pf.c b/drivers/block/paride/pf.c
new file mode 100644
index 000000000..00d629ddd
--- /dev/null
+++ b/drivers/block/paride/pf.c
@@ -0,0 +1,1072 @@
+/*
+ pf.c (c) 1997 Grant R. Guenther <grant@torque.net>
+ Under the terms of the GNU public license.
+
+ This is the high-level driver for parallel port ATAPI disk
+ drives based on chips supported by the paride module.
+
+ By default, the driver will autoprobe for a single parallel
+ port ATAPI disk drive, but if their individual parameters are
+ specified, the driver can handle up to 4 drives.
+
+ The behaviour of the pf driver can be altered by setting
+ some parameters from the insmod command line. The following
+ parameters are adjustable:
+
+ drive0 These four arguments can be arrays of
+ drive1 1-7 integers as follows:
+ drive2
+ drive3 <prt>,<pro>,<uni>,<mod>,<slv>,<lun>,<dly>
+
+ Where,
+
+ <prt> is the base of the parallel port address for
+ the corresponding drive. (required)
+
+ <pro> is the protocol number for the adapter that
+ supports this drive. These numbers are
+ logged by 'paride' when the protocol modules
+ are initialised. (0 if not given)
+
+ <uni> for those adapters that support chained
+ devices, this is the unit selector for the
+ chain of devices on the given port. It should
+ be zero for devices that don't support chaining.
+ (0 if not given)
+
+ <mod> this can be -1 to choose the best mode, or one
+ of the mode numbers supported by the adapter.
+ (-1 if not given)
+
+ <slv> ATAPI CDroms can be jumpered to master or slave.
+ Set this to 0 to choose the master drive, 1 to
+ choose the slave, -1 (the default) to choose the
+ first drive found.
+
+ <lun> Some ATAPI devices support multiple LUNs.
+ One example is the ATAPI PD/CD drive from
+ Matshita/Panasonic. This device has a
+ CD drive on LUN 0 and a PD drive on LUN 1.
+ By default, the driver will search for the
+ first LUN with a supported device. Set
+ this parameter to force it to use a specific
+ LUN. (default -1)
+
+ <dly> some parallel ports require the driver to
+ go more slowly. -1 sets a default value that
+ should work with the chosen protocol. Otherwise,
+ set this to a small integer, the larger it is
+ the slower the port i/o. In some cases, setting
+ this to zero will speed up the device. (default -1)
+
+ major You may use this parameter to overide the
+ default major number (47) that this driver
+ will use. Be sure to change the device
+ name as well.
+
+ name This parameter is a character string that
+ contains the name the kernel will use for this
+ device (in /proc output, for instance).
+ (default "pf").
+
+ cluster The driver will attempt to aggregate requests
+ for adjacent blocks into larger multi-block
+ clusters. The maximum cluster size (in 512
+ byte sectors) is set with this parameter.
+ (default 64)
+
+ verbose This parameter controls the amount of logging
+ that is done while the driver probes for
+ devices. Set it to 0 for a quiet load, or 1 to
+ see all the progress messages. (default 0)
+
+ nice This parameter controls the driver's use of
+ idle CPU time, at the expense of some speed.
+
+ If this driver is built into the kernel, you can use kernel
+ the following command line parameters, with the same values
+ as the corresponding module parameters listed above:
+
+ pf.drive0
+ pf.drive1
+ pf.drive2
+ pf.drive3
+ pf.cluster
+ pf.nice
+
+ In addition, you can use the parameter pf.disable to disable
+ the driver entirely.
+
+*/
+
+#define PF_VERSION "1.0"
+#define PF_MAJOR 47
+#define PF_NAME "pf"
+#define PF_UNITS 4
+
+/* Here are things one can override from the insmod command.
+ Most are autoprobed by paride unless set here. Verbose is on
+ by default.
+
+*/
+
+static int verbose = 0;
+static int major = PF_MAJOR;
+static char *name = PF_NAME;
+static int cluster = 64;
+static int nice = 0;
+static int disable = 0;
+
+static int drive0[7] = {0,0,0,-1,-1,-1,-1};
+static int drive1[7] = {0,0,0,-1,-1,-1,-1};
+static int drive2[7] = {0,0,0,-1,-1,-1,-1};
+static int drive3[7] = {0,0,0,-1,-1,-1,-1};
+
+static int (*drives[4])[7] = {&drive0,&drive1,&drive2,&drive3};
+static int pf_drive_count;
+
+#define D_PRT 0
+#define D_PRO 1
+#define D_UNI 2
+#define D_MOD 3
+#define D_SLV 4
+#define D_LUN 5
+#define D_DLY 6
+
+#define DU (*drives[unit])
+
+/* end of parameters */
+
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/genhd.h>
+#include <linux/hdreg.h>
+#include <linux/cdrom.h>
+
+#include <asm/uaccess.h>
+
+#ifndef MODULE
+
+#include "setup.h"
+
+static STT pf_stt[7] = {{"drive0",7,drive0},
+ {"drive1",7,drive1},
+ {"drive2",7,drive2},
+ {"drive3",7,drive3},
+ {"disable",1,&disable},
+ {"cluster",1,&cluster},
+ {"nice",1,&nice}};
+
+void pf_setup( char *str, int *ints)
+
+{ generic_setup(pf_stt,7,str);
+}
+
+#endif
+
+MODULE_PARM(verbose,"i");
+MODULE_PARM(major,"i");
+MODULE_PARM(name,"s");
+MODULE_PARM(cluster,"i");
+MODULE_PARM(nice,"i");
+MODULE_PARM(drive0,"1-7i");
+MODULE_PARM(drive1,"1-7i");
+MODULE_PARM(drive2,"1-7i");
+MODULE_PARM(drive3,"1-7i");
+
+#include "paride.h"
+
+/* set up defines for blk.h, why don't all drivers do it this way ? */
+
+#define MAJOR_NR major
+#define DEVICE_NAME "PF"
+#define DEVICE_REQUEST do_pf_request
+#define DEVICE_NR(device) MINOR(device)
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#include <linux/blk.h>
+
+#include "pseudo.h"
+
+/* constants for faking geometry numbers */
+
+#define PF_FD_MAX 8192 /* use FD geometry under this size */
+#define PF_FD_HDS 2
+#define PF_FD_SPT 18
+#define PF_HD_HDS 64
+#define PF_HD_SPT 32
+
+#define PF_MAX_RETRIES 5
+#define PF_TMO 800 /* interrupt timeout in jiffies */
+#define PF_SPIN_DEL 50 /* spin delay in micro-seconds */
+
+#define PF_SPIN (10000/PF_SPIN_DEL)*PF_TMO
+
+#define STAT_ERR 0x00001
+#define STAT_INDEX 0x00002
+#define STAT_ECC 0x00004
+#define STAT_DRQ 0x00008
+#define STAT_SEEK 0x00010
+#define STAT_WRERR 0x00020
+#define STAT_READY 0x00040
+#define STAT_BUSY 0x00080
+
+#define ATAPI_REQ_SENSE 0x03
+#define ATAPI_LOCK 0x1e
+#define ATAPI_DOOR 0x1b
+#define ATAPI_MODE_SENSE 0x5a
+#define ATAPI_CAPACITY 0x25
+#define ATAPI_IDENTIFY 0x12
+#define ATAPI_READ_10 0x28
+#define ATAPI_WRITE_10 0x2a
+
+int pf_init(void);
+#ifdef MODULE
+void cleanup_module( void );
+#endif
+static int pf_open(struct inode *inode, struct file *file);
+static void do_pf_request(void);
+static int pf_ioctl(struct inode *inode,struct file *file,
+ unsigned int cmd, unsigned long arg);
+
+static int pf_release (struct inode *inode, struct file *file);
+
+static int pf_detect(void);
+static void do_pf_read(void);
+static void do_pf_write(void);
+static void do_pf_read_drq( void );
+static void do_pf_write_done( void );
+
+static int pf_identify (int unit);
+static void pf_lock(int unit, int func);
+static void pf_eject(int unit);
+static int pf_check_media(kdev_t dev);
+
+static int pf_blocksizes[PF_UNITS];
+
+#define PF_NM 0
+#define PF_RO 1
+#define PF_RW 2
+
+#define PF_NAMELEN 8
+
+struct pf_unit {
+ struct pi_adapter pia; /* interface to paride layer */
+ struct pi_adapter *pi;
+ int removable; /* removable media device ? */
+ int media_status; /* media present ? WP ? */
+ int drive; /* drive */
+ int lun;
+ int access; /* count of active opens ... */
+ int capacity; /* Size of this volume in sectors */
+ int present; /* device present ? */
+ char name[PF_NAMELEN]; /* pf0, pf1, ... */
+ };
+
+struct pf_unit pf[PF_UNITS];
+
+/* 'unit' must be defined in all functions - either as a local or a param */
+
+#define PF pf[unit]
+#define PI PF.pi
+
+static char pf_scratch[512]; /* scratch block buffer */
+
+/* the variables below are used mainly in the I/O request engine, which
+ processes only one request at a time.
+*/
+
+static int pf_retries = 0; /* i/o error retry count */
+static int pf_busy = 0; /* request being processed ? */
+static int pf_block; /* address of next requested block */
+static int pf_count; /* number of blocks still to do */
+static int pf_run; /* sectors in current cluster */
+static int pf_cmd; /* current command READ/WRITE */
+static int pf_unit; /* unit of current request */
+static int pf_mask; /* stopper for pseudo-int */
+static char * pf_buf; /* buffer for request in progress */
+
+/* kernel glue structures */
+
+static struct file_operations pf_fops = {
+ NULL, /* lseek - default */
+ block_read, /* read - general block-dev read */
+ block_write, /* write - general block-dev write */
+ NULL, /* readdir - bad */
+ NULL, /* select */
+ pf_ioctl, /* ioctl */
+ NULL, /* mmap */
+ pf_open, /* open */
+ pf_release, /* release */
+ block_fsync, /* fsync */
+ NULL, /* fasync */
+ pf_check_media, /* media change ? */
+ NULL /* revalidate new media */
+};
+
+void pf_init_units( void )
+
+{ int unit, j;
+
+ pf_drive_count = 0;
+ for (unit=0;unit<PF_UNITS;unit++) {
+ PF.pi = & PF.pia;
+ PF.access = 0;
+ PF.media_status = PF_NM;
+ PF.capacity = 0;
+ PF.present = 0;
+ PF.drive = DU[D_SLV];
+ PF.lun = DU[D_LUN];
+ j = 0;
+ while ((j < PF_NAMELEN-2) && (PF.name[j]=name[j])) j++;
+ PF.name[j++] = '0' + unit;
+ PF.name[j] = 0;
+ if (DU[D_PRT]) pf_drive_count++;
+ }
+}
+
+int pf_init (void) /* preliminary initialisation */
+
+{ int i;
+
+ if (disable) return -1;
+
+ pf_init_units();
+
+ if (pf_detect()) return -1;
+ pf_busy = 0;
+
+ if (register_blkdev(MAJOR_NR,name,&pf_fops)) {
+ printk("pf_init: unable to get major number %d\n",
+ major);
+ return -1;
+ }
+ blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+ read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read ahead */
+
+ for (i=0;i<PF_UNITS;i++) pf_blocksizes[i] = 1024;
+ blksize_size[MAJOR_NR] = pf_blocksizes;
+
+ return 0;
+}
+
+static int pf_open (struct inode *inode, struct file *file)
+
+{ int unit = DEVICE_NR(inode->i_rdev);
+
+ if ((unit >= PF_UNITS) || (!PF.present)) return -ENODEV;
+
+ MOD_INC_USE_COUNT;
+
+ pf_identify(unit);
+
+ if (PF.media_status == PF_NM) {
+ MOD_DEC_USE_COUNT;
+ return -ENODEV;
+ }
+
+ if ((PF.media_status == PF_RO) && (file ->f_mode & 2)) {
+ MOD_DEC_USE_COUNT;
+ return -EROFS;
+ }
+
+ PF.access++;
+ if (PF.removable) pf_lock(unit,1);
+
+ return 0;
+}
+
+static int pf_ioctl(struct inode *inode,struct file *file,
+ unsigned int cmd, unsigned long arg)
+
+{ int err, unit;
+ struct hd_geometry *geo = (struct hd_geometry *) arg;
+
+ if ((!inode) || (!inode->i_rdev)) return -EINVAL;
+ unit = DEVICE_NR(inode->i_rdev);
+ if (unit >= PF_UNITS) return -EINVAL;
+ if (!PF.present) return -ENODEV;
+
+ switch (cmd) {
+ case CDROMEJECT:
+ if (PF.access == 1) {
+ pf_eject(unit);
+ return 0;
+ }
+ case HDIO_GETGEO:
+ if (!geo) return -EINVAL;
+ err = verify_area(VERIFY_WRITE,geo,sizeof(*geo));
+ if (err) return err;
+ if (PF.capacity < PF_FD_MAX) {
+ put_user(PF.capacity/(PF_FD_HDS*PF_FD_SPT),
+ (short *) &geo->cylinders);
+ put_user(PF_FD_HDS, (char *) &geo->heads);
+ put_user(PF_FD_SPT, (char *) &geo->sectors);
+ } else {
+ put_user(PF.capacity/(PF_HD_HDS*PF_HD_SPT),
+ (short *) &geo->cylinders);
+ put_user(PF_HD_HDS, (char *) &geo->heads);
+ put_user(PF_HD_SPT, (char *) &geo->sectors);
+ }
+ put_user(0,(long *)&geo->start);
+ return 0;
+ case BLKRASET:
+ if(!suser()) return -EACCES;
+ if(!(inode->i_rdev)) return -EINVAL;
+ if(arg > 0xff) return -EINVAL;
+ read_ahead[MAJOR(inode->i_rdev)] = arg;
+ return 0;
+ case BLKRAGET:
+ if (!arg) return -EINVAL;
+ err = verify_area(VERIFY_WRITE,(long *) arg,sizeof(long));
+ if (err) return (err);
+ put_user(read_ahead[MAJOR(inode->i_rdev)],(long *) arg);
+ return (0);
+ case BLKGETSIZE:
+ if (!arg) return -EINVAL;
+ err = verify_area(VERIFY_WRITE,(long *) arg,sizeof(long));
+ if (err) return (err);
+ put_user(PF.capacity,(long *) arg);
+ return (0);
+ case BLKFLSBUF:
+ if(!suser()) return -EACCES;
+ if(!(inode->i_rdev)) return -EINVAL;
+ fsync_dev(inode->i_rdev);
+ invalidate_buffers(inode->i_rdev);
+ return 0;
+ RO_IOCTLS(inode->i_rdev,arg);
+ default:
+ return -EINVAL;
+ }
+}
+
+
+static int pf_release (struct inode *inode, struct file *file)
+
+{ kdev_t devp;
+ int unit;
+
+ struct super_block *sb;
+
+ devp = inode->i_rdev;
+ unit = DEVICE_NR(devp);
+
+ if ((unit >= PF_UNITS) || (PF.access <= 0))
+ return -EINVAL;
+
+ PF.access--;
+
+ if (!PF.access) {
+ fsync_dev(devp);
+
+ sb = get_super(devp);
+ if (sb) invalidate_inodes(sb);
+
+ invalidate_buffers(devp);
+ if (PF.removable) pf_lock(unit,0);
+ }
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+
+}
+
+static int pf_check_media( kdev_t dev)
+
+{ return 1;
+}
+
+#ifdef MODULE
+
+/* Glue for modules ... */
+
+void cleanup_module(void);
+
+int init_module(void)
+
+{ int err;
+ long flags;
+
+ save_flags(flags);
+ cli();
+
+ err = pf_init();
+
+ restore_flags(flags);
+ return err;
+}
+
+void cleanup_module(void)
+
+{ long flags;
+ int unit;
+
+ save_flags(flags);
+ cli();
+
+ unregister_blkdev(MAJOR_NR,name);
+
+ for (unit=0;unit<PF_UNITS;unit++)
+ if (PF.present) pi_release(PI);
+
+ restore_flags(flags);
+}
+
+#endif
+
+#define WR(c,r,v) pi_write_regr(PI,c,r,v)
+#define RR(c,r) (pi_read_regr(PI,c,r))
+
+#define LUN (0x20*PF.lun)
+#define DRIVE (0xa0+0x10*PF.drive)
+
+static int pf_wait( int unit, int go, int stop, char * fun, char * msg )
+
+{ int j, r, e, s, p;
+
+ j = 0;
+ while ((((r=RR(1,6))&go)||(stop&&(!(r&stop))))&&(j++<PF_SPIN))
+ udelay(PF_SPIN_DEL);
+
+ if ((r&(STAT_ERR&stop))||(j>=PF_SPIN)) {
+ s = RR(0,7);
+ e = RR(0,1);
+ p = RR(0,2);
+ if (j >= PF_SPIN) e |= 0x100;
+ if (fun) printk("%s: %s %s: alt=0x%x stat=0x%x err=0x%x"
+ " loop=%d phase=%d\n",
+ PF.name,fun,msg,r,s,e,j,p);
+ return (e<<8)+s;
+ }
+ return 0;
+}
+
+static int pf_command( int unit, char * cmd, int dlen, char * fun )
+
+{ pi_connect(PI);
+
+ WR(0,6,DRIVE);
+
+ if (pf_wait(unit,STAT_BUSY|STAT_DRQ,0,fun,"before command")) {
+ pi_disconnect(PI);
+ return -1;
+ }
+
+ WR(0,4,dlen % 256);
+ WR(0,5,dlen / 256);
+ WR(0,7,0xa0); /* ATAPI packet command */
+
+ if (pf_wait(unit,STAT_BUSY,STAT_DRQ|STAT_ERR,fun,"command DRQ")) {
+ pi_disconnect(PI);
+ return -1;
+ }
+
+ if (RR(0,2) != 1) {
+ printk("%s: %s: command phase error\n",PF.name,fun);
+ pi_disconnect(PI);
+ return -1;
+ }
+
+ pi_write_block(PI,cmd,12);
+
+ return 0;
+}
+
+static int pf_completion( int unit, char * buf, char * fun )
+
+{ int r, s, n;
+
+ r = pf_wait(unit,STAT_BUSY,STAT_DRQ|STAT_READY|STAT_ERR,
+ fun,"completion");
+
+ if ((RR(0,2)&2) && (RR(0,7)&STAT_DRQ)) {
+ n = (RR(0,4)+256*RR(0,5));
+ pi_read_block(PI,buf,n);
+ }
+
+ s = pf_wait(unit,STAT_BUSY,STAT_READY|STAT_ERR,fun,"data done");
+
+ pi_disconnect(PI);
+
+ return (r?r:s);
+}
+
+static void pf_req_sense( int unit, int quiet )
+
+{ char rs_cmd[12] = { ATAPI_REQ_SENSE,LUN,0,0,16,0,0,0,0,0,0,0 };
+ char buf[16];
+ int r;
+
+ r = pf_command(unit,rs_cmd,16,"Request sense");
+ udelay(1000);
+ if (!r) pf_completion(unit,buf,"Request sense");
+
+ if ((!r)&&(!quiet))
+ printk("%s: Sense key: %x, ASC: %x, ASQ: %x\n",
+ PF.name,buf[2]&0xf,buf[12],buf[13]);
+}
+
+static int pf_atapi( int unit, char * cmd, int dlen, char * buf, char * fun )
+
+{ int r;
+
+ r = pf_command(unit,cmd,dlen,fun);
+ udelay(1000);
+ if (!r) r = pf_completion(unit,buf,fun);
+ if (r) pf_req_sense(unit,!fun);
+
+ return r;
+}
+
+#define DBMSG(msg) NULL
+
+static void pf_lock(int unit, int func)
+
+{ char lo_cmd[12] = { ATAPI_LOCK,LUN,0,0,func,0,0,0,0,0,0,0 };
+
+ pf_atapi(unit,lo_cmd,0,pf_scratch,func?"unlock":"lock");
+}
+
+
+static void pf_eject( int unit )
+
+{ char ej_cmd[12] = { ATAPI_DOOR,LUN,0,0,2,0,0,0,0,0,0,0 };
+
+ pf_lock(unit,0);
+ pf_atapi(unit,ej_cmd,0,pf_scratch,"eject");
+}
+
+#define PF_RESET_TMO 30 /* in tenths of a second */
+
+static void pf_sleep( int cs )
+
+{ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + cs;
+ schedule();
+}
+
+
+static int pf_reset( int unit )
+
+/* the ATAPI standard actually specifies the contents of all 7 registers
+ after a reset, but the specification is ambiguous concerning the last
+ two bytes, and different drives interpret the standard differently.
+*/
+
+{ int i, k, flg;
+ int expect[5] = {1,1,1,0x14,0xeb};
+ long flags;
+
+ pi_connect(PI);
+ WR(0,6,DRIVE);
+ WR(0,7,8);
+
+ save_flags(flags);
+ sti();
+
+ pf_sleep(2);
+
+ k = 0;
+ while ((k++ < PF_RESET_TMO) && (RR(1,6)&STAT_BUSY))
+ pf_sleep(10);
+
+ restore_flags(flags);
+
+ flg = 1;
+ for(i=0;i<5;i++) flg &= (RR(0,i+1) == expect[i]);
+
+ if (verbose) {
+ printk("%s: Reset (%d) signature = ",PF.name,k);
+ for (i=0;i<5;i++) printk("%3x",RR(0,i+1));
+ if (!flg) printk(" (incorrect)");
+ printk("\n");
+ }
+
+ pi_disconnect(PI);
+ return flg-1;
+}
+
+static void pf_mode_sense( int unit )
+
+{ char ms_cmd[12] = { ATAPI_MODE_SENSE,LUN,0,0,0,0,0,0,8,0,0,0};
+ char buf[8];
+
+ pf_atapi(unit,ms_cmd,8,buf,DBMSG("mode sense"));
+ PF.media_status = PF_RW;
+ if (buf[3] & 0x80) PF.media_status = PF_RO;
+}
+
+static void xs( char *buf, char *targ, int offs, int len )
+
+{ int j,k,l;
+
+ j=0; l=0;
+ for (k=0;k<len;k++)
+ if((buf[k+offs]!=0x20)||(buf[k+offs]!=l))
+ l=targ[j++]=buf[k+offs];
+ if (l==0x20) j--; targ[j]=0;
+}
+
+static int xl( char *buf, int offs )
+
+{ int v,k;
+
+ v=0;
+ for(k=0;k<4;k++) v=v*256+(buf[k+offs]&0xff);
+ return v;
+}
+
+static void pf_get_capacity( int unit )
+
+{ char rc_cmd[12] = { ATAPI_CAPACITY,LUN,0,0,0,0,0,0,0,0,0,0};
+ char buf[8];
+ int bs;
+
+ if (pf_atapi(unit,rc_cmd,8,buf,DBMSG("get capacity"))) {
+ PF.media_status = PF_NM;
+ return;
+ }
+ PF.capacity = xl(buf,0) + 1;
+ bs = xl(buf,4);
+ if (bs != 512) {
+ PF.capacity = 0;
+ if (verbose) printk("%s: Drive %d, LUN %d,"
+ " unsupported block size %d\n",
+ PF.name,PF.drive,PF.lun,bs);
+ }
+}
+
+static int pf_identify( int unit )
+
+{ int dt, s;
+ char *ms[2] = {"master","slave"};
+ char mf[10], id[18];
+ char id_cmd[12] = { ATAPI_IDENTIFY,LUN,0,0,36,0,0,0,0,0,0,0};
+ char buf[36];
+
+ s = pf_atapi(unit,id_cmd,36,buf,"identify");
+ if (s) return -1;
+
+ dt = buf[0] & 0x1f;
+ if ((dt != 0) && (dt != 7)) {
+ if (verbose)
+ printk("%s: Drive %d, LUN %d, unsupported type %d\n",
+ PF.name,PF.drive,PF.lun,dt);
+ return -1;
+ }
+
+ xs(buf,mf,8,8);
+ xs(buf,id,16,16);
+
+ PF.removable = (buf[1] & 0x80);
+
+ pf_mode_sense(unit);
+ pf_mode_sense(unit);
+ pf_mode_sense(unit);
+
+ pf_get_capacity(unit);
+
+ printk("%s: %s %s, %s LUN %d, type %d",
+ PF.name,mf,id,ms[PF.drive],PF.lun,dt);
+ if (PF.removable) printk(", removable");
+ if (PF.media_status == PF_NM)
+ printk(", no media\n");
+ else { if (PF.media_status == PF_RO) printk(", RO");
+ printk(", %d blocks\n",PF.capacity);
+ }
+
+ return 0;
+}
+
+static int pf_probe( int unit )
+
+/* returns 0, with id set if drive is detected
+ -1, if drive detection failed
+*/
+
+{ if (PF.drive == -1) {
+ for (PF.drive=0;PF.drive<=1;PF.drive++)
+ if (!pf_reset(unit)) {
+ if (PF.lun != -1) return pf_identify(unit);
+ else for (PF.lun=0;PF.lun<8;PF.lun++)
+ if (!pf_identify(unit)) return 0;
+ }
+ } else {
+ if (pf_reset(unit)) return -1;
+ if (PF.lun != -1) return pf_identify(unit);
+ for (PF.lun=0;PF.lun<8;PF.lun++)
+ if (!pf_identify(unit)) return 0;
+ }
+ return -1;
+}
+
+static int pf_detect( void )
+
+{ int k, unit;
+
+ printk("%s: %s version %s, major %d, cluster %d, nice %d\n",
+ name,name,PF_VERSION,major,cluster,nice);
+
+ k = 0;
+ if (pf_drive_count == 0) {
+ unit = 0;
+ if (pi_init(PI,1,-1,-1,-1,-1,-1,pf_scratch,
+ PI_PF,verbose,PF.name)) {
+ if (!pf_probe(unit)) {
+ PF.present = 1;
+ k++;
+ } else pi_release(PI);
+ }
+
+ } else for (unit=0;unit<PF_UNITS;unit++) if (DU[D_PRT])
+ if (pi_init(PI,0,DU[D_PRT],DU[D_MOD],DU[D_UNI],
+ DU[D_PRO],DU[D_DLY],pf_scratch,PI_PF,verbose,
+ PF.name)) {
+ if (!pf_probe(unit)) {
+ PF.present = 1;
+ k++;
+ } else pi_release(PI);
+ }
+
+ if (k) return 0;
+
+ printk("%s: No ATAPI disk detected\n",name);
+ return -1;
+}
+
+/* The i/o request engine */
+
+static int pf_start( int unit, int cmd, int b, int c )
+
+{ int i;
+ char io_cmd[12] = {cmd,LUN,0,0,0,0,0,0,0,0,0,0};
+
+ for(i=0;i<4;i++) {
+ io_cmd[5-i] = b & 0xff;
+ b = b >> 8;
+ }
+
+ io_cmd[8] = c & 0xff;
+ io_cmd[7] = (c >> 8) & 0xff;
+
+ i = pf_command(unit,io_cmd,c*512,"start i/o");
+
+ udelay(1000);
+
+ return i;
+}
+
+static int pf_ready( void )
+
+{ int unit = pf_unit;
+
+ return (((RR(1,6)&(STAT_BUSY|pf_mask)) == pf_mask));
+}
+
+static void do_pf_request (void)
+
+{ struct buffer_head * bh;
+ struct request * req;
+ int unit;
+
+ if (pf_busy) return;
+repeat:
+ if ((!CURRENT) || (CURRENT->rq_status == RQ_INACTIVE)) return;
+ INIT_REQUEST;
+
+ pf_unit = unit = DEVICE_NR(CURRENT->rq_dev);
+ pf_block = CURRENT->sector;
+ pf_count = CURRENT->nr_sectors;
+
+ bh = CURRENT->bh;
+ req = CURRENT;
+ if (bh->b_reqnext)
+ printk("%s: OUCH: b_reqnext != NULL\n",PF.name);
+
+ if ((pf_unit >= PF_UNITS) || (pf_block+pf_count > PF.capacity)) {
+ end_request(0);
+ goto repeat;
+ }
+
+ pf_cmd = CURRENT->cmd;
+ pf_run = pf_count;
+ while ((pf_run <= cluster) &&
+ (req = req->next) &&
+ (pf_block+pf_run == req->sector) &&
+ (pf_cmd == req->cmd) &&
+ (pf_unit == DEVICE_NR(req->rq_dev)))
+ pf_run += req->nr_sectors;
+
+ pf_buf = CURRENT->buffer;
+ pf_retries = 0;
+
+
+ if (pf_cmd == READ) pi_do_claimed(PI,do_pf_read);
+ else if (pf_cmd == WRITE) pi_do_claimed(PI,do_pf_write);
+ else { end_request(0);
+ goto repeat;
+ }
+}
+
+static void pf_next_buf( int unit )
+
+{ cli();
+ end_request(1);
+ if (!pf_run) { sti(); return; }
+
+/* paranoia */
+
+ if ((!CURRENT) ||
+ (CURRENT->cmd != pf_cmd) ||
+ (DEVICE_NR(CURRENT->rq_dev) != pf_unit) ||
+ (CURRENT->rq_status == RQ_INACTIVE) ||
+ (CURRENT->sector != pf_block))
+ printk("%s: OUCH: request list changed unexpectedly\n",
+ PF.name);
+
+ pf_count = CURRENT->nr_sectors;
+ pf_buf = CURRENT->buffer;
+ sti();
+}
+
+static void do_pf_read( void )
+
+{ int unit = pf_unit;
+
+ pf_busy = 1;
+
+ sti();
+
+ if (pf_start(unit,ATAPI_READ_10,pf_block,pf_run)) {
+ pi_disconnect(PI);
+ if (pf_retries < PF_MAX_RETRIES) {
+ pf_retries++;
+ pi_do_claimed(PI,do_pf_read);
+ return;
+ }
+ end_request(0);
+ pf_busy = 0;
+ cli();
+ do_pf_request();
+ return;
+ }
+ pf_mask=STAT_DRQ;
+ ps_set_intr(do_pf_read_drq,pf_ready,PF_TMO,nice);
+}
+
+static void do_pf_read_drq( void )
+
+{ int unit = pf_unit;
+
+ sti();
+ while (1) {
+ if (pf_wait(unit,STAT_BUSY,STAT_DRQ|STAT_ERR,
+ "read block","completion") & STAT_ERR) {
+ pi_disconnect(PI);
+ if (pf_retries < PF_MAX_RETRIES) {
+ pf_req_sense(unit,0);
+ pf_retries++;
+ pi_do_claimed(PI,do_pf_read);
+ return;
+ }
+ end_request(0);
+ pf_busy = 0;
+ cli();
+ do_pf_request();
+ return;
+ }
+ pi_read_block(PI,pf_buf,512);
+ pf_count--; pf_run--;
+ pf_buf += 512;
+ pf_block++;
+ if (!pf_run) break;
+ if (!pf_count) pf_next_buf(unit);
+ }
+ pi_disconnect(PI);
+ end_request(1);
+ pf_busy = 0;
+ cli();
+ do_pf_request();
+}
+
+static void do_pf_write( void )
+
+{ int unit = pf_unit;
+
+ pf_busy = 1;
+
+ sti();
+
+ if (pf_start(unit,ATAPI_WRITE_10,pf_block,pf_run)) {
+ pi_disconnect(PI);
+ if (pf_retries < PF_MAX_RETRIES) {
+ pf_retries++;
+ pi_do_claimed(PI,do_pf_write);
+ return;
+ }
+ end_request(0);
+ pf_busy = 0;
+ cli();
+ do_pf_request();
+ return;
+ }
+
+ while (1) {
+ if (pf_wait(unit,STAT_BUSY,STAT_DRQ|STAT_ERR,
+ "write block","data wait") & STAT_ERR) {
+ pi_disconnect(PI);
+ if (pf_retries < PF_MAX_RETRIES) {
+ pf_retries++;
+ pi_do_claimed(PI,do_pf_write);
+ return;
+ }
+ end_request(0);
+ pf_busy = 0;
+ cli();
+ do_pf_request();
+ return;
+ }
+ pi_write_block(PI,pf_buf,512);
+ pf_count--; pf_run--;
+ pf_buf += 512;
+ pf_block++;
+ if (!pf_run) break;
+ if (!pf_count) pf_next_buf(unit);
+ }
+ pf_mask = 0;
+ ps_set_intr(do_pf_write_done,pf_ready,PF_TMO,nice);
+}
+
+static void do_pf_write_done( void )
+
+{ int unit = pf_unit;
+
+ sti();
+ if (pf_wait(unit,STAT_BUSY,0,"write block","done") & STAT_ERR) {
+ pi_disconnect(PI);
+ if (pf_retries < PF_MAX_RETRIES) {
+ pf_retries++;
+ pi_do_claimed(PI,do_pf_write);
+ return;
+ }
+ end_request(0);
+ pf_busy = 0;
+ cli();
+ do_pf_request();
+ return;
+ }
+ pi_disconnect(PI);
+ end_request(1);
+ pf_busy = 0;
+ cli();
+ do_pf_request();
+}
+
+/* end of pf.c */
+
diff --git a/drivers/block/paride/pseudo.h b/drivers/block/paride/pseudo.h
new file mode 100644
index 000000000..44c74c7c9
--- /dev/null
+++ b/drivers/block/paride/pseudo.h
@@ -0,0 +1,138 @@
+/*
+ pseudo.h (c) 1997 Grant R. Guenther <grant@torque.net>
+ Under the terms of the GNU public license.
+
+ This is the "pseudo-interrupt" logic for parallel port drivers.
+
+ This module is #included into each driver. It makes one
+ function available:
+
+ ps_set_intr( void (*continuation)(void),
+ int (*ready)(void),
+ int timeout,
+ int nice )
+
+ Which will arrange for ready() to be evaluated frequently and
+ when either it returns true, or timeout jiffies have passed,
+ continuation() will be invoked.
+
+ If nice is true, the test will done approximately once a
+ jiffy. If nice is 0, the test will also be done whenever
+ the scheduler runs (by adding it to a task queue).
+
+*/
+
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/tqueue.h>
+
+static void ps_timer_int( unsigned long data);
+static void ps_tq_int( void *data);
+
+static int ps_use_tq = 1;
+static void (* ps_continuation)(void);
+static int (* ps_ready)(void);
+static int ps_then;
+static int ps_timeout;
+static int ps_timer_active = 0;
+static int ps_tq_active = 0;
+
+static struct timer_list ps_timer = {0,0,0,0,ps_timer_int};
+static struct tq_struct ps_tq = {0,0,ps_tq_int,NULL};
+
+static void ps_set_intr( void (*continuation)(void),
+ int (*ready)(void),
+ int timeout, int nice )
+
+{ long flags;
+
+ save_flags(flags);
+ cli();
+
+ ps_continuation = continuation;
+ ps_ready = ready;
+ ps_then = jiffies;
+ ps_timeout = jiffies + timeout;
+ ps_use_tq = !nice;
+
+ if (ps_use_tq && !ps_tq_active) {
+#ifdef HAVE_DISABLE_HLT
+ disable_hlt();
+#endif
+ ps_tq_active = 1;
+ queue_task(&ps_tq,&tq_scheduler);
+ }
+
+ if (!ps_timer_active) {
+ ps_timer_active = 1;
+ ps_timer.expires = jiffies;
+ add_timer(&ps_timer);
+ }
+
+ restore_flags(flags);
+}
+
+static void ps_tq_int( void *data )
+
+{ void (*con)(void);
+ long flags;
+
+ save_flags(flags);
+ cli();
+
+ con = ps_continuation;
+
+#ifdef HAVE_DISABLE_HLT
+ enable_hlt();
+#endif
+
+ ps_tq_active = 0;
+
+ if (!con) {
+ restore_flags(flags);
+ return;
+ }
+ if (ps_ready() || (jiffies >= ps_timeout)) {
+ ps_continuation = NULL;
+ restore_flags(flags);
+ con();
+ return;
+ }
+
+#ifdef HAVE_DISABLE_HLT
+ disable_hlt();
+#endif
+
+ ps_tq_active = 1;
+ queue_task(&ps_tq,&tq_scheduler);
+ restore_flags(flags);
+}
+
+static void ps_timer_int( unsigned long data)
+
+{ void (*con)(void);
+ long flags;
+
+ save_flags(flags);
+ cli();
+
+ con = ps_continuation;
+ ps_timer_active = 0;
+ if (!con) {
+ restore_flags(flags);
+ return;
+ }
+ if (ps_ready() || (jiffies >= ps_timeout)) {
+ ps_continuation = NULL;
+ restore_flags(flags);
+ con();
+ return;
+ }
+ ps_timer_active = 1;
+ ps_timer.expires = jiffies;
+ add_timer(&ps_timer);
+ restore_flags(flags);
+}
+
+/* end of pseudo.h */
+
diff --git a/drivers/block/paride/pt.c b/drivers/block/paride/pt.c
new file mode 100644
index 000000000..922e98271
--- /dev/null
+++ b/drivers/block/paride/pt.c
@@ -0,0 +1,959 @@
+/*
+ pt.c (c) 1998 Grant R. Guenther <grant@torque.net>
+ Under the terms of the GNU public license.
+
+ This is the high-level driver for parallel port ATAPI tape
+ drives based on chips supported by the paride module.
+
+ The driver implements both rewinding and non-rewinding
+ devices, filemarks, and the rewind ioctl. It allocates
+ a small internal "bounce buffer" for each open device, but
+ otherwise expects buffering and blocking to be done at the
+ user level. As with most block-structured tapes, short
+ writes are padded to full tape blocks, so reading back a file
+ may return more data than was actually written.
+
+ By default, the driver will autoprobe for a single parallel
+ port ATAPI tape drive, but if their individual parameters are
+ specified, the driver can handle up to 4 drives.
+
+ The rewinding devices are named /dev/pt0, /dev/pt1, ...
+ while the non-rewinding devices are /dev/npt0, /dev/npt1, etc.
+
+ The behaviour of the pt driver can be altered by setting
+ some parameters from the insmod command line. The following
+ parameters are adjustable:
+
+ drive0 These four arguments can be arrays of
+ drive1 1-6 integers as follows:
+ drive2
+ drive3 <prt>,<pro>,<uni>,<mod>,<slv>,<dly>
+
+ Where,
+
+ <prt> is the base of the parallel port address for
+ the corresponding drive. (required)
+
+ <pro> is the protocol number for the adapter that
+ supports this drive. These numbers are
+ logged by 'paride' when the protocol modules
+ are initialised. (0 if not given)
+
+ <uni> for those adapters that support chained
+ devices, this is the unit selector for the
+ chain of devices on the given port. It should
+ be zero for devices that don't support chaining.
+ (0 if not given)
+
+ <mod> this can be -1 to choose the best mode, or one
+ of the mode numbers supported by the adapter.
+ (-1 if not given)
+
+ <slv> ATAPI devices can be jumpered to master or slave.
+ Set this to 0 to choose the master drive, 1 to
+ choose the slave, -1 (the default) to choose the
+ first drive found.
+
+ <dly> some parallel ports require the driver to
+ go more slowly. -1 sets a default value that
+ should work with the chosen protocol. Otherwise,
+ set this to a small integer, the larger it is
+ the slower the port i/o. In some cases, setting
+ this to zero will speed up the device. (default -1)
+
+ major You may use this parameter to overide the
+ default major number (96) that this driver
+ will use. Be sure to change the device
+ name as well.
+
+ name This parameter is a character string that
+ contains the name the kernel will use for this
+ device (in /proc output, for instance).
+ (default "pt").
+
+ verbose This parameter controls the amount of logging
+ that is done while the driver probes for
+ devices. Set it to 0 for a quiet load, or 1 to
+ see all the progress messages. (default 0)
+
+ If this driver is built into the kernel, you can use
+ the following command line parameters, with the same values
+ as the corresponding module parameters listed above:
+
+ pt.drive0
+ pt.drive1
+ pt.drive2
+ pt.drive3
+
+ In addition, you can use the parameter pt.disable to disable
+ the driver entirely.
+
+*/
+
+#define PT_VERSION "1.0"
+#define PT_MAJOR 96
+#define PT_NAME "pt"
+#define PT_UNITS 4
+
+/* Here are things one can override from the insmod command.
+ Most are autoprobed by paride unless set here. Verbose is on
+ by default.
+
+*/
+
+static int verbose = 0;
+static int major = PT_MAJOR;
+static char *name = PT_NAME;
+static int disable = 0;
+
+static int drive0[6] = {0,0,0,-1,-1,-1};
+static int drive1[6] = {0,0,0,-1,-1,-1};
+static int drive2[6] = {0,0,0,-1,-1,-1};
+static int drive3[6] = {0,0,0,-1,-1,-1};
+
+static int (*drives[4])[6] = {&drive0,&drive1,&drive2,&drive3};
+static int pt_drive_count;
+
+#define D_PRT 0
+#define D_PRO 1
+#define D_UNI 2
+#define D_MOD 3
+#define D_SLV 4
+#define D_DLY 5
+
+#define DU (*drives[unit])
+
+/* end of parameters */
+
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/malloc.h>
+#include <linux/mtio.h>
+
+#include <asm/uaccess.h>
+
+#ifndef MODULE
+
+#include "setup.h"
+
+static STT pt_stt[5] = {{"drive0",6,drive0},
+ {"drive1",6,drive1},
+ {"drive2",6,drive2},
+ {"drive3",6,drive3},
+ {"disable",1,&disable}};
+
+void pt_setup( char *str, int *ints)
+
+{ generic_setup(pt_stt,5,str);
+}
+
+#endif
+
+MODULE_PARM(verbose,"i");
+MODULE_PARM(major,"i");
+MODULE_PARM(name,"s");
+MODULE_PARM(drive0,"1-6i");
+MODULE_PARM(drive1,"1-6i");
+MODULE_PARM(drive2,"1-6i");
+MODULE_PARM(drive3,"1-6i");
+
+#include "paride.h"
+
+#define PT_MAX_RETRIES 5
+#define PT_TMO 800 /* interrupt timeout in jiffies */
+#define PT_SPIN_DEL 50 /* spin delay in micro-seconds */
+#define PT_RESET_TMO 30 /* 3 seconds */
+#define PT_READY_TMO 60 /* 60 seconds */
+#define PT_REWIND_TMO 1200 /* 20 minutes */
+
+#define PT_SPIN (10000/PT_SPIN_DEL)*PT_TMO
+
+#define STAT_ERR 0x00001
+#define STAT_INDEX 0x00002
+#define STAT_ECC 0x00004
+#define STAT_DRQ 0x00008
+#define STAT_SEEK 0x00010
+#define STAT_WRERR 0x00020
+#define STAT_READY 0x00040
+#define STAT_BUSY 0x00080
+#define STAT_SENSE 0x1f000
+
+#define ATAPI_TEST_READY 0x00
+#define ATAPI_REWIND 0x01
+#define ATAPI_REQ_SENSE 0x03
+#define ATAPI_READ_6 0x08
+#define ATAPI_WRITE_6 0x0a
+#define ATAPI_WFM 0x10
+#define ATAPI_IDENTIFY 0x12
+#define ATAPI_MODE_SENSE 0x1a
+#define ATAPI_LOG_SENSE 0x4d
+
+int pt_init(void);
+#ifdef MODULE
+void cleanup_module( void );
+#endif
+
+static int pt_open(struct inode *inode, struct file *file);
+static int pt_ioctl(struct inode *inode,struct file *file,
+ unsigned int cmd, unsigned long arg);
+static int pt_release (struct inode *inode, struct file *file);
+static ssize_t pt_read(struct file * filp, char * buf,
+ size_t count, loff_t *ppos);
+static ssize_t pt_write(struct file * filp, const char * buf,
+ size_t count, loff_t *ppos);
+static int pt_detect(void);
+
+static int pt_identify (int unit);
+
+/* bits in PT.flags */
+
+#define PT_MEDIA 1
+#define PT_WRITE_OK 2
+#define PT_REWIND 4
+#define PT_WRITING 8
+#define PT_READING 16
+#define PT_EOF 32
+
+#define PT_NAMELEN 8
+#define PT_BUFSIZE 16384
+
+struct pt_unit {
+ struct pi_adapter pia; /* interface to paride layer */
+ struct pi_adapter *pi;
+ int flags; /* various state flags */
+ int last_sense; /* result of last request sense */
+ int drive; /* drive */
+ int access; /* count of active opens ... */
+ int bs; /* block size */
+ int capacity; /* Size of tape in KB */
+ int present; /* device present ? */
+ char *bufptr;
+ char name[PT_NAMELEN]; /* pf0, pf1, ... */
+ };
+
+struct pt_unit pt[PT_UNITS];
+
+/* 'unit' must be defined in all functions - either as a local or a param */
+
+#define PT pt[unit]
+#define PI PT.pi
+
+static char pt_scratch[512]; /* scratch block buffer */
+
+/* kernel glue structures */
+
+static struct file_operations pt_fops = {
+ NULL, /* lseek - default */
+ pt_read, /* read */
+ pt_write, /* write */
+ NULL, /* readdir - bad */
+ NULL, /* select */
+ pt_ioctl, /* ioctl */
+ NULL, /* mmap */
+ pt_open, /* open */
+ pt_release, /* release */
+ NULL, /* fsync */
+ NULL, /* fasync */
+ NULL, /* media change ? */
+ NULL /* revalidate new media */
+};
+
+void pt_init_units( void )
+
+{ int unit, j;
+
+ pt_drive_count = 0;
+ for (unit=0;unit<PT_UNITS;unit++) {
+ PT.pi = & PT.pia;
+ PT.access = 0;
+ PT.flags = 0;
+ PT.last_sense = 0;
+ PT.present = 0;
+ PT.bufptr = NULL;
+ PT.drive = DU[D_SLV];
+ j = 0;
+ while ((j < PT_NAMELEN-2) && (PT.name[j]=name[j])) j++;
+ PT.name[j++] = '0' + unit;
+ PT.name[j] = 0;
+ if (DU[D_PRT]) pt_drive_count++;
+ }
+}
+
+int pt_init (void) /* preliminary initialisation */
+
+{ int unit;
+
+ if (disable) return -1;
+
+ pt_init_units();
+
+ if (pt_detect()) return -1;
+
+ if (register_chrdev(major,name,&pt_fops)) {
+ printk("pt_init: unable to get major number %d\n",
+ major);
+ for (unit=0;unit<PT_UNITS;unit++)
+ if (PT.present) pi_release(PI);
+ return -1;
+ }
+
+ return 0;
+}
+
+#ifdef MODULE
+
+/* Glue for modules ... */
+
+void cleanup_module(void);
+
+int init_module(void)
+
+{ int err;
+ long flags;
+
+ save_flags(flags);
+ cli();
+
+ err = pt_init();
+
+ restore_flags(flags);
+ return err;
+}
+
+void cleanup_module(void)
+
+{ long flags;
+ int unit;
+
+ save_flags(flags);
+ cli();
+
+ unregister_chrdev(major,name);
+
+ for (unit=0;unit<PT_UNITS;unit++)
+ if (PT.present) pi_release(PI);
+
+ restore_flags(flags);
+}
+
+#endif
+
+#define WR(c,r,v) pi_write_regr(PI,c,r,v)
+#define RR(c,r) (pi_read_regr(PI,c,r))
+
+#define DRIVE (0xa0+0x10*PT.drive)
+
+static int pt_wait( int unit, int go, int stop, char * fun, char * msg )
+
+{ int j, r, e, s, p;
+
+ j = 0;
+ while ((((r=RR(1,6))&go)||(stop&&(!(r&stop))))&&(j++<PT_SPIN))
+ udelay(PT_SPIN_DEL);
+
+ if ((r&(STAT_ERR&stop))||(j>=PT_SPIN)) {
+ s = RR(0,7);
+ e = RR(0,1);
+ p = RR(0,2);
+ if (j >= PT_SPIN) e |= 0x100;
+ if (fun) printk("%s: %s %s: alt=0x%x stat=0x%x err=0x%x"
+ " loop=%d phase=%d\n",
+ PT.name,fun,msg,r,s,e,j,p);
+ return (e<<8)+s;
+ }
+ return 0;
+}
+
+static int pt_command( int unit, char * cmd, int dlen, char * fun )
+
+{ pi_connect(PI);
+
+ WR(0,6,DRIVE);
+
+ if (pt_wait(unit,STAT_BUSY|STAT_DRQ,0,fun,"before command")) {
+ pi_disconnect(PI);
+ return -1;
+ }
+
+ WR(0,4,dlen % 256);
+ WR(0,5,dlen / 256);
+ WR(0,7,0xa0); /* ATAPI packet command */
+
+ if (pt_wait(unit,STAT_BUSY,STAT_DRQ|STAT_ERR,fun,"command DRQ")) {
+ pi_disconnect(PI);
+ return -1;
+ }
+
+ if (RR(0,2) != 1) {
+ printk("%s: %s: command phase error\n",PT.name,fun);
+ pi_disconnect(PI);
+ return -1;
+ }
+
+ pi_write_block(PI,cmd,12);
+
+ return 0;
+}
+
+static int pt_completion( int unit, char * buf, char * fun )
+
+{ int r, s, n, p;
+
+ r = pt_wait(unit,STAT_BUSY,STAT_DRQ|STAT_READY|STAT_ERR,
+ fun,"completion");
+
+ if (RR(0,7)&STAT_DRQ) {
+ n = (RR(0,4)+256*RR(0,5));
+ p = RR(0,2)&3;
+ if (p == 0) pi_write_block(PI,buf,n);
+ if (p == 2) pi_read_block(PI,buf,n);
+ }
+
+ s = pt_wait(unit,STAT_BUSY,STAT_READY|STAT_ERR,fun,"data done");
+
+ pi_disconnect(PI);
+
+ return (r?r:s);
+}
+
+static void pt_req_sense( int unit, int quiet )
+
+{ char rs_cmd[12] = { ATAPI_REQ_SENSE,0,0,0,16,0,0,0,0,0,0,0 };
+ char buf[16];
+ int r;
+
+ r = pt_command(unit,rs_cmd,16,"Request sense");
+ udelay(1000);
+ if (!r) pt_completion(unit,buf,"Request sense");
+
+ PT.last_sense = -1;
+ if (!r) {
+ if (!quiet) printk("%s: Sense key: %x, ASC: %x, ASQ: %x\n",
+ PT.name,buf[2]&0xf,buf[12],buf[13]);
+ PT.last_sense = (buf[2]&0xf) | ((buf[12]&0xff)<<8)
+ | ((buf[13]&0xff)<<16) ;
+ }
+}
+
+static int pt_atapi( int unit, char * cmd, int dlen, char * buf, char * fun )
+
+{ int r;
+
+ r = pt_command(unit,cmd,dlen,fun);
+ udelay(1000);
+ if (!r) r = pt_completion(unit,buf,fun);
+ if (r) pt_req_sense(unit,!fun);
+
+ return r;
+}
+
+static void pt_sleep( int cs )
+
+{ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + cs;
+ schedule();
+}
+
+static int pt_poll_dsc( int unit, int pause, int tmo, char *msg )
+
+{ int k, e, s;
+
+ k = 0;
+ while (k < tmo) {
+ pt_sleep(pause);
+ k++;
+ pi_connect(PI);
+ WR(0,6,DRIVE);
+ s = RR(0,7);
+ e = RR(0,1);
+ pi_disconnect(PI);
+ if (s & (STAT_ERR|STAT_SEEK)) break;
+ }
+ if ((k >= tmo) || (s & STAT_ERR)) {
+ if (k >= tmo) printk("%s: %s DSC timeout\n",PT.name,msg);
+ else printk("%s: %s stat=0x%x err=0x%x\n",PT.name,msg,s,e);
+ pt_req_sense(unit,0);
+ return 0;
+ }
+ return 1;
+}
+
+static void pt_media_access_cmd( int unit, int tmo, char *cmd, char *fun)
+
+{ if (pt_command(unit,cmd,0,fun)) {
+ pt_req_sense(unit,0);
+ return;
+ }
+ pi_disconnect(PI);
+ pt_poll_dsc(unit,100,tmo,fun);
+}
+
+static void pt_rewind( int unit )
+
+{ char rw_cmd[12] = {ATAPI_REWIND,0,0,0,0,0,0,0,0,0,0,0};
+
+ pt_media_access_cmd(unit,PT_REWIND_TMO,rw_cmd,"rewind");
+}
+
+static void pt_write_fm( int unit )
+
+{ char wm_cmd[12] = {ATAPI_WFM,0,0,0,1,0,0,0,0,0,0,0};
+
+ pt_media_access_cmd(unit,PT_TMO,wm_cmd,"write filemark");
+}
+
+#define DBMSG(msg) NULL
+
+static int pt_reset( int unit )
+
+{ int i, k, flg;
+ int expect[5] = {1,1,1,0x14,0xeb};
+ long flags;
+
+ pi_connect(PI);
+ WR(0,6,DRIVE);
+ WR(0,7,8);
+
+ save_flags(flags);
+ sti();
+
+ pt_sleep(2);
+
+ k = 0;
+ while ((k++ < PT_RESET_TMO) && (RR(1,6)&STAT_BUSY))
+ pt_sleep(10);
+
+ restore_flags(flags);
+
+ flg = 1;
+ for(i=0;i<5;i++) flg &= (RR(0,i+1) == expect[i]);
+
+ if (verbose) {
+ printk("%s: Reset (%d) signature = ",PT.name,k);
+ for (i=0;i<5;i++) printk("%3x",RR(0,i+1));
+ if (!flg) printk(" (incorrect)");
+ printk("\n");
+ }
+
+ pi_disconnect(PI);
+ return flg-1;
+}
+
+static int pt_ready_wait( int unit, int tmo )
+
+{ char tr_cmd[12] = {ATAPI_TEST_READY,0,0,0,0,0,0,0,0,0,0,0};
+ int k, p;
+
+ k = 0;
+ while (k < tmo) {
+ PT.last_sense = 0;
+ pt_atapi(unit,tr_cmd,0,NULL,DBMSG("test unit ready"));
+ p = PT.last_sense;
+ if (!p) return 0;
+ if (!((p == 0x010402)||((p & 0xff) == 6))) return p;
+ k++;
+ pt_sleep(100);
+ }
+ return 0x000020; /* timeout */
+}
+
+static void xs( char *buf, char *targ, int offs, int len )
+
+{ int j,k,l;
+
+ j=0; l=0;
+ for (k=0;k<len;k++)
+ if((buf[k+offs]!=0x20)||(buf[k+offs]!=l))
+ l=targ[j++]=buf[k+offs];
+ if (l==0x20) j--; targ[j]=0;
+}
+
+static int xn( char *buf, int offs, int size )
+
+{ int v,k;
+
+ v=0;
+ for(k=0;k<size;k++) v=v*256+(buf[k+offs]&0xff);
+ return v;
+}
+
+static int pt_identify( int unit )
+
+{ int dt, s;
+ char *ms[2] = {"master","slave"};
+ char mf[10], id[18];
+ char id_cmd[12] = { ATAPI_IDENTIFY,0,0,0,36,0,0,0,0,0,0,0};
+ char ms_cmd[12] = { ATAPI_MODE_SENSE,0,0x2a,0,128,0,0,0,0,0,0,0};
+ char ls_cmd[12] = { ATAPI_LOG_SENSE,0,0x71,0,0,0,0,0,128,0,0,0};
+ char buf[36];
+
+ s = pt_atapi(unit,id_cmd,36,buf,"identify");
+ if (s) return -1;
+
+ dt = buf[0] & 0x1f;
+ if (dt != 1) {
+ if (verbose)
+ printk("%s: Drive %d, unsupported type %d\n",
+ PT.name,PT.drive,dt);
+ return -1;
+ }
+
+ xs(buf,mf,8,8);
+ xs(buf,id,16,16);
+
+ PT.flags = 0;
+ PT.capacity = 0;
+ PT.bs = 0;
+
+ if (!pt_ready_wait(unit,PT_READY_TMO)) PT.flags |= PT_MEDIA;
+
+ if (!pt_atapi(unit,ms_cmd,36,buf,"mode sense")) {
+ if (!(buf[2] & 0x80)) PT.flags |= PT_WRITE_OK;
+ PT.bs = xn(buf,10,2);
+ }
+
+ if (!pt_atapi(unit,ls_cmd,36,buf,"log sense"))
+ PT.capacity = xn(buf,24,4);
+
+ printk("%s: %s %s, %s",
+ PT.name,mf,id,ms[PT.drive]);
+ if (!(PT.flags & PT_MEDIA))
+ printk(", no media\n");
+ else { if (!(PT.flags & PT_WRITE_OK)) printk(", RO");
+ printk(", blocksize %d, %d MB\n",
+ PT.bs,PT.capacity/1024);
+ }
+
+ return 0;
+}
+
+static int pt_probe( int unit )
+
+/* returns 0, with id set if drive is detected
+ -1, if drive detection failed
+*/
+
+{ if (PT.drive == -1) {
+ for (PT.drive=0;PT.drive<=1;PT.drive++)
+ if (!pt_reset(unit)) return pt_identify(unit);
+ } else {
+ if (!pt_reset(unit)) return pt_identify(unit);
+ }
+ return -1;
+}
+
+static int pt_detect( void )
+
+{ int k, unit;
+
+ printk("%s: %s version %s, major %d\n",
+ name,name,PT_VERSION,major);
+
+ k = 0;
+ if (pt_drive_count == 0) {
+ unit = 0;
+ if (pi_init(PI,1,-1,-1,-1,-1,-1,pt_scratch,
+ PI_PT,verbose,PT.name)) {
+ if (!pt_probe(unit)) {
+ PT.present = 1;
+ k++;
+ } else pi_release(PI);
+ }
+
+ } else for (unit=0;unit<PT_UNITS;unit++) if (DU[D_PRT])
+ if (pi_init(PI,0,DU[D_PRT],DU[D_MOD],DU[D_UNI],
+ DU[D_PRO],DU[D_DLY],pt_scratch,PI_PT,verbose,
+ PT.name)) {
+ if (!pt_probe(unit)) {
+ PT.present = 1;
+ k++;
+ } else pi_release(PI);
+ }
+
+ if (k) return 0;
+
+ printk("%s: No ATAPI tape drive detected\n",name);
+ return -1;
+}
+
+#define DEVICE_NR(dev) (MINOR(dev) % 128)
+
+static int pt_open (struct inode *inode, struct file *file)
+
+{ int unit = DEVICE_NR(inode->i_rdev);
+
+ if ((unit >= PT_UNITS) || (!PT.present)) return -ENODEV;
+
+ PT.access++;
+
+ if (PT.access > 1) {
+ PT.access--;
+ return -EBUSY;
+ }
+
+ MOD_INC_USE_COUNT;
+
+ pt_identify(unit);
+
+ if (!PT.flags & PT_MEDIA) {
+ PT.access--;
+ MOD_DEC_USE_COUNT;
+ return -ENODEV;
+ }
+
+ if ((!PT.flags & PT_WRITE_OK) && (file ->f_mode & 2)) {
+ PT.access--;
+ MOD_DEC_USE_COUNT;
+ return -EROFS;
+ }
+
+ if (!(MINOR(inode->i_rdev) & 128))
+ PT.flags |= PT_REWIND;
+
+ PT.bufptr = kmalloc(PT_BUFSIZE,GFP_KERNEL);
+ if (PT.bufptr == NULL) {
+ PT.access--;
+ MOD_DEC_USE_COUNT;
+ printk("%s: buffer allocation failed\n",PT.name);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int pt_ioctl(struct inode *inode,struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int unit;
+ struct mtop mtop;
+
+ if (!inode || !inode->i_rdev)
+ return -EINVAL;
+ unit = DEVICE_NR(inode->i_rdev);
+ if (unit >= PT_UNITS)
+ return -EINVAL;
+ if (!PT.present)
+ return -ENODEV;
+
+ switch (cmd) {
+ case MTIOCTOP:
+ if (copy_from_user((char *)&mtop, (char *)arg,
+ sizeof(struct mtop))) return -EFAULT;
+
+ switch (mtop.mt_op) {
+
+ case MTREW:
+ pt_rewind(unit);
+ return 0;
+
+ default:
+ printk("%s: Unimplemented mt_op %d\n",PT.name,
+ mtop.mt_op);
+ return -EINVAL;
+ }
+
+ default:
+ printk("%s: Unimplemented ioctl 0x%x\n",PT.name,cmd);
+ return -EINVAL;
+
+ }
+}
+
+
+static int pt_release (struct inode *inode, struct file *file)
+{
+ int unit = DEVICE_NR(inode->i_rdev);
+
+ if ((unit >= PT_UNITS) || (PT.access <= 0))
+ return -EINVAL;
+
+ if (PT.flags & PT_WRITING) pt_write_fm(unit);
+
+ if (PT.flags & PT_REWIND) pt_rewind(unit);
+
+ PT.access--;
+
+ kfree(PT.bufptr);
+ PT.bufptr = NULL;
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+
+}
+
+static ssize_t pt_read(struct file * filp, char * buf,
+ size_t count, loff_t *ppos)
+{
+ struct inode *ino = filp->f_dentry->d_inode;
+ int unit = DEVICE_NR(ino->i_rdev);
+ char rd_cmd[12] = {ATAPI_READ_6,1,0,0,0,0,0,0,0,0,0,0};
+ int k, n, r, p, s, t, b;
+
+ if (!(PT.flags & (PT_READING|PT_WRITING))) {
+ PT.flags |= PT_READING;
+ if (pt_atapi(unit,rd_cmd,0,NULL,"start read-ahead"))
+ return -EIO;
+ } else if (PT.flags & PT_WRITING) return -EIO;
+
+ if (PT.flags & PT_EOF) return 0;
+
+ t = 0;
+
+ while (count > 0) {
+
+ if (!pt_poll_dsc(unit,1,PT_TMO,"read")) return -EIO;
+
+ n = count;
+ if (n > 32768) n = 32768; /* max per command */
+ b = (n-1+PT.bs)/PT.bs;
+ n = b*PT.bs; /* rounded up to even block */
+
+ rd_cmd[4] = b;
+
+ r = pt_command(unit,rd_cmd,n,"read");
+
+ udelay(1000);
+
+ if (r) {
+ pt_req_sense(unit,0);
+ return -EIO;
+ }
+
+ while (1) {
+
+ r = pt_wait(unit,STAT_BUSY,STAT_DRQ|STAT_ERR|STAT_READY,
+ DBMSG("read DRQ"),"");
+
+ if (r & STAT_SENSE) {
+ pi_disconnect(PI);
+ pt_req_sense(unit,0);
+ return -EIO;
+ }
+
+ if (r) PT.flags |= PT_EOF;
+
+ s = RR(0,7);
+
+ if (!(s & STAT_DRQ)) break;
+
+ n = (RR(0,4)+256*RR(0,5));
+ p = (RR(0,2)&3);
+ if (p != 2) {
+ pi_disconnect(PI);
+ printk("%s: Phase error on read: %d\n",PT.name,p);
+ return -EIO;
+ }
+
+ while (n > 0) {
+ k = n;
+ if (k > PT_BUFSIZE) k = PT_BUFSIZE;
+ pi_read_block(PI,PT.bufptr,k);
+ n -= k;
+ b = k;
+ if (b > count) b = count;
+ copy_to_user(buf+t,PT.bufptr,b);
+ t += b;
+ count -= b;
+ }
+
+ }
+ pi_disconnect(PI);
+ if (PT.flags & PT_EOF) break;
+ }
+
+ return t;
+
+}
+
+static ssize_t pt_write(struct file * filp, const char * buf,
+ size_t count, loff_t *ppos)
+{
+ struct inode *ino = filp->f_dentry->d_inode;
+ int unit = DEVICE_NR(ino->i_rdev);
+ char wr_cmd[12] = {ATAPI_WRITE_6,1,0,0,0,0,0,0,0,0,0,0};
+ int k, n, r, p, s, t, b;
+
+ if (!(PT.flags & PT_WRITE_OK)) return -EROFS;
+
+ if (!(PT.flags & (PT_READING|PT_WRITING))) {
+ PT.flags |= PT_WRITING;
+ if (pt_atapi(unit,wr_cmd,0,NULL,"start buffer-available mode"))
+ return -EIO;
+ } else if (PT.flags&PT_READING) return -EIO;
+
+ if (PT.flags & PT_EOF) return -ENOSPC;
+
+ t = 0;
+
+ while (count > 0) {
+
+ if (!pt_poll_dsc(unit,1,PT_TMO,"write")) return -EIO;
+
+ n = count;
+ if (n > 32768) n = 32768; /* max per command */
+ b = (n-1+PT.bs)/PT.bs;
+ n = b*PT.bs; /* rounded up to even block */
+
+ wr_cmd[4] = b;
+
+ r = pt_command(unit,wr_cmd,n,"write");
+
+ udelay(1000);
+
+ if (r) { /* error delivering command only */
+ pt_req_sense(unit,0);
+ return -EIO;
+ }
+
+ while (1) {
+
+ r = pt_wait(unit,STAT_BUSY,STAT_DRQ|STAT_ERR|STAT_READY,
+ DBMSG("write DRQ"),NULL);
+
+ if (r & STAT_SENSE) {
+ pi_disconnect(PI);
+ pt_req_sense(unit,0);
+ return -EIO;
+ }
+
+ if (r) PT.flags |= PT_EOF;
+
+ s = RR(0,7);
+
+ if (!(s & STAT_DRQ)) break;
+
+ n = (RR(0,4)+256*RR(0,5));
+ p = (RR(0,2)&3);
+ if (p != 0) {
+ pi_disconnect(PI);
+ printk("%s: Phase error on write: %d \n",PT.name,p);
+ return -EIO;
+ }
+
+ while (n > 0) {
+ k = n;
+ if (k > PT_BUFSIZE) k = PT_BUFSIZE;
+ b = k;
+ if (b > count) b = count;
+ copy_from_user(PT.bufptr,buf+t,b);
+ pi_write_block(PI,PT.bufptr,k);
+ t += b;
+ count -= b;
+ n -= k;
+ }
+
+ }
+ pi_disconnect(PI);
+ if (PT.flags & PT_EOF) break;
+ }
+
+ return t;
+}
+
+/* end of pt.c */
+
diff --git a/drivers/block/paride/setup.h b/drivers/block/paride/setup.h
new file mode 100644
index 000000000..6d941cc09
--- /dev/null
+++ b/drivers/block/paride/setup.h
@@ -0,0 +1,56 @@
+/*
+ setup.h (c) 1997 Grant R. Guenther <grant@torque.net>
+ Under the terms of the GNU public license.
+
+ This is a table driven setup function for kernel modules
+ using the module.variable=val,... command line notation.
+
+*/
+
+#include <linux/ctype.h>
+#include <linux/string.h>
+
+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;
+
+/* t is a table that describes the variables that can be set
+ by gen_setup
+ n is the number of entries in the table
+ ss is a string of the form:
+
+ <tag>=[<val>,...]<val>
+*/
+
+static void generic_setup( STT t[], int n, char *ss )
+
+{ int j,k;
+
+ k = 0;
+ for (j=0;j<n;j++) {
+ k = strlen(t[j].tag);
+ if (strncmp(ss,t[j].tag,k) == 0) break;
+ }
+ if (j == n) return;
+
+ if (ss[k] == 0) {
+ t[j].iv[0] = 1;
+ return;
+ }
+
+ if (ss[k] != '=') return;
+ ss += (k+1);
+
+ k = 0;
+ while (ss && isdigit(*ss) && (k < t[j].size)) {
+ t[j].iv[k++] = simple_strtoul(ss,NULL,0);
+ if ((ss = strchr(ss,',')) != NULL) ss++;
+ }
+}
+
+/* end of setup.h */