diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1998-03-17 22:05:47 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1998-03-17 22:05:47 +0000 |
commit | 27cfca1ec98e91261b1a5355d10a8996464b63af (patch) | |
tree | 8e895a53e372fa682b4c0a585b9377d67ed70d0e /drivers/block/paride | |
parent | 6a76fb7214c477ccf6582bd79c5b4ccc4f9c41b1 (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.in | 20 | ||||
-rw-r--r-- | drivers/block/paride/Makefile | 141 | ||||
-rw-r--r-- | drivers/block/paride/aten.c | 166 | ||||
-rw-r--r-- | drivers/block/paride/bpck.c | 476 | ||||
-rw-r--r-- | drivers/block/paride/comm.c | 222 | ||||
-rw-r--r-- | drivers/block/paride/dstr.c | 237 | ||||
-rw-r--r-- | drivers/block/paride/epat.c | 315 | ||||
-rw-r--r-- | drivers/block/paride/epia.c | 318 | ||||
-rw-r--r-- | drivers/block/paride/frpw.c | 256 | ||||
-rw-r--r-- | drivers/block/paride/kbic.c | 305 | ||||
-rw-r--r-- | drivers/block/paride/on20.c | 157 | ||||
-rw-r--r-- | drivers/block/paride/on26.c | 261 | ||||
-rw-r--r-- | drivers/block/paride/paride.c | 485 | ||||
-rw-r--r-- | drivers/block/paride/paride.h | 157 | ||||
-rw-r--r-- | drivers/block/paride/pcd.c | 816 | ||||
-rw-r--r-- | drivers/block/paride/pd.c | 1088 | ||||
-rw-r--r-- | drivers/block/paride/pf.c | 1072 | ||||
-rw-r--r-- | drivers/block/paride/pseudo.h | 138 | ||||
-rw-r--r-- | drivers/block/paride/pt.c | 959 | ||||
-rw-r--r-- | drivers/block/paride/setup.h | 56 |
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 */ |